Initial commit
1 parent 352e9de commit 7387d6d6f71c9ac6999ae02f57088fe5157fe2f2
root authored on 7 May 2019
Showing 5 changed files
View
344
Classes/CommandArgumentFilter.php 0 → 100644
<?php
/**
* Filters an array and extracts and validates command line arguments
*
* @author Patrick Forget <patforg at webtrendi.com>
*/
 
namespace Clapp;
 
/**
* Filters an array and extracts and validates command line arguments
*
* @author Patrick Forget <patforg at webtrendi.com>
*/
class CommandArgumentFilter
{
/**
* Command line arguments
* @var array
*/
private $arguments = array();
/**
* Definition of allowed parameters
* @var \Clapp\CommandLineArgumentDefinition
*/
private $definitions = null;
 
/**
* Flag if arguments have been parsed in to params
* @var boolean
*/
private $parsed = false;
 
/**
* Parsed params
* @var array
*/
private $params = array();
 
/**
* Trailing values
* @var string
*/
private $trailingValues = "";
/**
* program name
* @var string
*/
private $programName = "";
 
/**
* class constructor
*
* @author Patrick Forget <patforg at webtrendi.com>
*
* @param \Clapp\CommandLineDefinition $definitions contains list of allowed parameters
* @param array $args list of arguments to filter.
*/
public function __construct(\Clapp\CommandLineArgumentDefinition $definitions, $args)
{
if (is_array($args)) {
$this->arguments = $args;
} //if
 
$this->definitions = $definitions;
} // __construct()
 
/**
* returns parameter matching provided name
*
* @author Patrick Forget <patforg at webtrendi.com>
*
* @param string name of the paramter to retreive
*
* @return mixed if param the param appears only once the method will
* return 1 if the parameter doesn't take a value. The specified value
* for that param will returned if it does take value.
*
* If many occurence of the param appear the number of occurences will
* be returned for params that do not take values. An array of values
* will be returned for the parameters that do take values.
*
* If the parameter is not present null if it takes a value and false if
* it's not present and doesn't allow values
*/
public function getParam($name)
{
if (!$this->parsed) {
$this->parseParams();
} //if
 
$longName = strlen($name) === 1 ? $this->definitions->getLongName($name) : $name;
if (isset($this->params[$longName])) {
return $this->params[$longName];
} else {
if ($this->definitions->allowsValue($longName)) {
return null;
} else {
return false;
} //if
} //if
 
} // getParam()
 
/**
* retreive the program name
*
* @author Patrick Forget <patforg at webtrendi.com>
*/
public function getProgramName()
{
if (!$this->parsed) {
$this->parseParams();
} //if
 
return $this->programName;
} // getProgramName()
/**
* retreive the trailing values
*
* @author Patrick Forget <patforg at webtrendi.com>
*/
public function getTrailingValues()
{
if (!$this->parsed) {
$this->parseParams();
} //if
 
return $this->trailingValues;
} // getTrailingValues()
 
/**
* extracts params from arguments
*
* @author Patrick Forget <patforg at webtrendi.com>
*/
protected function parseParams()
{
 
$argumentStack = $this->arguments;
 
$expectingValue = false;
$currentLongName = null;
$currentValue = null;
$trailingValues = "";
$endOfDashedArguments = false;
$addParam = false;
$argumentsLeft = sizeof($argumentStack);
$multiShortParams = array();
 
$this->programName = array_shift($argumentStack); // remove first argument which is the program name
 
while ($currentArgument = array_shift($argumentStack)) {
$argumentsLeft--;
$currentArgumentLength = strlen($currentArgument);
 
// arguments that don't start with a dash
if (substr($currentArgument, 0, 1) !== '-') {
if ($expectingValue) {
$currentValue = $currentArgument;
$addParam = true;
} else {
$trailingValues .= " ". $currentArgument;
$endOfDashedArguments = true;
} //if
 
// double dash detected
} elseif (substr($currentArgument, 1, 1) === '-') {
if ($expectingValue) {
throw new \UnexpectedValueException("Parameter {$currentLongName} expects a values");
} //if
 
/* stop parsing arguments if double dash
only param is encountered */
if ($currentArgumentLength == 2) {
if ($trailingValues !== "") {
throw new \UnexpectedValueException("Trailing values must appear after double dash");
} //if
 
$trailingValues = " ". implode(" ", $argumentStack);
$argumentStack = array();
$endOfDashedArguments = true;
break;
} //if
 
$longNameParts = explode("=", substr($currentArgument, 2), 2);
 
$currentLongName = $longNameParts[0];
 
if (sizeof($longNameParts) > 1) {
$currentValue = $longNameParts[1];
$addParam = true;
} elseif ($this->definitions->allowsValue($currentLongName)) {
$expectingValue = true;
} else {
$addParam = true;
} //if
 
// single dash
} else {
if ($expectingValue) {
throw new \UnexpectedValueException("Parameter {$currentLongName} expects a values");
} //if
 
$shortNameParts = explode("=", substr($currentArgument, 1), 2);
 
$shortName = $shortNameParts[0];
 
if (strlen($shortName) <= 1) {
$currentLongName = $this->definitions->getLongName($shortName);
 
if ($currentLongName === null) {
throw new \InvalidArgumentException("Unable to find name with ".
"provided parameter ({$shortName})");
} //if
 
if (sizeof($shortNameParts) > 1) {
$currentValue = $shortNameParts[1];
$addParam = true;
} elseif ($this->definitions->allowsValue($currentLongName)) {
$expectingValue = true;
} else {
$addParam = true;
} //if
 
} else {
$multiShortParams = str_split($shortName);
 
/* process the last one (which is the only one that can have a value) */
$lastParam = array_pop($multiShortParams);
$currentLongName = $this->definitions->getLongName($lastParam);
if (sizeof($shortNameParts) > 1) {
$currentValue = $shortNameParts[1];
$addParam = true;
} elseif ($this->definitions->allowsValue($lastParam)) {
$expectingValue = true;
} else {
$addParam = true;
} //if
 
} //if
} //if
 
if ($addParam) {
if ($endOfDashedArguments) {
throw new \UnexpectedValueException("Unexpected argument after undashed values");
} //if
 
/* Not sure how this could happen */
// @codeCoverageIgnoreStart
if ($currentLongName === false || $currentLongName === null) {
throw new \UnexpectedValueException("Missing argument name");
} //if
// @codeCoverageIgnoreEnd
 
if (!$this->definitions->paramExists($currentLongName)) {
throw new \InvalidArgumentException("Invalid argument name");
} //if
 
$allowsMultiple = $this->definitions->allowsMultiple($currentLongName);
$allowsValue = $this->definitions->allowsValue($currentLongName);
 
if (isset($this->params[$currentLongName]) && !$allowsMultiple) {
throw new \UnexpectedValueException("Multiple instace of parameter {$currentLongName} not allowed");
} //if
 
if ($allowsValue) {
/* Missing values should always be detected before addParam is true */
// @codeCoverageIgnoreStart
if ($currentValue === null) {
throw new \UnexpectedValueException("Parameter {$currentLongName} expects a values");
} //if
// @codeCoverageIgnoreEnd
 
} elseif ($currentValue !== null) {
throw new \UnexpectedValueException("Parameter {$currentLongName} does not accept values");
 
} else {
$currentValue = true;
} //if
 
if ($allowsMultiple) {
if ($allowsValue) {
if (!isset($this->params[$currentLongName])) {
$this->params[$currentLongName] = array();
} //if
 
$this->params[$currentLongName][] = $currentValue;
 
} else {
if (!isset($this->params[$currentLongName])) {
$this->params[$currentLongName] = 0;
} //if
 
$this->params[$currentLongName]++;
 
} //if
 
} else {
$this->params[$currentLongName] = $currentValue;
} //if
 
foreach ($multiShortParams as $shortName) {
$argumentStack[] = "-{$shortName}";
$argumentsLeft++;
} //foreach
 
/* reset stuff for next param */
$expectingValue = false;
$currentLongName = null;
$currentValue = null;
$addParam = false;
$multiShortParams = array();
 
} //if
 
} //while
 
if ($expectingValue !== false) {
throw new \UnexpectedValueException("Parameter {$currentLongName} expects a values");
} //if
 
/* Not sure how this could happen */
// @codeCoverageIgnoreStart
if ($currentLongName !== null ||
$addParam !== false ||
$currentValue !== null ||
sizeof($multiShortParams) !== 0) {
throw new \UnexpectedValueException("Unable to process some parameters");
} //if
// @codeCoverageIgnoreEnd
 
if ($trailingValues !== "") {
$this->trailingValues = substr($trailingValues, 1); // remove extra space at the begging
} //if
 
$this->parsed = true;
} // parseParams()
}
View
390
Classes/CommandLineArgumentDefinition.php 0 → 100644
<?php
/**
* Defines list and formats of command line arguments
*
* @author Patrick Forget <patforg at webtrendi.com>
*/
 
