Merge pull request #383 from rsmudge/armitage

Armitage 05.14.12
This commit is contained in:
sinn3r 2012-05-13 16:15:59 -07:00
commit 2e8b11ca78
23 changed files with 609 additions and 210 deletions

Binary file not shown.

View File

@ -28,6 +28,15 @@ else
exit
fi
# check if keytool is available...
if [ $(command -v keytool) ]; then
true
else
echo "[-] keytool is not in \$PATH"
echo " install the Java Developer Kit"
exit
fi
# check if msfrpcd is available
if [ $(command -v msfrpcd) ]; then
true
@ -44,10 +53,20 @@ if [ "$(pidof msfrpcd)" ]; then
exit
fi
# generate a certificate
# naturally you're welcome to replace this step with your own permanent certificate.
# just make sure you pass -Djavax.net.ssl.keyStore="/path/to/whatever" and
# -Djavax.net.ssl.keyStorePassword="password" to java. This is used for setting up
# an SSL server socket. Also, the SHA-1 digest of the first certificate in the store
# is printed so users may have a chance to verify they're not being owned.
echo "[+] Generating X509 certificate and keystore (for SSL)"
rm -f ./armitage.store
keytool -keystore ./armitage.store -storepass 123456 -keypass 123456 -genkey -keyalg RSA -alias armitage -dname "CN=Armitage Hacker, OU=FastAndEasyHacking, O=Armitage, L=Somewhere, S=Cyberspace, C=Earth"
# start everything up
echo "[+] Starting RPC daemon"
msfrpcd -U msf -P $2 -a 127.0.0.1 -p 55554 -S
echo "[+] sleeping for 20s (to let msfrpcd initialize)"
sleep 20
echo "[+] Starting Armitage team server"
java -server -XX:+UseParallelGC -jar armitage.jar --server $1 55554 msf $2 55553
java -Djavax.net.ssl.keyStore=./armitage.store -Djavax.net.ssl.keyStorePassword=123456 -server -XX:+UseParallelGC -jar armitage.jar --server $1 55554 msf $2 55553

View File

@ -1,6 +1,34 @@
Armitage Changelog
==================
14 May 12
---------
- Oopserific--dynamic workspace shortcuts were not bound until you
clicked the Workspaces menu. I fixed that.
- Improved console pool's ability to detect a dead console. If you saw
"null" prompts in an open tab, it's because of a dead console. Fixed
- Bound Ctrl+Backspace to reset dynamic workspaces. Ctrl+0 is now back
to what it originally did (resetting the font size to default).
- Added Ctrl+T to take a screenshot of the active tab
- Added Ctrl+W to pop the active tab into its own window
- Armitage team server is now SSL enabled. The teamserver script (you
are using it, right?) generates a certificate for you using keytool.
The server presents the SHA1 hash of its certificate. Armitage users
have the opportunity to verify and trust the hash of the certificate
presented to them or to reject it and not connect.
- Added Ctrl+Left / Ctrl+Right to quickly navigate through tabs.
- Added a check to prevent clients from connecting to msfrpcd directly
when teaming is enabled.
- Fixed a bug that prevented command shells from opening on some sessions
- Team server client now caches certain calls to RPC server.
- Reworked the Loot/Downloads View button. Now, all highlighted files are
displayed in one View tab. This makes searching easier. Each file is
displayed with a colored header (to make it easier to tell when one file
ends and the other begins).
- Added Sync Files button to Loot/Downloads tabs when connected to a team
server. This button will download all files associated with the highlighted
rows and save them in the Armitage data directory.
7 May 12
--------
Note: Armitage team server setup has changed. Refer to the manual for

View File

@ -3,7 +3,7 @@
<center><h1>Armitage 1.44-dev</h1></center>
<p>An attack management tool for Metasploit&reg;
<br />Release: 7 May 12</p>
<br />Release: 14 May 12</p>
<br />
<p>Developed by:</p>
@ -18,6 +18,6 @@
<li>JGraph by JGraph Ltd. (BSD license)</li>
</ul>
<p><small>Metasploit&reg; is a registered trademark of Rapid7 Inc.</small></p>
<p><small>Metasploit&reg; is a registered trademark of Rapid7</small></p>
</body>
</html>

View File

@ -39,3 +39,10 @@ armitage.no_msf_banner.boolean=false
tab.highlight.color=#0000ff
armitage.show_all_commands.boolean=true
armitage.application_title.string=Armitage
console.color_0.color=#000000
console.color_1.color=#ffffff
console.color_2.color=#0000ff
console.color_3.color=#00ff00
console.color_4.color=#ff0000
console.color_5.color=#ffff00
console.color_6.color=#ff00ff

