commit
2e8b11ca78
Binary file not shown.
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<center><h1>Armitage 1.44-dev</h1></center>
|
||||
|
||||
<p>An attack management tool for Metasploit®
|
||||
<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® is a registered trademark of Rapid7 Inc.</small></p>
|
||||
<p><small>Metasploit® is a registered trademark of Rapid7</small></p>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package ssl;
|
||||
|
||||
public interface ArmitageTrustListener {
|
||||
public boolean trust(String fingerprint);
|
||||
}
|
|
@ -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];
|
||||
}
|
||||
}
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue