Merge branch 'rsmudge-armitage'

bug/bundler_fix
sinn3r 2013-01-22 22:52:35 -06:00
commit e376bb6fab
29 changed files with 385 additions and 118 deletions

Binary file not shown.

Binary file not shown.

View File

@ -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

View File

@ -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" />

View File

@ -3,7 +3,7 @@
<center><h1>Armitage 1.45</h1></center>
<p>An attack management tool for Metasploit&reg;
<br />Release: 4 Jan 13</p>
<br />Release: 23 Jan 13</p>
<br />
<p>Developed by:</p>

View File

@ -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

View File

@ -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$$

View File

@ -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");

View File

@ -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);
}

View File

@ -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));
}

View File

@ -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');

View File

@ -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])];

View File

@ -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]);
}

View File

@ -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));
}

View File

@ -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));
}

View File

@ -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
#

View File

@ -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

View File

@ -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));
}

View File

@ -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;

View File

@ -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) {

View File

@ -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;
}
}

View File

@ -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) {

View File

@ -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);

View File

@ -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") + "";

View File

@ -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();
}

View File

@ -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);

View File

@ -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) {

View File

@ -54,6 +54,8 @@ public class ZoomableImage extends JLabel {
check(ev);
}
});
setHorizontalAlignment(SwingConstants.CENTER);
}
protected void updateIcon() {

View File

@ -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