namespace Clapp;
 
/**
* Defines list and formats of command line arguments
*
* @author Patrick Forget <patforg at webtrendi.com>
*/
class CommandLineArgumentDefinition
{
 
/**
* @var array
*/
private $definitions = array();
 
/**
* long names as keys and array of properties as values
*
* properties are as follows
* * string "shortName" one letter char to the corresponding short name
* * boolean "isMultipleAllowed" true if mutliple instances of the param are allowed
* * mixed "parameterType" false if paramters are not alloweda value,
* otherwise a string with the value "integer" or "string"
* * string "description" description of the parameter
* @var array
*/
private $longNames = array();
/**
* list of short names as keys and their long name equivalent as values
* @var array
*/
private $shortNames = array();
 
/**
* Flag if arguments have been parsed in to params
* @var boolean
*/
private $isParsed = false;
 
/**
* class constructor
*
* @author Patrick Forget <patforg at webtrendi.com>
*
* @param array $definitions contains list of allowed parameters
* the key is the long name of the parameter followed by a pipe (|)
* then a single character specifying the short name.
*
* If the parameter allows for arguments then an equal sign (=)
* follows and then the type of paramter.
*
* Allowed types are either i, int or integer for integer types
* and s, str or string for string types.
*
* If a parameter can appear more than once the last character of
* the key should be a plus character (+).
*
* The value of the entry is the definition of what the paramter
* does.
*/
public function __construct($definitions)
{
if (is_array($definitions)) {
$this->definitions = $definitions;
} //if
} // __construct()
 
/**
* checks if parameter is allowed
*
* @author Patrick Forget <patforg at webtrendi.com>
*
* @param string $name either short or long name of the parameter to check
*
* @return boolean true if definition exisits, false otherwise
*/
public function paramExists($name)
{
if (!$this->isParsed) {
$this->parseDefinitions();
} //if
 
if (strlen($name) == 1) {
return isset($this->shortNames[$name]);
} else {
return isset($this->longNames[$name]);
} //if
} // paramExists($name)
 
/**
* checks if parameter allows a value if so what type
*
* @author Patrick Forget <patforg at webtrendi.com>
*
* @param string $name either short or long name of the parameter to check
*
* @return boolean|string false doesn't allow value, The value "string" or "integer" depending which type it allows
*/
public function allowsValue($name)
{
if (!$this->isParsed) {
$this->parseDefinitions();
} //if
 
$longName = (strlen($name) == 1 ? ( isset($this->shortNames[$name]) ? $this->shortNames[$name] : '') : $name);
 
if (isset($this->longNames[$longName])) {
return $this->longNames[$longName]['parameterType'] !== false ? true : false;
} else {
return false;
} //if
} // allowsValue()
/**
* returns the type of value allowed
*
* @author Patrick Forget <patforg at webtrendi.com>
*/
public function getValueType($name)
{
if (!$this->isParsed) {
$this->parseDefinitions();
} //if
 
$longName = (strlen($name) == 1 ? ( isset($this->shortNames[$name]) ? $this->shortNames[$name] : '') : $name);
 
if (isset($this->longNames[$longName]['parameterType'])
&& $this->longNames[$longName]['parameterType'] !== false) {
return $this->longNames[$longName]['parameterType'];
} else {
return '';
} //if
} // getValueType()
 
/**
* checks if pamultiple instance of parameter are allowed
*
* @author Patrick Forget <patforg at webtrendi.com>
*
* @param string $name either short or long name of the parameter to check
*
* @return boolean false if parameter doesn't allow multiple values, true if it does
*/
public function allowsMultiple($name)
{
if (!$this->isParsed) {
$this->parseDefinitions();
} //if
 
$longName = (strlen($name) == 1 ? ( isset($this->shortNames[$name]) ? $this->shortNames[$name] : '') : $name);
 
if (isset($this->longNames[$longName])) {
return $this->longNames[$longName]['isMultipleAllowed'];
} else {
return false;
} //if
} // allowsMultiple()
 
/**
* retreive short name of a parameter using its long name
*
* @author Patrick Forget <patforg at webtrendi.com>
*
* @param string $name long name of the parameter to check
*
* @return string character of the short name or null if it doesn't exist
*/
public function getShortName($name)
{
if (!$this->isParsed) {
$this->parseDefinitions();
} //if
 
if (isset($this->longNames[$name])) {
return $this->longNames[$name]['shortName'];
} else {
return null;
} //if
} // getShortName($name)
/**
* retreive long name of a parameter using its short name
*
* @author Patrick Forget <patforg at webtrendi.com>
*
* @param string $name short name of the parameter to check
*
* @return string long name or null if it doesn't exist
*/
public function getLongName($name)
{
if (!$this->isParsed) {
$this->parseDefinitions();
} //if
 
if (isset($this->shortNames[$name])) {
return $this->shortNames[$name];
} else {
return null;
} //if
} // getLongName($name)
 
/**
* retreive description of a paramter
*
* @author Patrick Forget <patforg at webtrendi.com>
*
* @param string $name either short or long name of the parameter to check
*
* @return string description or null if it doesn't exist
*/
public function getDescription($name)
{
if (!$this->isParsed) {
$this->parseDefinitions();
} //if
 
$longName = (strlen($name) == 1 ? ( isset($this->shortNames[$name]) ? $this->shortNames[$name] : '') : $name);
 
if (isset($this->longNames[$longName])) {
return $this->longNames[$longName]['description'];
} else {
return null;
} //if
} // getDescription()
/**
* builds a usage definition based on definition of params
*
* @author Patrick Forget <patforg at webtrendi.com>
*/
public function getUsage()
{
if (!$this->isParsed) {
$this->parseDefinitions();
} //if
 
/* build list of argument names and calculate
the first column width so we can pad to
align definitions */
$firstCol = array();
$longestDef = 0;
foreach (array_keys($this->longNames) as $longName) {
ob_start();
echo "--{$longName}|-{$this->getShortName($longName)}";
 
if ($this->allowsValue($longName)) {
echo "={$this->getValueType($longName)}";
} //if
 
if ($this->allowsMultiple($longName)) {
echo "+";
} //if
 
$defLength = ob_get_length();
 
$longestDef = max($longestDef, $defLength);
 
$firstCol[$longName] = ob_get_contents();
ob_end_clean();
 
} //foreach
 
$firstColMaxWidth = $longestDef + 4;
 
ob_start();
 
foreach ($firstCol as $longName => $def) {
$currentDefLength = strlen($def);
 
$padding = str_repeat(" ", $firstColMaxWidth - $currentDefLength);
 
echo "{$def}{$padding}{$this->getDescription($longName)}", PHP_EOL;
} //foreach
 
echo PHP_EOL;
 
$usage = ob_get_contents();
ob_end_clean();
return $usage;
 
} // getUsage()
 
/**
* parses the definitions
*
* @author Patrick Forget <patforg at webtrendi.com>
*/
protected function parseDefinitions()
{
foreach ($this->definitions as $nameDef => $description) {
$nameParts = explode("|", $nameDef);
 
if (sizeof($nameParts) !== 2) {
throw new \UnexpectedValueException("Unexpected argument name definition expecting \"longName|char\"");
} //if
 
$longName = $nameParts[0];
$isMulti = false;
$parameterType = false;
 
$shortNameLength = strlen($nameParts[1]);
 
if ($shortNameLength == 1) {
$shortName = $nameParts[1];
} else {
$secondChar = substr($nameParts[1], 1, 1);
 
switch ($secondChar) {
case '=':
$shortNameParts = explode("=", $nameParts[1]);
 
$shortName = $shortNameParts[0];
$parameterTypeString = $shortNameParts[1];
 
if (substr($parameterTypeString, -1) === '+') {
$isMulti = true;
$parameterTypeString = substr($parameterTypeString, 0, -1); // remove trailing +
} //if
 
switch ($parameterTypeString) {
case 'i':
case 'int':
case 'integer':
$parameterType = 'integer';
break;
case 's':
case 'str':
case 'string':
$parameterType = 'string';
break;
default:
throw new \UnexpectedValueException("Expecting parameter type".
" to be either integer or string");
break;
} //switch
 
break;
case '+':
if ($shortNameLength > 2) {
throw new \UnexpectedValueException("Multiple flag charachter (+)".
" should be last character in definition");
} //if
 
$shortName = substr($nameParts[1], 0, 1);
$isMulti = true;
 
break;
default:
throw new \UnexpectedValueException("Expecting short name definition to be a single char");
break;
} // switch
 
} //if
 
if (isset($this->longNames[$longName])) {
throw new \UnexpectedValueException("Cannot redefine long name {$longName}");
} //if
 
if (isset($this->shortNames[$shortName])) {
throw new \UnexpectedValueException("Cannot redefine short name {$shortName}");
} //if
 
$this->longNames[$longName] = array(
'shortName' => $shortName,
'isMultipleAllowed' => $isMulti,
'parameterType' => $parameterType,
'description' => $description
);
 
$this->shortNames[$shortName] = $longName;
 
} //foreach
 
$this->isParsed = true;
} // parseDefinitions()
}
View
23
Classes/Curl.php 0 → 100644
<?php
class curl {
public function curlQuery() {
$ch = curl_init();
 
curl_setopt($ch, CURLOPT_URL, $this->url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.6) Gecko/20070725 Firefox/2.0.0.6");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, True);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
//curl_setopt($ch, CURLOPT_VERBOSE, true); // verbose mode for debugging
 
$json = curl_exec($ch);
 
curl_close($ch);
 
$array = json_decode($json, true);
return $array;
}
}
?>
View
402
GoStats.php 0 → 100755
#!/usr/bin/php
<?php
error_reporting(0);
 
