Merge branch 'rsmudge-armitage'
commit
e376bb6fab
Binary file not shown.
Binary file not shown.
|
@ -1,6 +1,32 @@
|
|||
Armitage Changelog
|
||||
==================
|
||||
|
||||
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
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
depend="yes"
|
||||
debug="true"
|
||||
optimize="yes"
|
||||
target="1.6"
|
||||
source="1.6"
|
||||
includeantruntime="fuckno"
|
||||
>
|
||||
<classpath path="./lib/jgraphx.jar;./lib/sleep.jar;./lib/msgpack-0.5.1-devel.jar;./lib/postgresql-9.1-901.jdbc4.jar" />
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<center><h1>Armitage 1.45</h1></center>
|
||||
|
||||
<p>An attack management tool for Metasploit®
|
||||
<br />Release: 4 Jan 13</p>
|
||||
<br />Release: 23 Jan 13</p>
|
||||
<br />
|
||||
<p>Developed by:</p>
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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$$
|
|
@ -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");
|
||||
|
|
|
@ -9,7 +9,7 @@ import msf.*;
|
|||
|
||||
# setg("varname", "value")
|
||||
sub setg {
|
||||
call_async("core.setg", $1, $2);
|
||||
cmd_safe("setg $1 $2");
|
||||
}
|
||||
|
||||
sub readg {
|
||||
|
@ -335,14 +335,22 @@ sub multi_handler {
|
|||
}
|
||||
|
||||
sub handler {
|
||||
local('%o $3');
|
||||
local('%o $3 $key $value');
|
||||
|
||||
# default options
|
||||
%o['PAYLOAD'] = $1;
|
||||
%o['LPORT'] = $2;
|
||||
%o['DisablePayloadHandler'] = 'false';
|
||||
%o['ExitOnSession'] = 'false';
|
||||
|
||||
# let the user override anything
|
||||
if ($3) {
|
||||
%o = copy($3);
|
||||
foreach $key => $value ($3) {
|
||||
%o[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
%o['PAYLOAD'] = "payload/ $+ $1";
|
||||
%o['LPORT'] = $2;
|
||||
|
||||
# make sure LHOST is correct
|
||||
if ('LHOST' !in %o) {
|
||||
if ("*http*" iswm $1) {
|
||||
%o['LHOST'] = lhost();
|
||||
|
@ -352,6 +360,7 @@ sub handler {
|
|||
}
|
||||
}
|
||||
|
||||
# let's do it...
|
||||
return launch('exploit', 'multi/handler', %o);
|
||||
}
|
||||
|
||||
|
|
|
@ -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,7 +160,7 @@ 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;
|
||||
|
@ -239,10 +239,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));
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -446,7 +446,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])];
|
||||
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -372,3 +372,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));
|
||||
}
|
||||
|
||||
|
|
|
@ -403,9 +403,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
|
||||
#
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -159,12 +159,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 +310,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);
|
||||
|
||||
|
@ -416,7 +424,7 @@ sub connectDialog {
|
|||
[$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") {
|
||||
try {
|
||||
closef(connect("127.0.0.1", $p, 1000));
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -196,6 +196,7 @@ public class ArmitageApplication extends JFrame {
|
|||
r.setLayout(new BorderLayout());
|
||||
r.add(t.component, BorderLayout.CENTER);
|
||||
r.pack();
|
||||
t.component.validate();
|
||||
|
||||
r.addWindowListener(new WindowAdapter() {
|
||||
public void windowClosing(WindowEvent ev) {
|
||||
|
|
|
@ -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 class Loader implements Loadable {
|
|||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -69,7 +69,7 @@ public class Main implements Runnable, CortanaPipe.CortanaPipeListener {
|
|||
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) {
|
||||
|
|
|
@ -453,17 +453,26 @@ public class NetworkGraph extends JComponent implements ActionListener {
|
|||
|
||||
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);
|
||||
|
||||
|
|
|
@ -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 @@ public class DatabaseImpl implements RpcConnection {
|
|||
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 @@ public class DatabaseImpl implements RpcConnection {
|
|||
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 @@ public class DatabaseImpl implements RpcConnection {
|
|||
}
|
||||
}
|
||||
|
||||
/* 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 @@ public class DatabaseImpl implements RpcConnection {
|
|||
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 @@ public class DatabaseImpl implements RpcConnection {
|
|||
/* 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);
|
||||
|
||||
|
@ -235,7 +336,7 @@ public class DatabaseImpl implements RpcConnection {
|
|||
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));
|
||||
|
@ -332,6 +433,7 @@ public class DatabaseImpl implements RpcConnection {
|
|||
|
||||
rFilter = null;
|
||||
oFilter = null;
|
||||
lFilter = null;
|
||||
|
||||
List hosts = new LinkedList();
|
||||
List srvcs = new LinkedList();
|
||||
|
@ -385,6 +487,11 @@ public class DatabaseImpl implements RpcConnection {
|
|||
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 +513,31 @@ public class DatabaseImpl implements RpcConnection {
|
|||
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") + "";
|
||||
|
|
|
@ -106,6 +106,8 @@ public class RpcCacheImpl implements Runnable {
|
|||
key.append(temp.get("ports"));
|
||||
key.append(";");
|
||||
key.append(temp.get("session"));
|
||||
key.append(";");
|
||||
key.append(temp.get("labels"));
|
||||
return key.toString();
|
||||
}
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ public class NetworkTable extends JComponent implements ActionListener {
|
|||
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,12 +79,13 @@ public class NetworkTable extends JComponent implements ActionListener {
|
|||
};
|
||||
|
||||
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);
|
||||
|
@ -95,7 +96,7 @@ public class NetworkTable extends JComponent implements ActionListener {
|
|||
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"))) {
|
||||
|
@ -252,16 +253,17 @@ public class NetworkTable extends JComponent implements ActionListener {
|
|||
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);
|
||||
|
|
|
@ -26,6 +26,12 @@ public class ATable extends JTable {
|
|||
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) {
|
||||
|
|
|
@ -54,6 +54,8 @@ public class ZoomableImage extends JLabel {
|
|||
check(ev);
|
||||
}
|
||||
});
|
||||
|
||||
setHorizontalAlignment(SwingConstants.CENTER);
|
||||
}
|
||||
|
||||
protected void updateIcon() {
|
||||
|
|
|
@ -1,6 +1,32 @@
|
|||
Armitage Changelog
|
||||
==================
|
||||
|
||||
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
|
||||
|
|
Loading…
Reference in New Issue