Merged modular branch into master

resolved_file
Chris Truncer 2014-12-24 08:37:31 -05:00
parent 309eefeb43
commit 6477642b6e
22 changed files with 717 additions and 506 deletions

97
Egress-Assess.ps1 Normal file → Executable file
View File

@ -1,6 +1,4 @@
function Invoke-EgressAssess {
function Egress-Assess {
<# <#
.Synopsis .Synopsis
@ -11,58 +9,50 @@ function Egress-Assess {
Due to processing overhead in Powershell, numbers are created in batches of 5,000. Due to processing overhead in Powershell, numbers are created in batches of 5,000.
Reference: http://powershell.org/wp/2013/09/16/powershell-performance-the-operator-and-when-to-avoid-it/ Reference: http://powershell.org/wp/2013/09/16/powershell-performance-the-operator-and-when-to-avoid-it/
.Parameter HTTP .Parameter Client
The switch to enable transfer over http The string containing the protocol to egress data over
.Parameter HTTPS
The switch to enable transfer over https
.Parameter FTP
The switch to enable transer over ftp
.Parameter IP .Parameter IP
The string containing the IP or hostname of the egress assess server. The string containing the IP or hostname of the egress assess server.
.Parameter Proxy
This switch is used when you need to exfiltrate data using the system proxy.
.Parameter Username .Parameter Username
The username for the ftp server The username for the ftp server
.Parameter Password .Parameter Password
The password for the ftp server The password for the ftp server
.Parameter CC .Parameter Datatype
Enable this switch if you want to send credit card data The string containing the data you want to generate and exfil
.Parameter SSN
Enable this switch if you want to send social securit numbers
.Parameter Size .Parameter Size
How many blocks of 5000 numbers to generate How many blocks of 5000 numbers to generate
.Example .Example
Import-Module Egress-Assess.ps1 Import-Module Egress-Assess.ps1
Egress-Assess -http -ip 127.0.0.1 -CC -Size 1 -Verbose Invoke-EgressAssess -client http -ip 127.0.0.1 -datatype cc -Size 1 -Verbose
Script created by @rvrsh3ll Script created by @rvrsh3ll @christruncer @harmj0y @sixdub
https://www.rvrsh3ll.net https://www.rvrsh3ll.net
http://www.rvrsh3ll.net/blog/
Thanks to @christruncer for the project and @harmjoy for the powershell help!
https://www.christophertruncer.com/ https://www.christophertruncer.com/
http://blog.harmj0y.net/ http://blog.harmj0y.net/
http://sixdub.net/
#> #>
[CmdletBinding()] [CmdletBinding()]
Param ( Param (
[switch]$HTTP, [Parameter(Mandatory=$True)]
[switch]$HTTPS, [string]$CLIENT,
[switch]$FTP,
[Parameter(Mandatory=$True)] [Parameter(Mandatory=$True)]
[string]$IP, [string]$IP,
[switch]$Proxy,
[Parameter(Mandatory=$True)]
[string]$Datatype,
[string]$Username, [string]$Username,
[string]$Password, [string]$Password,
[switch]$CC,
[switch]$SSN,
[int]$Size=1 [int]$Size=1
) )
@ -133,26 +123,26 @@ begin {
# check for cc or ssn and pass to body # check for cc or ssn and pass to body
if ($CC) { if ($DATATYPE -eq "cc") {
Generate-CreditCards Generate-CreditCards
$Body = @() $Body = @()
$Body = $allCC $Body = $allCC
if ($http){ if ($client -eq "http"){
$url = "http://" + $IP + "/ccdata.php" $url = "http://" + $IP + "/post_data.php"
} }
elseif ($https){ elseif ($client -eq "https") {
$url = "https://" + $IP + "/ccdata.php" $url = "https://" + $IP + "/post_data.php"
} }
} }
elseif ($SSN){ elseif ($DATATYPE -eq "ssn"){
Generate-SSN Generate-SSN
$Body = @() $Body = @()
$Body = $allSSN $Body = $allSSN
if ($http){ if ($client -eq "http"){
$url = "http://" + $IP + "/ssndata.php" $url = "http://" + $IP + "/post_data.php"
} }
elseif ($https){ elseif ($client -eq "https"){
$url = "https://" + $IP + "/ssndata.php" $url = "https://" + $IP + "/post_data.php"
} }
} }
else { else {
@ -163,6 +153,11 @@ begin {
[Net.ServicePointManager]::ServerCertificateValidationCallback = {$true} [Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}
$uri = New-Object -TypeName System.Uri -ArgumentList $url $uri = New-Object -TypeName System.Uri -ArgumentList $url
$wc = New-Object -TypeName System.Net.WebClient $wc = New-Object -TypeName System.Net.WebClient
if ($proxy) {
$proxy = [System.Net.WebRequest]::GetSystemWebProxy()
$proxy.Credentials = [System.Net.CredentialCache]::DefaultCredentials
$wc.proxy = $proxy
}
Write-Verbose "Uploading data.." Write-Verbose "Uploading data.."
$wc.UploadString($uri, $Body) $wc.UploadString($uri, $Body)
Write-Verbose "Transaction Complete." Write-Verbose "Transaction Complete."
@ -170,32 +165,40 @@ begin {
function Use-Ftp { function Use-Ftp {
if ($CC) { $Date = Get-Date -Format Mdyyyy_hhmmss
$Path = "ftpdata" + $Date + ".txt"
if ($DATATYPE -eq "cc") {
Generate-CreditCards Generate-CreditCards
out-file -filepath ftpdata.txt -inputobject $allCC -encoding ASCII $FTPData = $allCC
} }
elseif ($SSN){ elseif ($DATATYPE -eq "ssn"){
Generate-SSN Generate-SSN
out-file -filepath ftpdata.txt -inputobject $allSSN -encoding ASCII $FTPData=$allSSN
} }
else { else {
Write-Verbose "You did not provide a data type to generate." Write-Verbose "You did not provide a data type to generate."
} }
$Path = "ftpdata.txt" $Destination = "ftp://" + $IP + "/" + $Path
$Destination = "ftp://" + $IP + "/ftpdata.txt"
$Credential = New-Object -TypeName System.Net.NetworkCredential -ArgumentList $Username,$Password $Credential = New-Object -TypeName System.Net.NetworkCredential -ArgumentList $Username,$Password
# Create the FTP request and upload the file # Create the FTP request and upload the file
$FtpRequest = [System.Net.FtpWebRequest][System.Net.WebRequest]::Create($Destination) $FtpRequest = [System.Net.FtpWebRequest][System.Net.WebRequest]::Create($Destination)
if ($proxy) {
$proxy = [System.Net.WebRequest]::GetSystemWebProxy()
$proxy.Credentials = [System.Net.CredentialCache]::DefaultCredentials
$FtpRequest.proxy = $proxy
}
$FtpRequest.KeepAlive = $False
$FtpRequest.Method = [System.Net.WebRequestMethods+Ftp]::UploadFile $FtpRequest.Method = [System.Net.WebRequestMethods+Ftp]::UploadFile
$FtpRequest.Credentials = $Credential $FtpRequest.Credentials = $Credential
# Get the request stream, and write the file bytes to the stream # Get the request stream, and write the file bytes to the stream
$Encoder = [system.Text.Encoding]::UTF8
$RequestStream = $FtpRequest.GetRequestStream() $RequestStream = $FtpRequest.GetRequestStream()
Get-Content -Path $Path -Encoding Byte | % { $RequestStream.WriteByte($_); } $Encoder.GetBytes($FTPData) | % { $RequestStream.WriteByte($_); }
$RequestStream.Close() $RequestStream.Close()
Remove-Item $Path
Write-Verbose "File Transfer Complete." Write-Verbose "File Transfer Complete."
} }
@ -203,11 +206,11 @@ begin {
} }
process { process {
if ($http -or $https) { if ($client -eq "http" -or $client -eq "https") {
Use-HTTP Use-HTTP
} }
elseif ($ftp) { elseif ($client -eq "ftp") {
Use-Ftp Use-Ftp
} }
else { else {

View File

@ -6,461 +6,70 @@
# capabilities. # capabilities.
import argparse
import copy
import os
import random
import socket
import ssl
import string
import sys import sys
import time from common import helpers
import urllib2 from common import orchestra
from ftplib import FTP
from ftplib import error_perm
from SocketServer import ThreadingMixIn
from threading import Thread
from BaseHTTPServer import BaseHTTPRequestHandler
from BaseHTTPServer import HTTPServer
from pyftpdlib.authorizers import DummyAuthorizer
from pyftpdlib.handlers import FTPHandler
from pyftpdlib.servers import FTPServer
class GetHandler(BaseHTTPRequestHandler):
# Some of the http server code came from Dave Kennedy's AES shell
# over http - the server specific code
# should be performing GET requests Help from
# http://pymotw.com/2/BaseHTTPServer/
def do_GET(self):
# 404 since we aren't serving up any pages, only receiving data
self.send_response(404)
self.end_headers()
return
# handle post request
def do_POST(self):
# Gather the Posted URI from the agent/browser
# parsed_path = urlparse.urlparse(self.path)
uri_posted = self.path
uri_posted = uri_posted.replace("/", "")
#incoming_ip = self.client_address[0]
# current directory
exfil_directory = os.path.join(os.getcwd(), "data")
loot_path = exfil_directory + "/"
# Info for this from -
# http://stackoverflow.com/questions/13146064/simple-
# python-webserver-to-save-file
if uri_posted == "ccdata.php":
self.send_response(200)
self.end_headers()
loot_path = exfil_directory + "/"
# Check to make sure the agent directory exists, and a loot
# directory for the agent. If not, make them
if not os.path.isdir(loot_path):
os.makedirs(loot_path)
# Get the date info
current_date = time.strftime("%m/%d/%Y")
current_time = time.strftime("%H:%M:%S")
screenshot_name = current_date.replace("/", "") +\
"_" + current_time.replace(":", "") + "ccdata.txt"
# Read the length of the screenshot file being uploaded
screen_length = self.headers['content-length']
screen_data = self.rfile.read(int(screen_length))
# Write out the file
with open(loot_path + screenshot_name, 'w') as cc_data_file:
cc_data_file.write(screen_data)
elif uri_posted == "ssndata.php":
self.send_response(200)
self.end_headers()
# Check to make sure the agent directory exists, and a loot
# directory for the agent. If not, make them
if not os.path.isdir(loot_path):
os.makedirs(loot_path)
# Get the date info
current_date = time.strftime("%m/%d/%Y")
current_time = time.strftime("%H:%M:%S")
screenshot_name = current_date.replace("/", "") +\
"_" + current_time.replace(":", "") + "ssndata.txt"
# Read the length of the screenshot file being uploaded
screen_length = self.headers['content-length']
screen_data = self.rfile.read(int(screen_length))
# Write out the file
with open(loot_path + screenshot_name, 'w') as cc_data_file:
cc_data_file.write(screen_data)
# All other Post requests
else:
self.send_response(404)
self.end_headers()
print "Odd... someone else is trying to access this web server..."
print "Might want to check that out..."
return
class ThreadingHTTPServer(ThreadingMixIn, HTTPServer):
pass
def cli_parser():
# Command line argument parser
parser = argparse.ArgumentParser(
add_help=False,
description="The Egress-Assess is a tool used to assess egress filters\
protecting a network")
parser.add_argument(
'-h', '-?', '--h', '-help', '--help', action="store_true",
help=argparse.SUPPRESS)
protocols = parser.add_argument_group('Client Protocol Options')
protocols.add_argument(
"--ftp", default=False, action='store_true',
help="Extract data over FTP.")
protocols.add_argument("--http", default=False, action='store_true',
help="Extract data over http.")
protocols.add_argument("--https", default=False, action='store_true',
help="Extract data over https.")
protocols.add_argument("--ip", metavar="192.168.1.2", default=None,
help="IP to extract data to.")
servers = parser.add_argument_group('Server Protocol Options')
servers.add_argument(
"--ftp-server", default=False, action='store_true',
help="FTP Server that receives client data.")
servers.add_argument("--http-server", default=False, action='store_true',
help="HTTP and HTTPS server, receives POST data.")
ftp_options = parser.add_argument_group('FTP Options')
ftp_options.add_argument(
"--username", metavar="testuser", default=None,
help="Username for FTP server authentication.")
ftp_options.add_argument(
"--password", metavar="pass123", default=None,
help="Password for FTP server authentication.")
data_content = parser.add_argument_group('Data Content Options')
data_content.add_argument(
"--data-size", default=1, type=int,
help="Number of megs to send")
data_content.add_argument(
"--ssn", default=False, action='store_true',
help="Extract data containing fake social security numbers.")
data_content.add_argument(
'--cc', default=False, action='store_true',
help="Extract data containing fake credit card numbers")
args = parser.parse_args()
if args.h:
parser.print_help()
sys.exit()
# If using FTP, check to make sure a username and pass is provided
if (args.ftp or args.ftp_server) and (
args.password is None or args.username is None):
print "[*] Error: FTP Server requires a username and password!"
print "[*] Error: Please re-run and provide the required info!"
sys.exit()
if (args.ftp or args.http or args.https) and args.ip is None:
print "[*] Error: You said to act like a client, but provided no ip"
print "[*] Error: to connect to. Please re-run with required info!"
sys.exit()
if (args.ftp or args.http or args.https) and (args.cc is False and args.ssn is False):
print "[*] Error: You need to tell Egress-Assess the type of data to send!"
print "[*] Error: to connect to. Please re-run with required info!"
sys.exit()
if not (
args.ftp or args.http or args.https or args.http_server or
args.ftp_server):
print "[*] Error: You didn't tell Egress-Assess to act like \
a server or client!".replace(' ', '')
print "[*] Error: Please re-run and provide an action to perform!\n\n"
parser.print_help()
sys.exit()
return args
def completed_number(prefix, length, the_generator):
"""
'prefix' is the start of the CC number as a string, any number of digits.
'length' is the length of the CC number to generate. Typically 13 or 16
"""
ccnumber = prefix
# generate digits
while len(ccnumber) < (length - 1):
digit = str(the_generator.choice(range(0, 10)))
ccnumber.append(digit)
# Calculate sum
sum = 0
pos = 0
reversedCCnumber = []
reversedCCnumber.extend(ccnumber)
reversedCCnumber.reverse()
while pos < length - 1:
odd = int(reversedCCnumber[pos]) * 2
if odd > 9:
odd -= 9
sum += odd
if pos != (length - 2):
sum += int(reversedCCnumber[pos + 1])
pos += 2
# Calculate check digit
checkdigit = ((sum / 10 + 1) * 10 - sum) % 10
ccnumber.append(str(checkdigit))
return ''.join(ccnumber)
def credit_card_number(prefixList, length, howMany):
generator = random.Random()
generator.seed()
result = []
while len(result) < howMany:
ccnumber = copy.copy(generator.choice(prefixList))
result.append(completed_number(ccnumber, length, generator))
return result
def ftp_client_connect(command_line_object):
# Create FTP objects that connects to the ftp server
# with the provided username and password
try:
ftp = FTP(command_line_object.ip)
except socket.gaierror:
print "[*] Error: Cannot connect to FTP server. Checking provided ip!"
sys.exit()
try:
ftp.login(command_line_object.username, command_line_object.password)
except error_perm:
print "[*] Error: Username or password is incorrect! Please re-run."
sys.exit()
# Create file to upload
if command_line_object.ssn:
# Get the date info
current_date = time.strftime("%m/%d/%Y")
current_time = time.strftime("%H:%M:%S")
ftp_file_name = current_date.replace("/", "") +\
"_" + current_time.replace(":", "") + "ssndata.txt"
# Generate 150000 SSNs for http(s) transfer
# This is about 1.9 megs
ssns = ''
for single_ssn in range(0, 81500 * command_line_object.data_size):
ssns += generate_ssn() + ', '
with open(os.getcwd() + "/" + ftp_file_name, 'w') as ssn_temp_file:
ssn_temp_file.write(ssns)
elif command_line_object.cc:
# Get the date info
current_date = time.strftime("%m/%d/%Y")
current_time = time.strftime("%H:%M:%S")
ftp_file_name = current_date.replace("/", "") +\
"_" + current_time.replace(":", "") + "ccdata.txt"
all_ccs = ''
credit_cards = generate_credit_cards(command_line_object)
for card in credit_cards:
all_ccs += card + ', '
with open(os.getcwd() + "/" + ftp_file_name, 'w') as cc_temp_file:
cc_temp_file.write(all_ccs)
ftp.storlines("STOR " + ftp_file_name, open(ftp_file_name))
ftp.quit()
os.remove(ftp_file_name)
print "[*] File sent!!!"
return
def ftp_server(command_line_object):
# current directory
exfil_directory = os.path.join(os.getcwd(), "data")
loot_path = exfil_directory + "/"
# Check to make sure the agent directory exists, and a loot
# directory for the agent. If not, make them
if not os.path.isdir(loot_path):
os.makedirs(loot_path)
try:
authorizer = DummyAuthorizer()
authorizer.add_user(
command_line_object.username, command_line_object.password,
loot_path, perm="lrw")
handler = FTPHandler
handler.authorizer = authorizer
# Define a customized banner (string returned when client connects)
handler.banner = "Connecting to Egress-Assess's FTP server!"
server = FTPServer(('', 21), handler)
server.serve_forever()
except ValueError:
print "[*] Error: The directory you provided may not exist!"
print "[*] Error: Please re-run with a valid FTP directory."
sys.exit()
def generate_credit_cards(command_object):
# Fake credit card generation code came from:
# https://github.com/grahamking/darkcoding-credit-card
# credit card constants
visaPrefixList = [
['4', '5', '3', '9'],
['4', '5', '5', '6'],
['4', '9', '1', '6'],
['4', '5', '3', '2'],
['4', '9', '2', '9'],
['4', '0', '2', '4', '0', '0', '7', '1'],
['4', '4', '8', '6'],
['4', '7', '1', '6'],
['4']]
mastercardPrefixList = [
['5', '1'], ['5', '2'], ['5', '3'], ['5', '4'], ['5', '5']]
amexPrefixList = [['3', '4'], ['3', '7']]
mastercards = credit_card_number(
mastercardPrefixList, 16, 19800 * command_object.data_size)
visas = credit_card_number(
visaPrefixList, 16, 19800 * command_object.data_size)
amexes = credit_card_number(
amexPrefixList, 15, 19800 * command_object.data_size)
all_cards = mastercards + visas + amexes
return all_cards
def generate_ssn():
ssn = randomNumbers(9)
ssn = ssn[0:3] + "-" + ssn[3:5] + "-" + ssn[5:9]
return ssn
def http_server():
Thread(target=serve_on_port, args=[443]).start()
return
def randomNumbers(b):
"""
Returns a random string/key of "b" characters in length, defaults to 5
"""
random_number = int(''.join(random.choice(string.digits) for x in range(b))
) + 10000
if random_number < 100000:
random_number = random_number + 100000
return str(random_number)
def serve_on_port(port):
if port == 443:
cert_path = os.getcwd() + '/server.pem'
server = ThreadingHTTPServer(("0.0.0.0", port), GetHandler)
server.socket = ssl.wrap_socket(
server.socket, certfile=cert_path, server_side=True)
server.serve_forever()
elif port == 80:
server80 = ThreadingHTTPServer(("0.0.0.0", port), GetHandler)
server80.serve_forever()
return
def title_screen():
os.system('clear')
print "################################################################################"
print "# Egress-Assess #"
print "################################################################################\n"
if __name__ == "__main__": if __name__ == "__main__":
title_screen() helpers.title_screen()
cli_parsed = cli_parser() cli_parsed = helpers.cli_parser()
if cli_parsed.http or cli_parsed.https: the_conductor = orchestra.Conductor()
if cli_parsed.ssn:
# Generate 150000 SSNs for http(s) transfer
# This is about 1.9 megs
post_data = ''
for single_ssn in range(0, 81500 * cli_parsed.data_size):
post_data += generate_ssn() + ', '
if cli_parsed.https:
post_url = 'https://' + cli_parsed.ip + '/ssndata.php'
elif cli_parsed.http:
post_url = 'http://' + cli_parsed.ip + '/ssndata.php'
elif cli_parsed.cc: # Check if only listing supported server/client protocols or datatypes
# Generate about 1.8 megs of different credit cards if cli_parsed.list_servers:
post_data = '' print "[*] Supported server protocols: \n"
credit_cards = generate_credit_cards(cli_parsed) the_conductor.load_server_protocols(cli_parsed)
for card in credit_cards: for name, server_module in the_conductor.server_protocols.iteritems():
post_data += card + ', ' print "[+] " + server_module.protocol
# Setup URL that data is sent to, then post it there print
if cli_parsed.https:
post_url = 'https://' + cli_parsed.ip + '/ccdata.php'
elif cli_parsed.http:
post_url = 'http://' + cli_parsed.ip + '/ccdata.php'
try:
f = urllib2.urlopen(post_url, post_data)
f.close()
print "[*] File sent!!!"
except urllib2.URLError:
print "[*] Error: Web server may not be active on " + cli_parsed.ip
print "[*] Error: Please check server to make sure it is active!"
sys.exit() sys.exit()
elif cli_parsed.ftp: elif cli_parsed.list_clients:
ftp_client_connect(cli_parsed) print "[*] Supported client protocols: \n"
the_conductor.load_client_protocols(cli_parsed)
for name, client_module in the_conductor.client_protocols.iteritems():
print "[+] " + client_module.protocol
print
sys.exit()
elif cli_parsed.http_server: elif cli_parsed.list_datatypes:
try: print "[*] Supported data types: \n"
print "[*] Starting web server..." the_conductor.load_datatypes(cli_parsed)
# bind to all interfaces for name, datatype_module in the_conductor.datatypes.iteritems():
Thread(target=serve_on_port, args=[443]).start() print "[+] " + datatype_module.cli + " - (" +\
Thread(target=serve_on_port, args=[80]).start() datatype_module.description + ")"
print "[*] Web server is currently running" print
print "[*] Type \"killall -9 python\" to stop the web server." sys.exit()
# handle keyboard interrupts
except KeyboardInterrupt:
print "[!] Rage quiting, and stopping the web server!"
elif cli_parsed.ftp_server: if cli_parsed.server is not None:
ftp_server(cli_parsed) the_conductor.load_server_protocols(cli_parsed)
for full_path, server in the_conductor.server_protocols.iteritems():
if server.protocol == cli_parsed.server.lower():
server.serve()
elif cli_parsed.client is not None:
# load up all supported client protocols and datatypes
the_conductor.load_client_protocols(cli_parsed)
the_conductor.load_datatypes(cli_parsed)
# Loop through and find the requested datatype
for name, datatype_module in the_conductor.datatypes.iteritems():
if datatype_module.cli == cli_parsed.datatype.lower():
generated_data = datatype_module.generate_data()
# Once data has been generated, transmit it using the
# protocol requested by the user
for proto_name, proto_module in the_conductor.client_protocols.iteritems():
if proto_module.protocol == cli_parsed.client.lower():
proto_module.transmit(generated_data)
sys.exit()
print "[*] Error: You either didn't provide a valid datatype or client protocol to use."
print "[*] Error: Re-run and use --list-datatypes or --list-clients to see possible options."
sys.exit()

View File

@ -24,25 +24,18 @@ Blog posts are available here:
Typical use case for Egress-Assess is to copy this tool in two locations. One location will act as the server, the other will act as the client. Egress-Assess can send data over FTP, HTTP, and HTTPS. Typical use case for Egress-Assess is to copy this tool in two locations. One location will act as the server, the other will act as the client. Egress-Assess can send data over FTP, HTTP, and HTTPS.
To extract data over FTP, you would first start Egress-Assesss FTP server by selecting “ftp-server” and providing a username and password to use: To extract data over FTP, you would first start Egress-Assesss FTP server by selecting “--server ftp” and providing a username and password to use:
./Egress-Assess.py ftp-server username testuser password pass123 ./Egress-Assess.py --server ftp --username testuser --password pass123
Now, to have the client connect and send data to the ftp server, you could run... Now, to have the client connect and send data to the ftp server, you could run...
./Egress-Assess.py --ftp --username testuser --password pass123 --ip 192.168.63.149 --ssn ./Egress-Assess.py --client ftp --username testuser --password pass123 --ip 192.168.63.149 --datatype ssn
Also, you can setup Egress-Assess to act as a web server by running.... Also, you can setup Egress-Assess to act as a web server by running....
./Egress-Assess.py --http-server ./Egress-Assess.py --server https
Then, to send data to the FTP server, and to specifically send 15 megs of credit card data, run the following command... Then, to send data to the FTP server, and to specifically send 15 megs of credit card data, run the following command...
./Egress-Assess.py http data-size 15 ip 192.168.63.149 cc ./Egress-Assess.py --client https --data-size 15 --ip 192.168.63.149 --datatype cc
Upcoming Changes
================
1. Make Egress-Assess modular so users can easily extend the tool to support additional frameworks and data types.

0
common/__init__.py Normal file
View File

116
common/helpers.py Normal file
View File

@ -0,0 +1,116 @@
'''
This is for functions potentially used by all modules
'''
import argparse
import os
import random
import string
import sys
def cli_parser():
# Command line argument parser
parser = argparse.ArgumentParser(
add_help=False,
description="The Egress-Assess is a tool used to assess egress filters\
protecting a network")
parser.add_argument(
'-h', '-?', '--h', '-help', '--help', action="store_true",
help=argparse.SUPPRESS)
protocols = parser.add_argument_group('Client Protocol Options')
protocols.add_argument(
"--client", default=None, metavar="[http]",
help="Extract data over the specified protocol.")
protocols.add_argument(
"--list-clients", default=False, action='store_true',
help="List all supported client protocols.")
protocols.add_argument("--ip", metavar="192.168.1.2", default=None,
help="IP to extract data to.")
servers = parser.add_argument_group('Server Protocol Options')
servers.add_argument(
"--server", default=None, metavar='[http]',
help="Create a server for the specified protocol.")
servers.add_argument("--list-servers", default=False, action='store_true',
help="Lists all supported server protocols.")
ftp_options = parser.add_argument_group('FTP Options')
ftp_options.add_argument(
"--username", metavar="testuser", default=None,
help="Username for FTP server authentication.")
ftp_options.add_argument(
"--password", metavar="pass123", default=None,
help="Password for FTP server authentication.")
data_content = parser.add_argument_group('Data Content Options')
data_content.add_argument(
"--datatype", default=None, metavar='[ssn]',
help="Extract data containing fake social security numbers.")
data_content.add_argument(
"--data-size", default=1, type=int,
help="Number of megs to send")
data_content.add_argument(
"--list-datatypes", default=False, action='store_true',
help="List all data types that can be generated by the framework.")
args = parser.parse_args()
if args.h:
parser.print_help()
sys.exit()
if (args.server == "ftp" or args.client == "ftp") and (
args.username is None or args.password is None):
print "[*] Error: FTP connections require a username and password!"
print "[*] Error: Please re-run and provide the required info!"
sys.exit()
if args.client and args.ip is None:
print "[*] Error: You said to act like a client, but provided no ip"
print "[*] Error: to connect to. Please re-run with required info!"
sys.exit()
if (args.client is not None) and (args.datatype is None):
print "[*] Error: You need to tell Egress-Assess the type of data to send!"
print "[*] Error: to connect to. Please re-run with required info!"
sys.exit()
if (args.client is None and args.server is None and
args.list_servers is None and args.list_clients is None and
args.list_datatypes is None):
print "[*] Error: You didn't tell Egress-Assess to act like \
a server or client!".replace(' ', '')
print "[*] Error: Please re-run and provide an action to perform!"
parser.print_help()
sys.exit()
return args
def randomNumbers(b):
"""
Returns a random string/key of "b" characters in length, defaults to 5
"""
random_number = int(''.join(random.choice(string.digits) for x in range(b))
) + 10000
if random_number < 100000:
random_number = random_number + 100000
return str(random_number)
def title_screen():
os.system('clear')
print "################################################################################"
print "# Egress-Assess #"
print "################################################################################\n"
return
def ea_path():
return os.getcwd()

55
common/orchestra.py Normal file
View File

@ -0,0 +1,55 @@
'''
This is the conductor which controls everything
'''
import glob
import imp
from protocols.servers import *
from protocols.clients import *
from datatypes import *
class Conductor:
def __init__(self):
# Create dictionaries of supported modules
# empty until stuff loaded into them
self.client_protocols = {}
self.server_protocols = {}
self.datatypes = {}
def load_client_protocols(self, command_line_object):
for name in glob.glob('protocols/clients/*.py'):
if name.endswith("__init__.py"):
pass
elif name.endswith(".pyc"):
pass
else:
loaded_client_proto = imp.load_source(name.replace("/", ".").rstrip('.py'), name)
self.client_protocols[name] = loaded_client_proto.Client(command_line_object)
return
def load_server_protocols(self, command_line_object):
for name in glob.glob('protocols/servers/*.py'):
if name.endswith("__init__.py"):
pass
elif name.endswith(".pyc"):
pass
else:
loaded_server_proto = imp.load_source(name.replace("/", ".").rstrip('.py'), name)
self.server_protocols[name] = loaded_server_proto.Server(command_line_object)
return
def load_datatypes(self, command_line_object):
for name in glob.glob('datatypes/*.py'):
if name.endswith("__init__.py"):
pass
elif name.endswith(".pyc"):
pass
else:
loaded_datatypes = imp.load_source(name.replace("/", ".").rstrip('.py'), name)
self.datatypes[name] = loaded_datatypes.Datatype(command_line_object)
return

0
datatypes/__init__.py Normal file
View File

98
datatypes/creditcards.py Normal file
View File

@ -0,0 +1,98 @@
'''
This module generates credit card data
'''
import copy
import random
class Datatype:
def __init__(self, cli_object):
self.cli = "cc"
self.description = "Credit Card Numbers"
self.filetype = "text"
self.datasize = int(cli_object.data_size)
def completed_number(self, prefix, length, the_generator):
"""
'prefix' is the start of the CC number as a string, any number of digits.
'length' is the length of the CC number to generate. Typically 13 or 16
"""
ccnumber = prefix
# generate digits
while len(ccnumber) < (length - 1):
digit = str(the_generator.choice(range(0, 10)))
ccnumber.append(digit)
# Calculate sum
sum = 0
pos = 0
reversedCCnumber = []
reversedCCnumber.extend(ccnumber)
reversedCCnumber.reverse()
while pos < length - 1:
odd = int(reversedCCnumber[pos]) * 2
if odd > 9:
odd -= 9
sum += odd
if pos != (length - 2):
sum += int(reversedCCnumber[pos + 1])
pos += 2
# Calculate check digit
checkdigit = ((sum / 10 + 1) * 10 - sum) % 10
ccnumber.append(str(checkdigit))
return ''.join(ccnumber)
def credit_card_number(self, prefixList, length, howMany):
generator = random.Random()
generator.seed()
result = []
while len(result) < howMany:
ccnumber = copy.copy(generator.choice(prefixList))
result.append(self.completed_number(ccnumber, length, generator))
return result
def generate_data(self):
# credit card constants
visaPrefixList = [
['4', '5', '3', '9'],
['4', '5', '5', '6'],
['4', '9', '1', '6'],
['4', '5', '3', '2'],
['4', '9', '2', '9'],
['4', '0', '2', '4', '0', '0', '7', '1'],
['4', '4', '8', '6'],
['4', '7', '1', '6'],
['4']]
mastercardPrefixList = [
['5', '1'], ['5', '2'], ['5', '3'], ['5', '4'], ['5', '5']]
amexPrefixList = [['3', '4'], ['3', '7']]
mastercards = self.credit_card_number(
mastercardPrefixList, 16, 19800 * self.datasize)
visas = self.credit_card_number(
visaPrefixList, 16, 19800 * self.datasize)
amexes = self.credit_card_number(
amexPrefixList, 15, 19800 * self.datasize)
all_cards = mastercards + visas + amexes
final_cards = ''
for card in all_cards:
final_cards += card + ', '
return final_cards

28
datatypes/socials.py Normal file
View File

@ -0,0 +1,28 @@
'''
This module generates social security numbers
'''
from common import helpers
class Datatype:
def __init__(self, cli_object):
self.cli = "ssn"
self.description = "Social Security Numbers"
self.filetype = "text"
self.datasize = int(cli_object.data_size)
def create_ssn(self):
ssn = helpers.randomNumbers(9)
ssn = ssn[0:3] + "-" + ssn[3:5] + "-" + ssn[5:9]
return ssn
def generate_data(self):
ssns = ''
# This is approx 1 meg of socials
for single_ssn in range(0, 81500 * self.datasize):
ssns += self.create_ssn() + ', '
return ssns

0
protocols/__init__.py Normal file
View File

View File

View File

@ -0,0 +1,50 @@
'''
This is the ftp client code
'''
import os
import socket
import sys
import time
from common import helpers
from ftplib import FTP
from ftplib import error_perm
class Client:
def __init__(self, cli_object):
self.protocol = "ftp"
self.remote_server = cli_object.ip
self.username = cli_object.username
self.password = cli_object.password
def transmit(self, data_to_transmit):
try:
ftp = FTP(self.remote_server)
except socket.gaierror:
print "[*] Error: Cannot connect to FTP server. Checking provided ip!"
sys.exit()
try:
ftp.login(self.username, self.password)
except error_perm:
print "[*] Error: Username or password is incorrect! Please re-run."
sys.exit()
# Create file name and write out file for ftp transfer
current_date = time.strftime("%m/%d/%Y")
current_time = time.strftime("%H:%M:%S")
ftp_file_name = current_date.replace("/", "") +\
"_" + current_time.replace(":", "") + "ftp_data.txt"
with open(helpers.ea_path() + "/" + ftp_file_name, 'w') as cc_temp_file:
cc_temp_file.write(data_to_transmit)
ftp.storlines("STOR " + ftp_file_name, open(helpers.ea_path() + "/" + ftp_file_name))
ftp.quit()
os.remove(helpers.ea_path() + "/" + ftp_file_name)
print "[*] File sent!!!"

View File

@ -0,0 +1,32 @@
'''
This is the web client code
'''
import sys
import urllib2
class Client:
def __init__(self, cli_object):
# Really http and https
self.data_to_transmit = ''
self.remote_server = cli_object.ip
self.protocol = "http"
def transmit(self, data_to_transmit):
# Create the url to post to
url = "http://" + self.remote_server + "/post_data.php"
# Post the data to the web server at the specified URL
try:
f = urllib2.urlopen(url, data_to_transmit)
f.close()
print "[*] File sent!!!"
except urllib2.URLError:
print "[*] Error: Web server may not be active on " + self.remote_server
print "[*] Error: Please check server to make sure it is active!"
sys.exit()
return

View File

@ -0,0 +1,32 @@
'''
This is the web client code
'''
import sys
import urllib2
class Client:
def __init__(self, cli_object):
# Really http and https
self.data_to_transmit = ''
self.remote_server = cli_object.ip
self.protocol = "https"
def transmit(self, data_to_transmit):
# Create the url to post to
url = "https://" + self.remote_server + "/post_data.php"
# Post the data to the web server at the specified URL
try:
f = urllib2.urlopen(url, data_to_transmit)
f.close()
print "[*] File sent!!!"
except urllib2.URLError:
print "[*] Error: Web server may not be active on " + self.remote_server
print "[*] Error: Please check server to make sure it is active!"
sys.exit()
return

View File

View File

@ -0,0 +1,49 @@
'''
This is the code for the ftp server
'''
import os
from pyftpdlib.authorizers import DummyAuthorizer
from pyftpdlib.handlers import FTPHandler
from pyftpdlib.servers import FTPServer
class Server:
def __init__(self, cli_object):
self.protocol = "ftp"
self.username = cli_object.username
self.password = cli_object.password
self.data_directory = ""
def serve(self):
# current directory
exfil_directory = os.path.join(os.getcwd(), "data")
loot_path = exfil_directory + "/"
# Check to make sure the agent directory exists, and a loot
# directory for the agent. If not, make them
if not os.path.isdir(loot_path):
os.makedirs(loot_path)
try:
authorizer = DummyAuthorizer()
authorizer.add_user(
self.username, self.password,
loot_path, perm="elradfmwM")
handler = FTPHandler
handler.authorizer = authorizer
# Define a customized banner (string returned when client connects)
handler.banner = "Connecting to Egress-Assess's FTP server!"
server = FTPServer(('', 21), handler)
server.serve_forever()
except ValueError:
print "[*] Error: The directory you provided may not exist!"
print "[*] Error: Please re-run with a valid FTP directory."
sys.exit()
return

View File

@ -0,0 +1,32 @@
'''
This is the code for the web server
'''
from protocols.servers.serverlibs import base_handler
from protocols.servers.serverlibs import threaded_http
from threading import Thread
class Server:
def __init__(self, cli_object):
self.protocol = "http"
def serve(self):
try:
print "[*] Starting web (http) server..."
# bind to all interfaces
Thread(target=self.serve_on_port).start()
print "[*] Web server is currently running"
print "[*] Type \"killall -9 python\" to stop the web server."
# handle keyboard interrupts
except KeyboardInterrupt:
print "[!] Rage quiting, and stopping the web server!"
def serve_on_port(self):
server80 = threaded_http.ThreadingHTTPServer(
("0.0.0.0", 80), base_handler.GetHandler)
server80.serve_forever()
return

View File

@ -0,0 +1,38 @@
'''
This is the code for the web server
'''
import ssl
from common import helpers
from protocols.servers.serverlibs import base_handler
from protocols.servers.serverlibs import threaded_http
from threading import Thread
class Server:
def __init__(self, cli_object):
self.protocol = "https"
def serve(self):
try:
print "[*] Starting web (https) server..."
# bind to all interfaces
Thread(target=self.serve_on_port).start()
print "[*] Web server is currently running"
print "[*] Type \"killall -9 python\" to stop the web server."
# handle keyboard interrupts
except KeyboardInterrupt:
print "[!] Rage quiting, and stopping the web server!"
def serve_on_port(self):
cert_path = helpers.ea_path() +\
'/protocols/servers/serverlibs/server.pem'
server = threaded_http.ThreadingHTTPServer(
("0.0.0.0", 443), base_handler.GetHandler)
server.socket = ssl.wrap_socket(
server.socket, certfile=cert_path, server_side=True)
server.serve_forever()
return

View File

View File

@ -0,0 +1,67 @@
import os
import time
from BaseHTTPServer import BaseHTTPRequestHandler
from common import helpers
class GetHandler(BaseHTTPRequestHandler):
# Some of the http server code came from Dave Kennedy's AES shell
# over http - the server specific code
# should be performing GET requests Help from
# http://pymotw.com/2/BaseHTTPServer/
def do_GET(self):
# 404 since we aren't serving up any pages, only receiving data
self.send_response(404)
self.end_headers()
return
# handle post request
def do_POST(self):
# Gather the Posted URI from the agent/browser
# parsed_path = urlparse.urlparse(self.path)
uri_posted = self.path
uri_posted = uri_posted.replace("/", "")
#incoming_ip = self.client_address[0]
# current directory
exfil_directory = os.path.join(helpers.ea_path(), "data")
loot_path = exfil_directory + "/"
# Info for this from -
# http://stackoverflow.com/questions/13146064/simple-
# python-webserver-to-save-file
if uri_posted == "post_data.php":
self.send_response(200)
self.end_headers()
# Check to make sure the agent directory exists, and a loot
# directory for the agent. If not, make them
if not os.path.isdir(loot_path):
os.makedirs(loot_path)
# Get the date info
current_date = time.strftime("%m/%d/%Y")
current_time = time.strftime("%H:%M:%S")
screenshot_name = current_date.replace("/", "") +\
"_" + current_time.replace(":", "") + "web_data.txt"
# Read the length of the screenshot file being uploaded
screen_length = self.headers['content-length']
screen_data = self.rfile.read(int(screen_length))
# Write out the file
with open(loot_path + screenshot_name, 'w') as cc_data_file:
cc_data_file.write(screen_data)
# All other Post requests
else:
self.send_response(404)
self.end_headers()
print "Odd... someone else is trying to access this web server..."
print "Might want to check that out..."
return

View File

@ -0,0 +1,6 @@
from BaseHTTPServer import HTTPServer
from SocketServer import ThreadingMixIn
class ThreadingHTTPServer(ThreadingMixIn, HTTPServer):
pass

View File

@ -1,15 +1,18 @@
#!/bin/bash #!/bin/bash
clear clear
echo "[*] Installing Egress-Assess Dependencies..."
echo "[*] Installing pyftpdlib..." echo "[*] Installing pyftpdlib..."
git clone https://github.com/giampaolo/pyftpdlib.git git clone https://github.com/giampaolo/pyftpdlib.git
cd pyftpdlib cd pyftpdlib
python setup.py install python setup.py install
cd .. cd ..
rm -rf pyftpdlib rm -rf pyftpdlib
cd .. cd ../protocols/servers/serverlibs
clear clear
echo "[*] Generating SSL Certificate" echo "[*] Generating SSL Certificate"
openssl req -new -x509 -keyout server.pem -out server.pem -days 365 -nodes openssl req -new -x509 -keyout server.pem -out server.pem -days 365 -nodes
echo echo
echo
echo "[*] Install complete!" echo "[*] Install complete!"
echo "[*] Enjoy Egress-Assess!"