/***
* Configuration Settings - CHANGE THESE
*/
$url = ""; // URL of GoPhish listner e.g. http://www.site.com:8080/
$key = ""; // GoPhish API key
$pwd = "/opt/Pwdlyser/"; // /directory/containing/pwdlyser
$geoip = true; // use freegeoip.net on IP addresses? set to false to disable this.
 
/***
* Main program - Don't edit below
*/
echo "╔═╗┌─┐╔═╗┌┬┐┌─┐┌┬┐┌─┐\n║ ╦│ │╚═╗ │ ├─┤ │ └─┐ v1.0\n╚═╝└─┘╚═╝ ┴ ┴ ┴ ┴ └─┘\n";
 
foreach (glob("Classes/*.php") as $filename)
include $filename;
 
$definitions = new \Clapp\CommandLineArgumentDefinition(
array(
"help|h" => "Shows help message",
"list|l" => "List campaigns and their ID's",
"campaign|c=i" => "Get campaign by id",
"dump|d=s" => "Dump user:pass list to </path/to/file.txt>",
"training|t=s" => "Dump list of users requiring training </path/to/file.txt>",
"all|a" => "All of the below options",
"ips|i" => "Top 10 IP's",
"useragent|u" => "Top 10 user agents",
"attempts|m" => "Top 10 attempts to log in",
"active|o" => "Active times",
"speed|e" => "Clickthrough speed",
"stats|s" => "Victim statistics",
"pass|p" => "Password analysis with pwdlyser",
)
);
 
