Merge pull request #700 from rsmudge/armitage

Armitage 08.16.12
unstable
Tod Beardsley 2012-08-18 05:55:26 -07:00
commit f46545db58
21 changed files with 259 additions and 40 deletions

Binary file not shown.

Binary file not shown.

View File

@ -1,6 +1,30 @@
Armitage Changelog
==================
16 Aug 12 (tested against msf r15753)
----------
- Dynamic workspaces now removes closed services from its set of
hosts matching certain open ports.
- Cortana console now reports a clear error message a built-in
command is executed without the right number of arguments.
- Added host icons for Android and iOS. You may now set these
operating systems by going to [host] -> Host -> Operating System
- Armitage now shows the client-side exploit dialog for any exploit
that does not target an RHOST (for example, windows/smb/smb_relay)
- Added support for remote exploits that use RHOSTS over RHOST
(this includes the new windows/local/current_user_psexec)
- Added a helper for setting the SESSION option
Cortana Updates (for scripters)
--------
- s_cmd no longer times out after 60s. It will wait forever for
a command to complete now.
- added shell_read event which fires when a shell s_cmd comes
back with intermediate output.
- fixed a potential deadlock with &open_console_tab
- scripts now have the ability to redefine the max size of a
workspace: db_workspace(%(size => #####));
2 Aug 12 (tested again msf r15698)
--------
- Armitage now reports vulnerability module and descriptions

View File

@ -3,7 +3,7 @@
<center><h1>Armitage 1.44</h1></center>
<p>An attack management tool for Metasploit&reg;
<br />Release: 2 Aug 12</p>
<br />Release: 16 Aug 12</p>
<br />
<p>Developed by:</p>

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

View File

@ -48,7 +48,7 @@ sub open_console_tab {
quit();
}
_call_("&setupConsoleStyle", $tab);
_call_later_("&setupConsoleStyle", $tab);
return $console;
}, $title => $1, $log_folder => $2, $popup_hook => $3, $q_activity => $4);
}

View File