View File

@ -101,6 +101,9 @@ sub connectToMetasploit {
sub _connectToMetasploit {
global('$database $client $mclient $console @exploits @auxiliary @payloads @post');
# reset rejected fingerprints
let(&verify_server, %rejected => %());
# update preferences
local('%props $property $value $flag $exception');
@ -179,7 +182,7 @@ sub _connectToMetasploit {
# create a console to force the database to initialize
local('$c');
$c = createConsole($client);
call($client, "console.destroy", $c);
call_async($client, "console.destroy", $c);
# connect to the database plz...
$database = connectToDatabase();
@ -212,7 +215,11 @@ sub _connectToMetasploit {
[$progress setNote: "Connected: ..."];
[$progress setProgress: 60];
dispatchEvent(&postSetup);
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

@ -5,6 +5,7 @@
import msf.*;
import armitage.*;
import console.*;
import ssl.*;
sub createEventLogTab {
this('$console $client');
@ -24,10 +25,38 @@ sub createEventLogTab {
[$frame addTab: "Event Log", $console, $null];
}
sub verify_server {
this('%rejected');
local('$fingerprints $fingerprint $check');
$fingerprints = split(', ', [$preferences getProperty: "trusted.servers", ""]);
foreach $fingerprint ($fingerprints) {
if ($fingerprint eq $1) {
return 1;
}
}
if (%rejected[$1] == 1) {
return $null;
}
$check = askYesNo("The team server's fingerprint is:\n\n<html><body><b> $+ $1 $+ </b></body></html>\n\nDoes this match the fingerprint shown\nwhen the team server started?", "Verify Fingerprint");
if ($check) {
%rejected[$1] = 1;
return $null;
}
else {
push($fingerprints, $1);
[$preferences setProperty: "trusted.servers", join(", ", $fingerprints)];
savePreferences();
return 1;
}
}
sub c_client {
# run this thing in its own thread to avoid really stupid deadlock situations
local('$handle');
$handle = connect($1, $2, 5000);
$handle = [[new SecureSocket: $1, int($2), &verify_server] client];
return wait(fork({
local('$client');
$client = newInstance(^RpcConnection, lambda({

View File

@ -34,7 +34,7 @@ sub updateDownloadModel {
}
sub createDownloadBrowser {
local('$table $model $panel $refresh $sorter $host $view');
local('$table $model $panel $refresh $sorter $host $view $sync');
$model = [new GenericTableModel: @("host", "name", "path", "size", "date"), "location", 16];
@ -55,14 +55,25 @@ sub createDownloadBrowser {
addMouseListener($table, lambda({
if ($0 eq "mousePressed" && [$1 getClickCount] >= 2) {
showLoot(\$model, \$table);
showLoot(\$model, \$table, $getme => "location");
}
}, \$model, \$table));
$view = [new JButton: "View"];
if ($client is $mclient) {
$sync = [new JButton: "Open Folder"];
[$sync addActionListener: gotoFile([new java.io.File: getFileProper(dataDirectory(), "downloads")])];
}
else {
$sync = [new JButton: "Sync Files"];
[$sync addActionListener: lambda({
downloadLoot(\$model, \$table, $getme => "location", $type => "downloads");
}, \$model, \$table)];
}
[$view addActionListener: lambda({
showLoot(\$model, \$table);
showLoot(\$model, \$table, $getme => "location");
}, \$model, \$table)];
$refresh = [new JButton: "Refresh"];
@ -72,7 +83,7 @@ sub createDownloadBrowser {
updateDownloadModel(\$model);
[$panel add: center($view, $refresh), [BorderLayout SOUTH]];
[$panel add: center($view, $sync, $refresh), [BorderLayout SOUTH]];
[$frame addTab: "Downloads", $panel, $null];
}

View File

@ -28,59 +28,109 @@ sub updateLootModel {
}, \$model));
}
sub showLoot {
local('$dialog $v $button $refresh $text $data');
$v = [$model getSelectedValue: $table];
sub downloadLoot {
thread(lambda({
local('$dest');
#$dest = chooseFile($title => "Where shall I save these files?", $dirsonly => 1, $always => 1);
$dest = getFileProper(dataDirectory(), $type);
_downloadLoot(\$model, \$table, \$getme, \$dest, $dtype => $type);
}, \$model, \$table, \$getme, \$type));
}
#
# well then, file is binary... let's do something else with it, like save it.
#
if ($v !is $null && "*binary*" iswm [$model getSelectedValueFromColumn: $table, "content_type"]) {
if ($client is $mclient) {
[gotoFile([new java.io.File: getFileParent($v)])];
sub _downloadLoot {
local('$progress $entries $index $host $location $name $type $when $loot $path');
$entries = [$model getSelectedValuesFromColumns: $table, @('host', $getme, 'name', 'content_type', 'updated_at', 'path')];
$progress = [new ProgressMonitor: $frame, "Download Data", "", 0, size($entries)];
foreach $index => $loot ($entries) {
($host, $location, $name, $type, $when, $path) = $loot;
[$progress setNote: $name];
# make the folder to store our downloads into
local('$handle $data $file');
if ($dtype eq "downloads") {
$file = getFileProper($dest, $host, $path, $name);
}
else {
local('$name $save');
$name = [$model getSelectedValueFromColumn: $table, "name"];
$save = getFileName($name);
thread(lambda({
local('$handle $data');
$data = getFileContent($v);
$handle = openf("> $+ $save");
writeb($handle, $data);
closef($handle);
[gotoFile([new java.io.File: cwd()])];
}, \$v, \$save));
$file = getFileProper($dest, $host, $name);
}
return;
mkdir(getFileParent($file));
# dump the file contents there...
$data = getFileContent($location);
$handle = openf("> $+ $file");
writeb($handle, $data);
closef($handle);
[$progress setProgress: $index + 1];
}
else if ($v !is $null) {
$dialog = [new JPanel];
[$dialog setLayout: [new BorderLayout]];
[$progress close];
showError("File(s) saved to:\n $+ $dest");
[gotoFile([new java.io.File: $dest])];
}
#$dialog = dialog("View Loot", 640, 480);
$text = [new console.Display: $preferences];
[$text setText: getFileContent($v)];
[$text setFont: [Font decode: [$preferences getProperty: "console.font.font", "Monospaced BOLD 14"]]];
[$text setForeground: [Color decode: [$preferences getProperty: "console.foreground.color", "#ffffff"]]];
[$text setBackground: [Color decode: [$preferences getProperty: "console.background.color", "#000000"]]];
sub showLoot {
thread(lambda(&_showLoot, \$model, \$table, \$getme));
}
$button = [new JButton: "Close"];
[$button addActionListener: lambda({ [$dialog setVisible: 0]; }, \$dialog)];
sub _postLoot {
local('$host $location $name $type $when');
($host, $location, $name, $type, $when) = $1;
$refresh = [new JButton: "Refresh"];
[$refresh addActionListener: lambda({ [$text setText: getFileContent($v)]; }, \$text, \$v)];
[$2 append: "
#
# $host $+ : $name
#
", "3", "#00ff00"];
[$dialog add: $text, [BorderLayout CENTER]];
[$dialog add: center($refresh), [BorderLayout SOUTH]];
[$frame addTab: "View", $dialog, $null, $v];
#[$dialog show];
}
if ("*binary*" iswm $type) {
[$2 append: "This is a binary file\n", "4", "#ff0000"];
}
else {
[$2 append: getFileContent($location), $null, $null];
}
}
sub _showLoot {
local('$loot $entries $dialog $display $refresh');
$dialog = [new JPanel];
[$dialog setLayout: [new BorderLayout]];
$display = [new console.Display: $preferences];
$entries = [$model getSelectedValuesFromColumns: $table, @('host', $getme, 'name', 'content_type', 'updated_at')];
foreach $loot ($entries) {
_postLoot($loot, $display);
yield 10;
}
$refresh = [new JButton: "Refresh"];
[$refresh addActionListener: lambda({
local('$r');
$r = [[$display console] getVisibleRect];
[$display setText: ""];
thread(lambda({
local('$loot');
foreach $loot ($entries) {
_postLoot($loot, $display);
yield 10;
}
dispatchEvent(lambda({
[[$display console] scrollRectToVisible: $r];
}, \$display, \$r));
}, \$entries, \$display, \$r));
}, \$entries, \$display)];
[$dialog add: $display, [BorderLayout CENTER]];
[[$display console] scrollRectToVisible: [new Rectangle: 0, 0, 0, 0]];
[$dialog add: center($refresh), [BorderLayout SOUTH]];
[$frame addTab: "View", $dialog, $null, $null];
}
sub createLootBrowser {
local('$table $model $panel $refresh $view $sorter $host');
local('$table $model $panel $refresh $view $sorter $host $sync');
$model = [new GenericTableModel: @("host", "type", "info", "date"), "path", 16];
@ -102,12 +152,17 @@ sub createLootBrowser {
addMouseListener($table, lambda({
if ($0 eq "mousePressed" && [$1 getClickCount] >= 2) {
showLoot(\$model, \$table);
showLoot(\$model, \$table, $getme => "path");
}
}, \$model, \$table));
$sync = [new JButton: "Sync Files"];
[$sync addActionListener: lambda({
downloadLoot(\$model, \$table, $getme => "path", $type => "loots");
}, \$model, \$table)];
[$view addActionListener: lambda({
showLoot(\$model, \$table);
showLoot(\$model, \$table, $getme => "path");
}, \$model, \$table)];
$refresh = [new JButton: "Refresh"];
@ -117,7 +172,12 @@ sub createLootBrowser {
updateLootModel(\$model);
[$panel add: center($view, $refresh), [BorderLayout SOUTH]];
if ($client is $mclient) {
[$panel add: center($view, $refresh), [BorderLayout SOUTH]];
}
else {
[$panel add: center($view, $sync, $refresh), [BorderLayout SOUTH]];
}
[$frame addTab: "Loot", $panel, $null];
}

View File

@ -60,12 +60,6 @@ sub view_items {
item($1, 'Console', 'C', { thread(&createConsoleTab); });
if ($RPC_CONSOLE !is $null) {
item($1, 'RPC Console', 'P', {
[$frame addTab: "msfrpcd", $RPC_CONSOLE, {}];
});
}
if ($mclient !is $client && $mclient !is $null) {
item($1, 'Event Log', 'E', &createEventLogTab);
}
@ -235,8 +229,13 @@ sub init_menus {
# setup some global keyboard shortcuts...
[$frame bindKey: "Ctrl+N", { thread(&createConsoleTab); }];
[$frame bindKey: "Ctrl+W", { [$frame openActiveTab]; }];
[$frame bindKey: "Ctrl+D", { [$frame closeActiveTab]; }];
[$frame bindKey: "Ctrl+O", { thread(&createPreferencesTab); }];
[$frame bindKey: "Ctrl+T", { [$frame snapActiveTab]; }];
[$frame bindKey: "Ctrl+Left", { [$frame previousTab]; }];
[$frame bindKey: "Ctrl+Right", { [$frame nextTab]; }];
setupWorkspaceShortcuts(workspaces());
cmd_safe("show exploits", {
local('$line $os $type $id $rank $name $k $date $exploit');

View File

@ -21,6 +21,7 @@
debug(7);
import msf.*;
import ssl.*;
sub result {
local('$rv $key $value');
@ -348,7 +349,7 @@ sub client {
sub main {
global('$client $mclient');
local('$server %sessions $sess_lock $read_lock $poll_lock $lock_lock %locks %readq $id @events $error $auth %cache $cach_lock $client_cache');
local('$server %sessions $sess_lock $read_lock $poll_lock $lock_lock %locks %readq $id @events $error $auth %cache $cach_lock $client_cache $handle');
$auth = unpack("H*", digest(rand() . ticks(), "MD5"))[0];
@ -396,6 +397,9 @@ sub main {
# set the LHOST to whatever the user specified
call_async($client, "core.setg", "LHOST", $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
#
@ -495,6 +499,12 @@ service framework-postgres start");
# setup the reporting API (must happen after base directory/database is setup)
initReporting();
$server = [new SecureServerSocket: int($sport)];
if (checkError($error)) {
println("[-] Could not listen on $sport $+ : $error");
[System exit: 0];
}
#
# spit out the details
#
@ -503,19 +513,22 @@ service framework-postgres start");
println("\tPort: $sport");
println("\tUser: $user");
println("\tPass: $pass");
println("\n\tFingerprint (check for this string when you connect):\n\t" . [$server fingerprint]);
println("\n" . rand(@("I'm ready to accept you or other clients for who they are",
"multi-player metasploit... ready to go",
"hacking is such a lonely thing, until now",
"feel free to connect now, Armitage is ready for collaboration")));
$id = 0;
while (1) {
$server = listen($sport, 0);
$handle = [$server accept];
if ($handle !is $null) {
%readq[$id] = %();
fork(&client, \$client, \$handle, \%sessions, \$read_lock, \$sess_lock, \$poll_lock, $queue => %readq[$id], \$id, \@events, \$auth, \%locks, \$lock_lock, \$cach_lock, \%cache, \$motd, \$client_cache, $_user => $user, $_pass => $pass);
%readq[$id] = %();
fork(&client, \$client, $handle => $server, \%sessions, \$read_lock, \$sess_lock, \$poll_lock, $queue => %readq[$id], \$id, \@events, \$auth, \%locks, \$lock_lock, \$cach_lock, \%cache, \$motd, \$client_cache, $_user => $user, $_pass => $pass);
$id++;
$id++;
}
}
}

View File

@ -183,9 +183,6 @@ sub client_workspace_items {
set_workspace($name);
}, \$name)];
}
# setup a keyboard shortcut for this workspace...
[$frame bindKey: "Ctrl+0", &reset_workspace];
}
sub set_workspace {
@ -219,5 +216,21 @@ sub workspaces {
sub saveWorkspaces {
[$preferences setProperty: "armitage.workspaces.menus", join("!!", map({ return join("@@", values($1)); }, $1))];
savePreferences();
savePreferences();
setupWorkspaceShortcuts($1);
}
sub setupWorkspaceShortcuts {
local('$x $y $workspace $name');
foreach $y => $workspace ($1) {
$name = $workspace['name'];
$x = $y + 1;
# setup a keyboard shortcut for this workspace...
[$frame bindKey: "Ctrl+ $+ $x", lambda({
set_workspace($name);
}, \$name)];
}
[$frame bindKey: "Ctrl+Backspace", &reset_workspace];
}

View File

@ -72,6 +72,19 @@ public class ArmitageApplication extends JFrame {
split.revalidate();
}
public void nextTab() {
tabs.setSelectedIndex((tabs.getSelectedIndex() + 1) % tabs.getTabCount());
}
public void previousTab() {
if (tabs.getSelectedIndex() == 0) {
tabs.setSelectedIndex(tabs.getTabCount() - 1);
}
else {
tabs.setSelectedIndex((tabs.getSelectedIndex() - 1) % tabs.getTabCount());
}
}
public void addTab(final String title, final JComponent tab, final ActionListener removeListener) {
if (SwingUtilities.isEventDispatchThread()) {
_addTab(title, tab, removeListener, null);
@ -117,6 +130,24 @@ public class ArmitageApplication extends JFrame {
}
}
public void openActiveTab() {
JComponent tab = (JComponent)tabs.getSelectedComponent();
if (tab != null) {
popAppTab(tab);
}
}
public void snapActiveTab() {
JComponent tab = (JComponent)tabs.getSelectedComponent();
Iterator i = apptabs.iterator();
while (i.hasNext()) {
ApplicationTab t = (ApplicationTab)i.next();
if (t.component == tab) {
snapAppTab(t.title, tab);
}
}
}
public void addAppTab(String title, JComponent component, ActionListener removeListener) {
ApplicationTab t = new ApplicationTab();
t.title = title;
@ -151,6 +182,18 @@ public class ArmitageApplication extends JFrame {
}
}
public void snapAppTab(String title, Component tab) {
/* capture the current tab in an image */
BufferedImage image = new BufferedImage(tab.getWidth(), tab.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
Graphics g = image.getGraphics();
tab.paint(g);
g.dispose();
if (screens != null) {
screens.saveScreenshot(image, title);
}
}
public void removeAppTab(Component tab, String title, ActionEvent ev) {
Iterator i = apptabs.iterator();
String titleshort = title != null ? title.split(" ")[0] : "%b%";
@ -218,15 +261,7 @@ public class ArmitageApplication extends JFrame {
JMenuItem c = new JMenuItem("Save screenshot", 'S');
c.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ev) {
/* capture the current tab in an image */
BufferedImage image = new BufferedImage(tab.getWidth(), tab.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
Graphics g = image.getGraphics();
tab.paint(g);
g.dispose();
if (screens != null) {
screens.saveScreenshot(image, title);
}
snapAppTab(title, tab);
}
});

View File

@ -14,7 +14,7 @@ import java.util.*;
/** A generic multi-feature console for use in the Armitage network attack tool */
public class Display extends JPanel {
protected JTextArea console;
protected JTextPane console;
protected Properties display;
protected Font consoleFont;
@ -45,6 +45,50 @@ public class Display extends JPanel {
}
}
private static Map colors = new HashMap();
public static AttributeSet getColor(String index, Properties preferences, String def) {
synchronized (colors) {
if (colors.get(index) == null) {
SimpleAttributeSet attrs = new SimpleAttributeSet();
Color temp = Color.decode(preferences.getProperty("console.color_" + index + ".color", def));
StyleConstants.setForeground(attrs, temp);
colors.put(index, attrs);
}
return (SimpleAttributeSet)colors.get(index);
}
}
public void append(final String text, final String index, final String fg) {
if (SwingUtilities.isEventDispatchThread()) {
_append(text, index, fg);
}
else {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
_append(text, index, fg);
}
});
}
}
public void _append(String text, String index, String foreground) {
try {
Rectangle r = console.getVisibleRect();
StyledDocument doc = console.getStyledDocument();
if (foreground == null) {
doc.insertString(doc.getLength(), text, null);
}
else {
doc.insertString(doc.getLength(), text, getColor(index, display, foreground));
}
console.scrollRectToVisible(r);
}
catch(Exception e) {
System.out.println(e);
}
}
public void setText(final String _text) {
if (SwingUtilities.isEventDispatchThread()) {
console.setText(_text);
@ -71,9 +115,9 @@ public class Display extends JPanel {
/* init the console */
console = new JTextArea();
console = new JTextPane();
console.setEditable(false);
console.setLineWrap(true);
//console.setLineWrap(true);
JScrollPane scroll = new JScrollPane(
console,
@ -111,6 +155,12 @@ public class Display extends JPanel {
setupFindShortcutFeature();
setupPageShortcutFeature();
setupFontShortcutFeature();
/* work-around for Nimbus L&F */
console.setBackground(new Color(0,0,0,0));
Color background = Color.decode(display.getProperty("console.background.color", "#000000"));
scroll.getViewport().setBackground(background);
console.setOpaque(false);
}
private void setupFindShortcutFeature() {

View File

@ -48,12 +48,12 @@ public class ConsolePool implements RpcConnection {
Map temp = (Map)client.execute("console.read", new Object[] { rv.get("id") + "" });
/* this is a sanity check to make sure this console is not dead or hung */
if ("true".equals(temp.get("busy")) || "".equals(temp.get("prompt"))) {
((RpcAsync)client).execute_async("console.destroy", new Object[] { rv.get("id") + "" });
//System.err.println("Kill Console: " + rv.get("id") + " => " + temp);
if ("failure".equals(temp.get("result")) || "true".equals(temp.get("busy") + "") || "".equals(temp.get("prompt") + "")) {
System.err.println("Kill Console: " + rv + " => " + temp);
client.execute("console.destroy", new Object[] { rv.get("id") + "" });
}
else {
//System.err.println("Reusing: " + rv.get("id"));
//System.err.println("Reusing: " + rv + " => " + temp);
return rv;
}
}
@ -78,17 +78,18 @@ public class ConsolePool implements RpcConnection {
public void release(String id) throws IOException {
/* make sure we're in a "clean" console */
HashMap rv = new HashMap();
rv.put("id", id);
boolean b;
synchronized (this) {
b = tracked.contains(id);
}
if (b) {
//System.err.println("Added: " + rv + " to pool");
client.execute("console.write", new Object[] { id, "back\n" });
synchronized (this) {
HashMap rv = new HashMap();
rv.put("id", id);
//System.err.println("Added: " + rv + " to pool");
inactive.add(rv);
}
}

View File

@ -113,7 +113,11 @@ public class MeterpreterSession implements Runnable {
readUntilSuccessful(c, true);
return;
}
else if (c.text.startsWith("route")) {
else if (c.text.startsWith("execute") && !teammode) {
readUntilSuccessful(c, false);
return;
}
else if (c.text.startsWith("route") && !teammode) {
readUntilSuccessful(c, false);
return;
}

View File

@ -1,6 +1,7 @@
package msf;
import java.io.*;
import java.util.*;
public class RpcAsync implements RpcConnection, Async {
protected RpcQueue queue;
@ -25,7 +26,28 @@ public class RpcAsync implements RpcConnection, Async {
return connection.execute(methodName);
}
protected Map cache = new HashMap();
public Object execute(String methodName, Object[] params) throws IOException {
return connection.execute(methodName, params);
if (methodName.equals("module.info") || methodName.equals("module.options") || methodName.equals("module.compatible_payloads")) {
StringBuilder keysb = new StringBuilder(methodName);
for(int i = 1; i < params.length; i++)
keysb.append(params[i].toString());
String key = keysb.toString();
Object result = cache.get(key);
if(result != null) {
return result;
}
result = connection.execute(methodName, params);
cache.put(key, result);
return result;
}
else {
return connection.execute(methodName, params);
}
}
}

View File

@ -1,119 +0,0 @@
package msf;
import java.net.*;
import java.io.*;
import javax.net.ssl.*;
import javax.net.*;
import java.security.*;
import java.security.cert.*;
/* taken from jIRCii, I developed it, so I get to do what I want ;) */
public class SecureSocket
{
protected SSLSocket socket;
public SecureSocket(String host, int port) throws Exception
{
socket = null;
DummySSLSocketFactory factory = new DummySSLSocketFactory();
socket = (SSLSocket)factory.createSocket(host, port);
socket.setSoTimeout(8192);
socket.startHandshake();
}
public Socket getSocket()
{
return socket;
}
private static class DummySSLSocketFactory extends SSLSocketFactory
{
private SSLSocketFactory factory;
public DummySSLSocketFactory()
{
try
{
SSLContext sslcontext = SSLContext.getInstance("SSL");
sslcontext.init(null, new TrustManager[] {new DummyTrustManager()}, new java.security.SecureRandom());
factory = (SSLSocketFactory) sslcontext.getSocketFactory();
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
public static SocketFactory getDefault()
{
return new DummySSLSocketFactory();
}
public Socket createSocket(Socket socket, String s, int i, boolean flag) throws IOException
{
return factory.createSocket(socket, s, i, flag);
}
public Socket createSocket(InetAddress inaddr, int i, InetAddress inaddr1, int j) throws IOException
{
return factory.createSocket(inaddr, i, inaddr1, j);
}
public Socket createSocket(InetAddress inaddr, int i) throws IOException
{
return factory.createSocket(inaddr, i);
}
public Socket createSocket(String s, int i, InetAddress inaddr, int j) throws IOException
{
return factory.createSocket(s, i, inaddr, j);
}
public Socket createSocket(String s, int i) throws IOException
{
return factory.createSocket(s, i);
}
public String[] getDefaultCipherSuites()
{
return factory.getSupportedCipherSuites();
}
public String[] getSupportedCipherSuites()
{
return factory.getSupportedCipherSuites();
}
}
private static class DummyTrustManager implements X509TrustManager
{
public void checkClientTrusted(X509Certificate ax509certificate[], String authType)
{
return;
}
public void checkServerTrusted(X509Certificate ax509certificate[], String authType)
{
return;
}
public boolean isClientTrusted(X509Certificate[] cert)
{
return true;
}
public boolean isServerTrusted(X509Certificate[] cert)
{
return true;
}
public X509Certificate[] getAcceptedIssuers()
{
return new X509Certificate[0];
}
}
}

View File

@ -0,0 +1,5 @@
package ssl;
public interface ArmitageTrustListener {
public boolean trust(String fingerprint);
}

View File

@ -0,0 +1,54 @@
package ssl;
import java.net.*;
import java.io.*;
import javax.net.ssl.*;
import javax.net.*;
import java.util.*;
import java.security.*;
import java.security.cert.*;
import java.math.*;
import javax.swing.*;
public class ArmitageTrustManager implements X509TrustManager {
protected ArmitageTrustListener checker;
public ArmitageTrustManager(ArmitageTrustListener checker) {
this.checker = checker;
}
public void checkClientTrusted(X509Certificate ax509certificate[], String authType) {
return;
}
public void checkServerTrusted(X509Certificate ax509certificate[], String authType) throws CertificateException {
try {
for (int x = 0; x < ax509certificate.length; x++) {
byte[] bytesOfMessage = ax509certificate[x].getEncoded();
MessageDigest md = MessageDigest.getInstance("SHA1");
byte[] thedigest = md.digest(bytesOfMessage);
BigInteger bi = new BigInteger(1, thedigest);
String fingerprint = bi.toString(16);
if (checker != null && !checker.trust(fingerprint))
throw new CertificateException("Certificate Rejected. Press Cancel.");
}
return;
}
catch (CertificateException cex) {
throw cex;
}
catch (Exception ex) {
throw new CertificateException(ex.getMessage());
}
}
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}

View File

@ -0,0 +1,86 @@
package ssl;
import java.net.*;
import java.io.*;
import javax.net.ssl.*;
import javax.net.*;
import java.security.*;
import java.security.cert.*;
import sleep.bridges.io.*;
/* internal package... I don't like it, but need it to generate a self-signed cert */
import sun.security.x509.*;
import java.math.*;
import java.util.*;
/* taken from jIRCii, I developed it, so I get to do what I want ;) */
public class SecureServerSocket {
protected ServerSocket server;
protected String last = "";
public String last() {
return last;
}
public IOObject accept() {
try {
Socket client = server.accept();
IOObject temp = new IOObject();
temp.openRead(client.getInputStream());
temp.openWrite(client.getOutputStream());
last = client.getInetAddress().getHostAddress();
client.setSoTimeout(0);
return temp;
}
catch (Exception ex) {
throw new RuntimeException(ex);
}
}
public SecureServerSocket(int port) throws Exception {
ServerSocketFactory factory = getFactory();
server = factory.createServerSocket(port, 32);
server.setSoTimeout(0); /* we wait forever until something comes */
server.setReuseAddress(true);
}
private ServerSocketFactory getFactory() throws Exception {
return SSLServerSocketFactory.getDefault();
}
public ServerSocket getServerSocket() {
return server;
}
/* grab the SSL cert we're using and digest it with SHA-1. Return this so we may
present it on server startup */
public String fingerprint() {
try {
FileInputStream is = new FileInputStream(System.getProperty("javax.net.ssl.keyStore"));
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
keystore.load(is, (System.getProperty("javax.net.ssl.keyStorePassword") + "").toCharArray());
Enumeration en = keystore.aliases();
if (en.hasMoreElements()) {
String alias = en.nextElement() + "";
java.security.cert.Certificate cert = keystore.getCertificate(alias);
byte[] bytesOfMessage = cert.getEncoded();
MessageDigest md = MessageDigest.getInstance("SHA1");
byte[] thedigest = md.digest(bytesOfMessage);
BigInteger bi = new BigInteger(1, thedigest);
return bi.toString(16);
}
}
catch (Exception ex) {
System.err.println(ex);
ex.printStackTrace();
}
return "unknown";
}
}

View File

@ -0,0 +1,47 @@
package ssl;
import java.net.*;
import java.io.*;
import javax.net.ssl.*;
import javax.net.*;
import java.security.*;
import java.security.cert.*;
import sleep.bridges.io.*;
/* taken from jIRCii, I developed it, so I get to do what I want ;) */
public class SecureSocket {
protected SSLSocket socket;
public SecureSocket(String host, int port, ArmitageTrustListener checker) throws Exception {
socket = null;
SSLContext sslcontext = SSLContext.getInstance("SSL");
sslcontext.init(null, new TrustManager[] { new ArmitageTrustManager(checker) }, new java.security.SecureRandom());
SSLSocketFactory factory = (SSLSocketFactory) sslcontext.getSocketFactory();
socket = (SSLSocket)factory.createSocket(host, port);
socket.setSoTimeout(4048);
socket.startHandshake();
}
public IOObject client() {
try {
IOObject temp = new IOObject();
temp.openRead(socket.getInputStream());
temp.openWrite(socket.getOutputStream());
socket.setSoTimeout(0);
return temp;
}
catch (Exception ex) {
throw new RuntimeException(ex);
}
}
public Socket getSocket() {
return socket;
}
}

View File

@ -1,6 +1,34 @@
Armitage Changelog
==================
14 May 12
---------
- Oopserific--dynamic workspace shortcuts were not bound until you
clicked the Workspaces menu. I fixed that.
- Improved console pool's ability to detect a dead console. If you saw
"null" prompts in an open tab, it's because of a dead console. Fixed
- Bound Ctrl+Backspace to reset dynamic workspaces. Ctrl+0 is now back
to what it originally did (resetting the font size to default).
- Added Ctrl+T to take a screenshot of the active tab
- Added Ctrl+W to pop the active tab into its own window
- Armitage team server is now SSL enabled. The teamserver script (you
are using it, right?) generates a certificate for you using keytool.
The server presents the SHA1 hash of its certificate. Armitage users
have the opportunity to verify and trust the hash of the certificate
presented to them or to reject it and not connect.
- Added Ctrl+Left / Ctrl+Right to quickly navigate through tabs.
- Added a check to prevent clients from connecting to msfrpcd directly
when teaming is enabled.
- Fixed a bug that prevented command shells from opening on some sessions
- Team server client now caches certain calls to RPC server.
- Reworked the Loot/Downloads View button. Now, all highlighted files are
displayed in one View tab. This makes searching easier. Each file is
displayed with a colored header (to make it easier to tell when one file
ends and the other begins).
- Added Sync Files button to Loot/Downloads tabs when connected to a team
server. This button will download all files associated with the highlighted
rows and save them in the Armitage data directory.
7 May 12
--------
Note: Armitage team server setup has changed. Refer to the manual for