$filter = new \Clapp\CommandArgumentFilter($definitions, $argv);
 
if ($filter->getParam('h') === true || $argc < 2) {
fwrite(STDERR, $definitions->getUsage());
exit(0);
}
 
/* Get list of campaigns */
if ($filter->getParam("list") !== false) {
echo "[+] Getting data from server\n";
$curl = new curl();
$curl->url = "$url/api/campaigns/?api_key=$key";
$list = $curl->curlQuery();
 
if(isset($list->message) && $list->message == "Invalid API Key"){
echo "[!] Invalid API key\n";
exit(0);
}else{
echo "[id] -campaign name-\n";
foreach($list as $id)
echo "[".$id['id']."] ".$id['name']."\n";
}
exit(0);
}
 
/* Get campaign data */
$campid = $filter->getParam('c');
if ($campid == null || !is_numeric($campid)) {
echo "[!] Campaign ID not set\nn";
exit(0);
}else{
echo "[+] Getting data from server\n";
$curl = new curl();
$curl->url = "$url/api/campaigns/$campid?api_key=$key";
$list = $curl->curlQuery();
if(isset($list->message) && $list->message == "Invalid API Key"){
echo "[!] Invalid API key\n";
exit(0);
}else{
/* all data got correctly time to do stuff! */
echo "[$campid] ".$list['name']."\n";
echo "\n--- Notable times ---\n";
 
if(isset($list['launch_date']) && $list['launch_date'] <> ""){
$time = date('d-m-Y H:i', $datetime = strtotime(substr($list['launch_date'], 0, 10) . ' ' . substr($list['launch_date'], 11, 8 )));
echo "Campaign launched: $time\n";
}
 
foreach($list['timeline'] as $record){
if($record['message'] == "Email Sent"){
$time = date('d-m-Y H:i', $datetime = strtotime(substr($record['time'], 0, 10) . ' ' . substr($record['time'], 11, 8 )));
echo "First email sent: $time\n";
break;
}
}
foreach($list['timeline'] as $record){
if($record['message'] == "Email Sent"){
$time = date('d-m-Y H:i', $datetime = strtotime(substr($record['time'], 0, 10) . ' ' . substr($record['time'], 11, 8 )));
}
}
echo "Last email sent:: $time\n";
foreach($list['timeline'] as $record){
if($record['message'] == "Clicked Link"){
$time = date('d-m-Y H:i', $datetime = strtotime(substr($record['time'], 0, 10) . ' ' . substr($record['time'], 11, 8 )));
echo "First email opened: $time\n";
break;
}
}
foreach($list['timeline'] as $record){
if($record['message'] == "Clicked Link"){
$time = date('d-m-Y H:i', $datetime = strtotime(substr($record['time'], 0, 10) . ' ' . substr($record['time'], 11, 8 )));
echo "First page view: $time\n";
break;
}
}
foreach($list['timeline'] as $record){
if($record['message'] == "Submitted Data"){
$time = date('d-m-Y H:i', $datetime = strtotime(substr($record['time'], 0, 10) . ' ' . substr($record['time'], 11, 8 )));
echo "First credentials submitted: $time\n";
break;
}
}
if(isset($list['completed_date']) && $list['completed_date'] <> ""){
$time = date('d-m-Y H:i', $datetime = strtotime(substr($list['completed_date'], 0, 10) . ' ' . substr($list['completed_date'], 11, 8 )));
echo "Campaign finished: $time\n";
}
}
}
 