@ -63,6 +63,9 @@ sub showHost {
push(@overlay, 'resources/windows7.png');
}
}
else if ($os eq "Apple iOS" || "*AppleTV*" iswm $os) {
push(@overlay, 'resources/ios.png');
}
else if ($os eq "Mac OS X" || "*apple*" iswm lc($os) || "*mac*os*x*" iswm lc($os)) {
push(@overlay, 'resources/macosx.png');
}
@ -81,6 +84,9 @@ sub showHost {
else if ("*VMware*" iswm $os) {
push(@overlay, 'resources/vmware.png');
}
else if ($os eq "Android") {
push(@overlay, 'resources/android.png');
}
else if ($purpose eq "firewall") {
return overlay_images(@('resources/firewall.png'));
}

View File

@ -397,6 +397,9 @@ sub attack_dialog {
if ($key eq "RHOST") {
$value["default"] = join(", ", $3);
}
else if ($key eq "RHOSTS") {
$value["default"] = join(", ", $3);
}
[$model _addEntry: %(Option => $key,
Value => $value["default"],
@ -454,6 +457,34 @@ sub attack_dialog {
$options["TARGET"] = split(' \=\> ', [$combobox getSelectedItem])[0];
if ('RHOSTS' in $options) {
thread(lambda({
local('$hosts $host');
$hosts = split(', ', $options["RHOSTS"]);
if (size($hosts) == 0) {
showError("Please specify an RHOSTS value");
return;
}
$options["PAYLOAD"] = best_payload($hosts[0], $exploit, [$b isSelected]);
if ([$b isSelected]) {
$options["LPORT"] = randomPort();
}
# give scripts a chance to filter this data.
foreach $host ($hosts) {
($exploit, $host, $options) = filter_data("exploit", $exploit, $host, $options);
}
module_execute("exploit", $exploit, copy($options));
if ([$preferences getProperty: "armitage.show_all_commands.boolean", "true"] eq "false" || size($hosts) >= 4) {
showError("Launched $exploit at " . size($hosts) . " host" . iff(size($hosts) == 1, "", "s"));
}
}, $options => copy($options), \$exploit, \$b));
}
else {
thread(lambda({
local('$host $hosts');
$hosts = split(', ', $options["RHOST"]);
@ -480,6 +511,7 @@ sub attack_dialog {
showError("Launched $exploit at " . size($hosts) . " host" . iff(size($hosts) == 1, "", "s"));
}
}, $options => copy($options), \$exploit, \$b));
}
if (!isShift($1)) {
[$dialog setVisible: 0];
@ -615,6 +647,35 @@ sub addFileListener {
$actions["SigningKey"] = $actions["*FILE*"];
$actions["WORDLIST"] = $actions["*FILE*"];
# set up an action to choose a session
$actions["SESSION"] = {
local('@data $sid $data $host $hdata $temp $tablef');
# obtain a list of sessions
foreach $host (keys(%hosts)) {
foreach $sid => $data (getSessions($host)) {
$temp = copy($data);
$temp['sid'] = $sid;
push(@data, $temp);
}
}
# sort the session data
@data = sort({ return $1['sid'] <=> $2['sid']; }, @data);
# update the table widths
$tablef = {
[[$1 getColumn: "sid"] setPreferredWidth: 100];
[[$1 getColumn: "session_host"] setPreferredWidth: 300];
[[$1 getColumn: "info"] setPreferredWidth: 1024];
};
# let the user choose a session
quickListDialog("Choose a session", "Select", @("sid", "sid", "session_host", "info"), @data, $width => 640, $height => 240, lambda({
[$call : $1];
}, $call => $4), \$tablef);
};
# set up an action to pop up a file chooser for different file type values.
$actions["RHOST"] = {
local('$title $temp');

View File

@ -432,7 +432,7 @@ sub setupTable {
# creates a list dialog,
# $1 = title, $2 = button text, $3 = columns, $4 = rows, $5 = callback
sub quickListDialog {
local('$dialog $panel $table $row $model $button $sorter $after $a');
local('$dialog $panel $table $row $model $button $sorter $after $a $tablef');
$dialog = dialog($1, $width, $height);
$panel = [new JPanel];
[$panel setLayout: [new BorderLayout]];
@ -440,6 +440,10 @@ sub quickListDialog {
($table, $model) = setupTable($3[0], sublist($3, 1), $4);
[$panel add: [new JScrollPane: $table], [BorderLayout CENTER]];
if ($tablef !is $null) {
[$tablef: $table, $model];
}
$button = [new JButton: $2];
[$button addActionListener: lambda({
[$callback : [$model getSelectedValueFromColumn: $table, $lead]];

View File

@ -36,6 +36,8 @@ sub host_selected_items {
$h = menu($1, "Host", 'H');
$o = menu($h, "Operating System", 'O');
item($o, "Android", 'A', setHostValueFunction($2, "os_name", "Android"));
item($o, "Apple iOS", 'i', setHostValueFunction($2, "os_name", "Apple iOS"));
item($o, "Cisco IOS", 'C', setHostValueFunction($2, "os_name", "Cisco IOS"));
item($o, "FreeBSD", 'F', setHostValueFunction($2, "os_name", "FreeBSD"));
item($o, "Linux", 'L', setHostValueFunction($2, "os_name", "Linux"));

View File

@ -19,9 +19,24 @@ sub createModuleBrowser {
return $split;
}
sub isClientside {
local('$options');
$options = call($mclient, "module.options", "exploit", $1);
return iff ('RHOST' in $options || 'RHOSTS' in $options, $null, 1);
}
sub showModulePopup {
local('$event $type $path');
($event, $type, $path) = @_;
# we go through this hassle because &isClientside calls module.options which could block
# and freeze the UI--we don't want to do that...
thread(lambda(&_showModulePopup, \$event, \$type, \$path));
}
sub _showModulePopup {
local('$menu');
if (($2 eq "exploit" && "*/browser/*" !iswm $3 && "*/fileformat/*" !iswm $3) || ($2 eq "auxiliary" && "*_login" iswm $3)) {
if (($type eq "exploit" && !isClientside($path)) || ($type eq "auxiliary" && "*_login" iswm $path)) {
$menu = [new JPopupMenu];
item($menu, "Relevant Targets", 'R', lambda({
thread(lambda({
@ -61,14 +76,18 @@ sub showModulePopup {
showError("I'm sorry, this option doesn't work for\nthis module.");
}
}, \$module, \$type));
}, $module => $3, $type => $2));
}, $module => $path, \$type));
setupMenu($menu, "module", @($2, $3));
setupMenu($menu, "module", @($type, $path));
[$menu show: [$1 getSource], [$1 getX], [$1 getY]];
dispatchEvent(lambda({
[$menu show: [$event getSource], [$event getX], [$event getY]];
}, \$menu, \$event));
}
else {
installMenu($1, "module", @($2, $3));
dispatchEvent(lambda({
installMenu($event, "module", @($type, $path));
}, \$type, \$path, \$event));
}
}
@ -79,7 +98,7 @@ sub moduleAction {
thread(lambda({
if ($path in @exploits || $path in @auxiliary || $path in @payloads || $path in @post) {
if ($type eq "exploit") {
if ('*/browser/*' iswm $path || '*/fileformat/*' iswm $path) {
if (isClientside($path)) {
launch_dialog($path, $type, $path, 1, $hosts);
}
else {

View File

@ -152,7 +152,7 @@ sub updatePrefModel {
foreach $key => $value (convertAll($preferences)) {
($component, $name, $type) = split('\\.', $key);
if ($type eq "color" || $type eq "shortcut" || $type eq "font" || $type eq "folder") {
if ($type eq "color" || $type eq "shortcut" || $type eq "font" || $type eq "folder" || $type eq "file") {
$type = "$type \u271A";
}
@ -220,6 +220,14 @@ sub createPreferencesTab {
[$model fireListeners];
}
}
else if ($type eq "file") {
local('$file');
$file = chooseFile();
if ($file !is $null) {
[$model setValueAtRow: $row, "value", $file];
[$model fireListeners];
}
}
else if ($type eq "font") {
local('$dialog $select $style $size $ok $cancel $preview $graphics $l $font $_style');
$dialog = dialog("Choose a font", 640, 240);

View File

@ -278,6 +278,12 @@ public class Cortana implements Loadable, RuntimeWarningWatcher {
states.add("askon");
states.add("askoff");
Set cmds = new HashSet();
cmds.addAll(states);
cmds.add("unload");
cmds.add("load");
cmds.add("reload");
if ("ls".equals(text)) {
p("");
p("Scripts");
@ -292,6 +298,9 @@ public class Cortana implements Loadable, RuntimeWarningWatcher {
}
p("");
}
else if (cmds.contains(data[0]) && data.length != 2) {
p("[-] Missing arguments");
}
else if (states.contains(data[0]) && data.length == 2) {
String script = findScript(data[1]);
if (script == null) {

View File

@ -135,12 +135,17 @@ public class DataManager implements ArmitageTimerClient, Loadable, Function, Pre
Map workspace = new HashMap();
Object[] argz = new Object[1];
if (args.size() == 4) {
if (args.size() >= 4) {
String hosts = BridgeUtilities.getString(args, "");
String ports = BridgeUtilities.getString(args, "");
String os = BridgeUtilities.getString(args, "");
String session = BridgeUtilities.getString(args, "");
if (!args.isEmpty()) {
String size = BridgeUtilities.getString(args, "512");
workspace.put("size", size);
}
if (!hosts.equals(""))
workspace.put("hosts", hosts);

View File

@ -28,6 +28,27 @@ public class ShellBridge implements Loadable, Function, ShellSession.ShellCallba
public SleepClosure function;
}
public void commandUpdate(String session, Object token, String output) {
if (!(token instanceof ShellToken))
return;
ScriptInstance script = ((ShellToken)token).script;
String command = ((ShellToken)token).command;
SleepClosure function = ((ShellToken)token).function;
Stack args = new Stack();
args.push(FilterManager.convertAll(output));
args.push(SleepUtils.getScalar(command));
args.push(SleepUtils.getScalar(session));
if (function == null) {
events.fireEvent("shell_read", args, script);
}
else {
SleepUtils.runCode(function, "read", script, args);
}
}
public void commandComplete(String session, Object token, String output) {
if (!(token instanceof ShellToken))
return;

View File

@ -25,6 +25,7 @@ public class ShellSession implements Runnable {
public static interface ShellCallback {
public void commandComplete(String session, Object token, String response);
public void commandUpdate(String session, Object token, String response);
}
public void addListener(ShellCallback l) {
@ -33,13 +34,17 @@ public class ShellSession implements Runnable {
}
}
public void fireEvent(Command command, String output) {
public void fireEvent(Command command, String output, boolean done) {
Iterator i;
synchronized (this) {
i = new LinkedList(listeners).iterator();
}
while (i.hasNext()) {
if (done)
((ShellCallback)i.next()).commandComplete(session, command != null ? command.token : null, output);
else
((ShellCallback)i.next()).commandUpdate(session, command != null ? command.token : null, output);
}
}
@ -68,25 +73,28 @@ public class ShellSession implements Runnable {
/* read until we encounter AAAAAAAAAA */
StringBuffer output = new StringBuffer();
/* loop for 60s trying to read output... give up after 60s, some commands may simply take this long */
for (int x = 0; x < 600; x++) {
/* loop forever waiting for response to come back. If session is dead
then this loop will break with an exception */
while (true) {
response = readResponse();
String data = (response.get("data") + "");
if (data.length() > 0) {
if (data.endsWith(marker)) {
data = data.substring(0, data.length() - marker.length());
fireEvent(c, data, false);
output.append(data);
fireEvent(c, output.toString());
fireEvent(c, output.toString(), true);
return;
}
else {
fireEvent(c, data, false);
output.append(data);
}
}
Thread.sleep(100);
}
fireEvent(c, output.toString());
}
catch (Exception ex) {
System.err.println(session + " -> " + c.text + " ( " + response + ")");

View File

@ -16,6 +16,7 @@ public class Shared implements Function, Loadable {
the armitage function must register itself though */
script.getScriptEnvironment().getEnvironment().put("&_call_", this);
script.getScriptEnvironment().getEnvironment().put("&_call_async_", this);
script.getScriptEnvironment().getEnvironment().put("&_call_later_", this);
}
public void scriptUnloaded(ScriptInstance script) {
@ -38,6 +39,16 @@ public class Shared implements Function, Loadable {
}).start();
return SleepUtils.getEmptyScalar();
}
else if (name.equals("&_call_later_")) {
final SleepClosure f = (SleepClosure)values.get(function);
final Stack argz = EventManager.shallowCopy(args);
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
SleepUtils.runCode(f, function, f.getOwner(), argz);
}
});
return SleepUtils.getEmptyScalar();
}
}
throw new RuntimeException("'" + function + "' does not exist");

View File

@ -19,6 +19,12 @@ public class DatabaseImpl implements RpcConnection {
protected int hindex = 0;
protected int sindex = 0;
/* define the maximum hosts in a workspace */
protected int maxhosts = 512;
/* define the maximum services in a workspace */
protected int maxservices = 512 * 24;
public void resetHostsIndex() {
hindex = 0;
queries = build();
@ -186,8 +192,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 ? 512 : 30000;
int limit2 = rFilter == null && oFilter == null ? 12288 : 100000;
int limit1 = rFilter == null && oFilter == null ? maxhosts : 30000;
int limit2 = rFilter == null && oFilter == 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);
@ -226,10 +232,10 @@ public class DatabaseImpl implements RpcConnection {
Map result = new HashMap();
if (methodName.equals("db.services")) {
result.put(methodName.substring(3), filterByRoute(executeQuery(query), 12288));
result.put(methodName.substring(3), filterByRoute(executeQuery(query), maxservices));
}
else if (methodName.equals("db.hosts")) {
result.put(methodName.substring(3), filterByRoute(executeQuery(query), 512));
result.put(methodName.substring(3), filterByRoute(executeQuery(query), maxhosts));
}
else {
result.put(methodName.substring(3), executeQuery(query));
@ -335,6 +341,15 @@ public class DatabaseImpl implements RpcConnection {
//srvcs.add("sessions.host_id = hosts.id AND sessions.closed_at IS NULL");
}
if (values.containsKey("size")) {
try {
maxhosts = Integer.parseInt(values.get("size") + "");
maxservices = maxhosts * 24;
}
catch (Exception ex) {
}
}
if (values.containsKey("hosts") && (values.get("hosts") + "").length() > 0) {
String h = values.get("hosts") + "";
if (!h.matches("[0-9a-fA-F\\.:\\%\\_/, ]+")) {
@ -362,6 +377,7 @@ public class DatabaseImpl implements RpcConnection {
//ports2.add("s.port = " + p[x]);
}
hosts.add("services.host_id = hosts.id");
hosts.add("services.state = 'open'");
hosts.add("(" + join(ports, " OR ") + ")");
}

View File

@ -23,6 +23,7 @@ public class ATable extends JTable {
specialitems.add("SigningKey");
specialitems.add("SigningCert");
specialitems.add("WORDLIST");
specialitems.add("SESSION");
return new TableCellRenderer() {
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {

View File

@ -1,6 +1,30 @@
Armitage Changelog
==================
16 Aug 12 (tested against msf r15753)
----------
- Dynamic workspaces now removes closed services from its set of
hosts matching certain open ports.
- Cortana console now reports a clear error message a built-in
command is executed without the right number of arguments.
- Added host icons for Android and iOS. You may now set these
operating systems by going to [host] -> Host -> Operating System
- Armitage now shows the client-side exploit dialog for any exploit
that does not target an RHOST (for example, windows/smb/smb_relay)
- Added support for remote exploits that use RHOSTS over RHOST
(this includes the new windows/local/current_user_psexec)
- Added a helper for setting the SESSION option
Cortana Updates (for scripters)
--------
- s_cmd no longer times out after 60s. It will wait forever for
a command to complete now.
- added shell_read event which fires when a shell s_cmd comes
back with intermediate output.
- fixed a potential deadlock with &open_console_tab
- scripts now have the ability to redefine the max size of a
workspace: db_workspace(%(size => #####));
2 Aug 12 (tested again msf r15698)
--------
- Armitage now reports vulnerability module and descriptions