/* Top 10 IP's */
if ($filter->getParam("ips") !== false || $filter->getParam("all") !== false) {
$ips = array();
foreach($list['timeline'] as $item){
if($item['details'] <> ""){
$details = json_decode($item['details'], true);
if($details['browser']['address'] !== "unknown")
$ips[] = (string)$details['browser']['address'];
}
}
$ips = array_count_values($ips);
arsort($ips);
$ips = array_slice($ips,0,10,true);
echo "\n--- Top 10 IP's ---\n";
foreach($ips as $ip=>$no){
$geoip_details = "";
if($geoip == true){
$geojson = file_get_contents("http://freegeoip.net/json/$ip");
$geodetails = json_decode($geojson, true);
$geoip_details = "- ".$geodetails['country_name'].", ".$geodetails['city'];
}
echo "[$no] $ip $geoip_details\n";
}
}
 
/* Top 10 user agent's */
if ($filter->getParam("useragent") !== false || $filter->getParam("all") !== false) {
$agents = array();
foreach($list['timeline'] as $item){
if($item['details'] <> "" && $item['message'] == "Clicked Link"){ // only people who visited site, not email user agent
$details = json_decode($item['details'], true);
if($details['browser']['user-agent'] !== "unknown" && $details['browser']['user-agent'] !== "")
$agents[] = (string)$details['browser']['user-agent'];
}
}
$agents = array_count_values($agents);
arsort($agents);
$agents = array_slice($agents,0,10,true);
echo "\n--- Top 10 User Agents ---\n";
foreach($agents as $ua=>$no){
echo "[$no] $ua\n";
}
}
 
/* Top 10 attempts to log in */
if($filter->getParam("attempts") !== false || $filter->getParam("all") !== false) {
$userids = array();
foreach($list['results'] as $item){
$userids[$item['id']] = $item['email'];
}
$attemptrids = array();
foreach($list['timeline'] as $item){
if($item['details'] <> ""){
$details = json_decode($item['details'], true);
if(isset($details['payload']['password'][0]) && $details['payload']['password'][0] <> ""){
$attemptrids[$details['payload']['rid'][0]] += 1;
}
}
}
arsort($attemptrids);
$attemptrids = array_slice($attemptrids,0,10,true);
echo "\n--- Top 10 Login Attempts ---\n";
foreach($attemptrids as $id=>$amount){
$newemail= preg_replace('/(?:^|.@).\K|.\.[^@]*$(*SKIP)(*F)|.(?=.*?\.)/', '*', $userids[$id]);
echo "[$amount] $newemail\n";
}
}
 
/* Active times */
if($filter->getParam("active") !== false || $filter->getParam("all") !== false) {
$active_count = array();
$active_percent = array();
$total = 0;
echo "\n--- Active times (hour, actions & percent) ---\n";
foreach($list['timeline'] as $item){
if($item['message'] != "Campaign Created" && $item['message'] != "Email Sent" ){
$hour = (int)substr($item['time'], 11, 2);
$active_count[$hour]++;
$total++;
}
 
}
foreach($active_count as $id => $count) // populate percentages
$active_percent[$id] = ($count / $total) * 100;
 
for($i = 0; $i <= 12; $i++){
$iDsp = str_pad($i, 2, " ", STR_PAD_LEFT);
$j = $i+12;
$user1 = str_pad($active_count[$i], 4, " ", STR_PAD_LEFT);
$user2 = str_pad($active_count[$j], 4, " ", STR_PAD_LEFT);
$percent1 = number_format($active_percent[$i], 2, '.', '');
$percent1 = str_pad($percent1, 5, " ", STR_PAD_LEFT);
$percent2 = number_format($active_percent[$j], 2, '.', '');
$percent2 = str_pad($percent2, 5, " ", STR_PAD_LEFT);
echo "$iDsp - $user1 = $percent1% | $j - $user2 = $percent2% \n";
}
}
 
/* Clickthrough speed */
if ($filter->getParam("speed") !== false || $filter->getParam("all") !== false) {
$speed_opened = array();
$speed_visited = array();
$speed_offset = array();
echo "\n--- Clickthrough Speed ---\n";
foreach($list['timeline'] as $item){
if($item['message'] == "Email Opened"){
$details = json_decode($item['details'], true);
$check_rid = $details['payload']['rid'][0];
 
$current_time = strtotime(substr($item['time'], 0, 10) . ' ' . substr($item['time'], 11, 8 ));
$existing_time = strtotime(substr($speed_opened[$check_rid], 0, 10) . ' ' . substr($speed_opened[$check_rid], 11, 8 ));
 
if(!isset($speed_opened[$check_rid]) || $existing_time > $current_time)
$speed_opened[$check_rid] = $item['time'];
}
if($item['message'] == "Clicked Link"){
$details = json_decode($item['details'], true);
$check_rid = $details['payload']['rid'][0];
if(!isset($speed_visited[$check_rid]))
$speed_visited[$check_rid] = $item['time'];
}
}
foreach($speed_opened as $id=>$val){ // remove all the ones that didn't visit site
if(!isset($speed_visited[$id]))
unset($speed_opened[$id]);
}
foreach($speed_visited as $id=>$val){ // remove all the ones that didn't load email tracking image
if(!isset($speed_opened[$id]))
unset($speed_visited[$id]);
}
foreach($speed_opened as $id=>$val){ //calculate speed between reading email and clicking link
$date_opened = substr($val, 0, 10);
$time_opened = substr($val, 11, 8 );
$time_opened_stamp = strtotime($date_opened." ".$time_opened);
 
$date_visited = substr($speed_visited[$id], 0, 10);
$time_visited = substr($speed_visited[$id], 11, 8 );
$time_visited_stamp = strtotime($date_visited." ".$time_visited);
 
$diff = $time_visited_stamp - $time_opened_stamp;
if($diff > 0)
$speed_offset[$id] = $diff;
}
 
unset($speed_opened); // check me out doing memory management and cleaning up! :D
unset($speed_visited);
 
$quickest = min($speed_offset);
echo "Quickest click: $quickest sec\n";
$longest = max($speed_offset);
$longest = floor(($longest / 60) % 60);
echo "Longest click: $longest min\n";
$sec_5 = array_reduce($speed_offset, function ($a, $b){
return ($b <= 5) ? ++$a : $a; });
echo "Users clicked < 5 sec: $sec_5 \n";
$sec_30 = array_reduce($speed_offset, function ($a, $b){
return ($b <= 30) ? ++$a : $a; });
echo "Users clicked < 30 sec: $sec_30 \n";
$sec_60 = array_reduce($speed_offset, function ($a, $b){
return ($b <= 60) ? ++$a : $a; });
echo "Users clicked < 1 min: $sec_60 \n";
}
 
/* Victim statistics */
if ($filter->getParam("stats") !== false || $filter->getParam("all") !== false) {
$status = array();
foreach($list['results'] as $item){
if($item['status'] <> ""){
$status[] = $item['status'];
}
}
echo "\n--- Victim Statistics ---\n";
$statusall = count($status);
$counts = array_count_values($status);
echo "Targets: ".$statusall."\n";
$openedpercent = ($counts['Email Opened'] / $statusall) * 100;
echo "Email opened: ".$counts['Email Opened']." (".round($openedpercent, 2)."%)\n";
$linkpercent = ($counts['Clicked Link'] / $statusall) * 100;
echo "Visited link: ".$counts['Clicked Link']." (".round($linkpercent, 2)."%)\n";
$subpercent = ($counts['Submitted Data'] / $statusall) * 100;
echo "Submitted data: ".$counts['Submitted Data']." (".round($subpercent, 2)."%)\n";
foreach($list['timeline'] as $item){
if($item['details'] <> ""){
$details = json_decode($item['details'], true);
if($details['payload']['password'][0] <> "")
$totalLoginAttempts++;
}
}
echo "Total login attempts: $totalLoginAttempts\n";
}
 
/* Pwdlyzer */
if ($filter->getParam("pass") !== false || $filter->getParam("all") !== false) {
$username = array();
$password = array();
echo "\n--- Password Statistics ---\n";
foreach($list['timeline'] as $item){
if($item['details'] <> ""){
$details = json_decode($item['details'], true);
if($details['payload']['password'][0] <> ""){
$username[] = $details['payload']['username'][0];
$password[] = $details['payload']['password'][0];
}
}
}
$tmpfname = tempnam("/tmp", "GoStats-");
$pwdfname = tempnam("/tmp", "GoStats-");
$handle = fopen($tmpfname, "w");
foreach($username as $id=>$user){
fwrite($handle, "$user:".$password[$id]."\n");
}
fclose($handle);
echo "[+] Launching pwdlyzer\n";
exec("cd $pwd && ./pwdlyser.py -p $tmpfname --all > $pwdfname");
unlink($tmpfname);
echo "[+] pwdlyzer results at: $pwdfname\n";
}
 
/* dump username:password list to file */
$dumpfile = $filter->getParam('dump');
if(file_exists($dumpfile)){
echo "[!] File already exists ($dumpfile)\n";
exit(0);
}
if(!file_exists($dumpfile) && isset($dumpfile)){
$username = array();
$password = array();
echo "\n--- Dumping username:password to file ---\n";
foreach($list['timeline'] as $item){
if($item['details'] <> ""){
$details = json_decode($item['details'], true);
if($details['payload']['password'][0] <> ""){
$username[] = $details['payload']['username'][0];
$password[] = $details['payload']['password'][0];
}
}
}
$handle = fopen($dumpfile, "w");
foreach($username as $id=>$user){
fwrite($handle, "$user:".$password[$id]."\n");
}
fclose($handle);
echo "[+] File created: $dumpfile\n";
}
 
/* dump list of users requiring training */
$dumpfile2 = $filter->getParam('training');
if(file_exists($dumpfile2)){
echo "[!] File already exists ($dumpfile)\n";
exit(0);
}
if(!file_exists($dumpfile2) && isset($dumpfile2)){
$tusername = array();
$temail = array();
$tstatus = array();
echo "\n--- Dumping list of users requiring training ---\n";
foreach($list['results'] as $item){
if($item['status'] == "Submitted Data" || $item['status'] == "Clicked Link"){
$tusername[] = $item['first_name']." ".$item['last_name'];
$temail[] = $item['email'];
$tstatus[] = $item['status'];
}
}
$handle = fopen($dumpfile2, "w");
foreach($tusername as $id=>$user){
fwrite($handle, "$user, ".$temail[$id].", ".$tstatus[$id]."\n");
}
fclose($handle);
echo "[+] File created: $dumpfile2\n";
}
 
?>
View
260
README.md
GoStats
===============
 
Get statistics from GoPhish campaign
Get statistics from GoPhish campaigns
 
Requirements
===============
Pwdlyser - [https://github.com/ins1gn1a/Pwdlyser](https://github.com/ins1gn1a/Pwdlyser)
 
Installation and Usage
===============
git clone the repo
 
chmod +x ./GoStats.php
 
Modify **GoStats.php** to contain your gophish URL, API key and path to pwdlyser
 
./GoStats.php
 
Example Output
===============
 
root[/opt/GoStats]: ./GoStats.php
╔═╗┌─┐╔═╗┌┬┐┌─┐┌┬┐┌─┐
╦│ │╚═╗ ├─┤ └─┐ v1.0
╚═╝└─┘╚═╝ └─┘
--help|-h Shows help message
--list|-l List campaigns and their ID's
--campaign|-c=integer Get campaign by id
--dump|-d=string Dump user:pass list to </path/to/file.txt>
--training|-t=string Dump list of users requiring training </path/to/file.txt>
--all|-a All of the below options
--ips|-i Top 10 IP's
--useragent|-u Top 10 user agents
--attempts|-m Top 10 attempts to log in
--active|-o Active times
--speed|-e Clickthrough speed
--stats|-s Victim statistics
--pass|-p Password analysis with pwdlyser
 
root[/opt/GoStats]: ./GoStats.php -l
╔═╗┌─┐╔═╗┌┬┐┌─┐┌┬┐┌─┐
╦│ │╚═╗ ├─┤ └─┐ v1.0
╚═╝└─┘╚═╝ └─┘
[+] Getting data from server
[id] -campaign name-
[33] Campaign_01
[60] Campaign_02
 
root[/opt/GoStats]: ./GoStats.php -c 60 -a
╔═╗┌─┐╔═╗┌┬┐┌─┐┌┬┐┌─┐
╦│ │╚═╗ ├─┤ └─┐ v1.0
╚═╝└─┘╚═╝ └─┘
[+] Getting data from server
[60] Campaign_02
 
[+] Notable times
Campaign launched: 16-10-2017 09:28
First email sent: 16-10-2017 10:20
Last email sent: 16-10-2017 10:25
First email opened: 16-10-2017 10:28
First page view: 16-10-2017 10:28
First credentials submitted: 16-10-2017 10:29
Campaign finished: 21-10-2017 10:09
 
[+] Top 10 IPs
[177] 130.***.**.50 - United Kingdom, London
[96] 212.***.**.69 - France,
[41] 86.**.**.2 - United Kingdom, Edgware
[32] 193.***.**.190 - United Kingdom,
[28] 205.***.**.189 - United States, Chesterfield
[19] 86.***.***.47 - United Kingdom, Gillingham
[15] 82.**.**.34 - United Kingdom, Bradford-on-Avon
[14] 24.**.***.62 - United States, New York
[9] 66.**.**.130 - United States, West Jordan
[7] 2.**.**.183 - United Kingdom, London
 
[+] Top 10 User Agents
[60] WebClient/1.0
[32] Mozilla/4.0 (redacted details)
[30] Mozilla/4.0 (redacted details)
[29] Mozilla/4.0 (redacted details)
[26] Mozilla/4.0 (redacted details)
[26] Mozilla/4.0 (redacted details)
[22] Mozilla/4.0 (redacted details)
[20] Mozilla/5.0 (redacted details) Chrome/61.0.3163.100 Safari/537.36
[18] Mozilla/4.0 (redacted details)
[16] Mozilla/4.0 (redacted details) AppleWebKit/603.3.8
 
[+] Top 10 Login Attempts
[19] b**********s@w***n.com
[12] b***************o@w***n.com
[8] n*************e@g*********e.com
[7] c****a@w***n.com
[7] s***************d@g*********e.com
[7] e**********r@w***n.com
[6] c*********m@w***n.com
[5] d******j@w***n.com
[3] j********d@a********l.com
[3] c***********n@a********l.com
 
[+] Active times (hour, actions & percent)
0 - = 0.00% | 12 - 28 = 5.47%
1 - = 0.00% | 13 - 22 = 4.30%
2 - = 0.00% | 14 - 31 = 6.05%
3 - = 0.00% | 15 - 25 = 4.88%
4 - = 0.00% | 16 - 53 = 10.35%
5 - 1 = 0.20% | 17 - 2 = 0.39%
6 - 5 = 0.98% | 18 - 18 = 3.52%
7 - 9 = 1.76% | 19 - 8 = 1.56%
8 - 21 = 4.10% | 20 - = 0.00%
9 - 134 = 26.17% | 21 - 4 = 0.78%
10 - 125 = 24.41% | 22 - 1 = 0.20%
11 - 24 = 4.69% | 23 - 1 = 0.20%
12 - 28 = 5.47% | 24 - = 0.00%
 
[+] Clickthrough Speed
Quickest click: 2 sec
Longest click: 18 min
Users clicked < 5 sec: 7
Users clicked < 30 sec: 11
Users clicked < 1 min: 16
 
[+] Victim Statistics
Targets: 136
Email opened: 15 (11.03%)
Visited link: 20 (14.71%)
Submitted data: 20 (14.71%)
Total login attempts: 96
 
[+] Password Statistics
[+] Launching pwdlyzer
[!] pwdlyzer results at: /tmp/GoStats-VpGHth
Buy Me A Coffee