Update Evil Portal to 3.1 (#32)

pull/33/head
Josh 2018-07-21 20:45:22 -04:00 committed by Sebastian Kinne
parent ac9df1c5bb
commit af3ed0d98c
8 changed files with 260 additions and 85 deletions

View File

@ -43,6 +43,10 @@ class EvilPortal extends Module
$this->response = $this->getFileOrDirectoryContents($this->request->filePath);
break;
case 'download':
$this->response = $this->download($this->request->filePath);
break;
case 'listAvailablePortals':
$this->response = $this->getListOfPortals();
break;
@ -138,7 +142,7 @@ class EvilPortal extends Module
$contents = array(
"name" => basename($file),
"path" => $file,
"size" => filesize($file),
"size" => $this->readableFileSize($file),
"fileContent" => file_get_contents($file)
);
$success = true;
@ -153,7 +157,7 @@ class EvilPortal extends Module
$obj = array("name" => $object, "directory" => is_dir("{$file}/{$object}"),
"path" => realpath("{$file}/{$object}"),
"permissions" => substr(sprintf('%o', fileperms("{$file}/{$object}")), -4),
"size" => filesize("{$file}/{$object}"),
"size" => $this->readableFileSize("{$file}/{$object}"),
"deletable" => $this->isFileDeletable($object));
array_push($contents, $obj);
}
@ -202,6 +206,20 @@ class EvilPortal extends Module
return array("success" => $success, "message" => $message);
}
/**
* Download a file
* @param: The path to the file to download
* @return array : array
*/
private function download($filePath)
{
if (file_exists($filePath)) {
return array("success" => true, "message" => null, "download" => $this->downloadFile($filePath));
} else {
return array("success" => false, "message" => "File does not exist", "download" => null);
}
}
/**
* Get a list of portals found on internal and sd storage.
*/
@ -226,6 +244,7 @@ class EvilPortal extends Module
$portal = array(
"title" => $object,
"portalType" => $this->getValueFromJSONFile(array("type"), "{$storageLocation}{$object}/{$object}.ep")["type"],
"size" => $this->readableFileSize("{$storageLocation}{$object}"),
"location" => "{$storageLocation}{$object}",
"storage" => $medium,
"active" => (file_exists("/www/{$object}.ep"))
@ -547,7 +566,7 @@ class EvilPortal extends Module
$allowedClients = file_get_contents($this->ALLOWED_FILE);
file_put_contents($this->CLIENTS_FILE, $allowedClients);
// // Configure other rules
// Configure other rules
exec("iptables -A INPUT -s 172.16.42.0/24 -j DROP");
exec("iptables -A OUTPUT -s 172.16.42.0/24 -j DROP");
exec("iptables -A INPUT -s 172.16.42.0/24 -p udp --dport 53 -j ACCEPT");
@ -719,4 +738,25 @@ class EvilPortal extends Module
return $values;
}
/**
* Get the size of a file and add a unit to the end of it.
* @param $file: The file to get size of
* @return string: File size plus unit. Exp: 3.14M
*/
private function readableFileSize($file) {
$size = filesize($file);
if ($size == null)
return "0 Bytes";
if ($size < 1024) {
return "{$size} Bytes";
} else if ($size >= 1024 && $size < 1024*1024) {
return round($size / 1024, 2) . "K";
} else if ($size >= 1024*1024) {
return round($size / (1024*1024), 2) . "M";
}
return "{$size} Bytes";
}
}

View File

@ -29,11 +29,37 @@ abstract class Portal
* Run a command in the background and don't wait for it to finish.
* @param $command: The command to run
*/
protected function execBackground($command)
protected final function execBackground($command)
{
exec("echo \"{$command}\" | at now");
}
/**
* Send notifications to the web UI.
* @param $message: The notification message
*/
protected final function notify($message)
{
$this->execBackground("notify {$message}");
}
/**
* Write a log to the portals log file.
* These logs can be retrieved from the web UI for .logs in the portals directory.
* The log file is automatically appended to so there is no reason to add new line characters to your message.
* @param $message: The message to write to the log file.
*/
protected final function writeLog($message)
{
try {
$reflector = new \ReflectionClass(get_class($this));
$logPath = dirname($reflector->getFileName());
file_put_contents("{$logPath}/.logs", "{$message}\n", FILE_APPEND);
} catch (\ReflectionException $e) {
// do nothing.
}
}
/**
* Creates an iptables rule allowing the client to access the internet and writes them to the authorized clients.
* Override this method to add other authorization steps validation.
@ -44,7 +70,7 @@ abstract class Portal
{
if (!$this->isClientAuthorized($clientIP)) {
exec("iptables -t nat -I PREROUTING -s {$clientIP} -j ACCEPT");
// exec("{$this->BASE_EP_COMMAND} add {$clientIP}");
// exec("{$this->BASE_EP_COMMAND} add {$clientIP}");
file_put_contents($this->AUTHORIZED_CLIENTS_FILE, "{$clientIP}\n", FILE_APPEND);
}
return true;
@ -82,7 +108,7 @@ abstract class Portal
*/
protected function onSuccess()
{
$this->execBackground("notify New client authorized through EvilPortal!");
$this->notify("New client authorized through EvilPortal!");
}
/**

View File

@ -9,7 +9,6 @@ class MyPortal extends Portal
// Call parent to handle basic authorization first
parent::handleAuthorization();
}
/**

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -33,10 +33,14 @@ registerController("EvilPortalController", ['$api', '$scope', function ($api, $s
$scope.portalDeleteValidation = null;
// the portal workshop
$scope.workshop = {"portal": {}, "dirContents": null, "inRoot": true, "rootDirectory": null, "editFile": {"path": null, "isNewFile": true},
"onEnable": null, "onDisable": null, "concreteTargetedRules": null, "workingTargetedRules": null, "deleteFile": null
$scope.workshop = {"portal": {}, "dirContents": null, "inRoot": true, "rootDirectory": null,
"editFile": {"path": null, "isNewFile": true}, "onEnable": null, "onDisable": null,
"concreteTargetedRules": null, "workingTargetedRules": null, "deleteFile": null
};
// active log file
$scope.activeLog = {"title": null, "path": null, "contents": null};
/**
* Reset the workshop object to a blank slate with initial values.
*/
@ -396,6 +400,16 @@ registerController("EvilPortalController", ['$api', '$scope', function ($api, $s
});
};
/**
* Load logs for a given portal
* @param portal: The portal to load logs for
*/
$scope.loadPortalLog = function(portal) {
var basePath = (portal.storage === "sd") ? "/sd/portals/" : "/root/portals/";
var logPath = basePath + portal.title + "/.logs";
$scope.loadLog(logPath);
}
/**
* check if a given object is empty.
* @param obj: The object to check
@ -405,6 +419,51 @@ registerController("EvilPortalController", ['$api', '$scope', function ($api, $s
return (Object.keys(obj).length === 0);
};
/**
* Loads a file from filePath and puts the data into the activeLog object.
*/
$scope.loadLog = function(filePath) {
getFileOrDirectoryContent(filePath, function(response) {
console.log(response);
if (!response.success) {
$scope.activeLog = {
"title": "Unknown",
"path": null,
"contents": null,
"size": "0 Bytes"
};
return;
}
$scope.activeLog = {
"title": response.content.name,
"path": response.content.path,
"contents": response.content.fileContent,
"size": response.content.size
};
});
};
/**
* Writes an empty string to whatever file path is in $scope.activeLog.path
* On successful write, $scope.activeLog.contents is set to null.
*
* If $scope.activeLog.path is null, the request if not made.
*
*/
$scope.clearLog = function() {
if ($scope.activeLog.path == null)
return;
writeToFile($scope.activeLog.path, '', false, function(response) {
if (!response.success) {
$scope.sendMessage("Error Clearing Log", response.message);
return;
}
$scope.activeLog.contents = response.content;
$scope.activeLog.size = "0 Bytes";
});
}
/**
* Load the contents of a given file.
* @param filePath: The path to the file to load
@ -431,6 +490,7 @@ registerController("EvilPortalController", ['$api', '$scope', function ($api, $s
var basePath = ($scope.workshop.portal.storage === "sd") ? "/sd/portals/" : "/root/portals/";
$scope.workshop.editFile.path = basePath + $scope.workshop.portal.title + "/";
$scope.workshop.editFile.isNewFile = true;
$scope.workshop.editFile.size = "0 Bytes";
};
/**
@ -442,7 +502,6 @@ registerController("EvilPortalController", ['$api', '$scope', function ($api, $s
if (!editFile.path.includes(editFile.name))
editFile.path = editFile.path + editFile.name;
console.log(editFile.path);
writeToFile(editFile.path, editFile.content, false, function(response) {
if (!response.success)
$scope.sendMessage("Error write to file " + editFile.name, response.message);
@ -463,6 +522,20 @@ registerController("EvilPortalController", ['$api', '$scope', function ($api, $s
});
};
$scope.download = function(filePath) {
$api.request({
module: "EvilPortal",
action: "download",
filePath: filePath
}, function (response) {
if (!response.success) {
$scope.sendMessage("Error", response.message);
return;
}
window.location = "/api/?download=" + response.download;
})
};
/**
* Load either the white list or the authorized clients (access) list
* @param listName: The name of the list: whiteList or accessList (authorized clients)
@ -654,6 +727,7 @@ registerController("EvilPortalController", ['$api', '$scope', function ($api, $s
response.portals.forEach(function(item, index) {
$scope.portals.unshift({
title: item.title,
size: item.size,
storage: item.storage,
active: item.active,
type: item.portalType,

View File

@ -84,36 +84,40 @@
<div class="table-responsive">
<table class="table table-striped" align="center">
<thead>
<th>Portal Name</th>
<th>Portal Type</th>
<th>Location</th>
<th ng-show="evilPortal.sdAvailable">Move</th>
<th>Activate</th>
<th>Delete</th>
<th>Portal Name</th>
<th>Portal Type</th>
<th>Location</th>
<th ng-show="evilPortal.sdAvailable">Move To</th>
<th>Logs</th>
<th>Activate</th>
<th>Delete</th>
</thead>
<tbody>
<tr ng-repeat="portal in portals">
<!-- Open portal work shop -->
<td><a href="javascript:;" ng-click="loadPortal(portal)" style="text-transform: capitalize"><b>{{ portal.title }}</b></a></td>
<!-- Portal type -->
<td class="text-muted" style="text-transform: capitalize"><i>{{ portal.type }}</i></td>
<!-- What storage device the portal is on -->
<td class="text-muted" style="text-transform: capitalize"><i>{{ portal.storage }}</i></td>
<!-- change storage -->
<td ng-show="evilPortal.sdAvailable">
<a ng-show="(portal.storage == 'internal') && !portal.active" href="javascript:;" ng-click="movePortal(portal, 'sd');">Move to SD</a>
<a ng-show="(portal.storage == 'sd') && !portal.active" href="javascript:;" ng-click="movePortal(portal, 'internal');">Move to Internal</a>
</td>
<!-- activate/deactivate -->
<td>
<a ng-hide="portal.active" href="javascript:;" ng-click="activatePortal(portal)">Activate</a>
<a ng-show="portal.active" href="javascript:;" ng-click="deactivatePortal(portal)">Deactivate</a>
</td>
<!-- delete portal button -->
<td>
<a ng-hide="portal.active" data-toggle="modal" data-target="#deleteModal" ng-click="deletePortal(false, portal)">Delete</a>
</td>
</tr>
<tr ng-repeat="portal in portals">
<!-- Open portal work shop -->
<td><a href="javascript:;" ng-click="loadPortal(portal)" style="text-transform: capitalize"><b>{{ portal.title }}</b></a></td>
<!-- Portal type -->
<td class="text-muted" style="text-transform: capitalize">{{ portal.type }}</td>
<!-- What storage device the portal is on -->
<td class="text-muted" style="text-transform: capitalize">{{ portal.storage }}</td>
<!-- change storage -->
<td ng-show="evilPortal.sdAvailable">
<a ng-show="(portal.storage == 'internal') && !portal.active" href="javascript:;" ng-click="movePortal(portal, 'sd');">SD</a>
<a ng-show="(portal.storage == 'sd') && !portal.active" href="javascript:;" ng-click="movePortal(portal, 'internal');">Internal</a>
</td>
<td>
<a href="javascript:;" data-toggle="modal" data-target="#readLogsModal" ng-click="loadPortalLog(portal)">View</a>
</td>
<!-- activate/deactivate -->
<td>
<a ng-hide="portal.active" href="javascript:;" ng-click="activatePortal(portal)">Activate</a>
<a ng-show="portal.active" href="javascript:;" ng-click="deactivatePortal(portal)">Deactivate</a>
</td>
<!-- delete portal button -->
<td>
<a ng-hide="portal.active" data-toggle="modal" data-target="#deleteModal" ng-click="deletePortal(false, portal)">Delete</a>
</td>
</tr>
</tbody>
</table>
</div>
@ -129,9 +133,10 @@
<!-- Editor Buttons -->
<div class="row-fluid">
<span class="pull-left">
<button type="submit" class="btn btn-default btn-sm" ng-click="evilPortal.library = true; resetWorkshop()">Back To Library</button>
<button type="submit" class="btn btn-default btn-sm" ng-click="evilPortal.library = true; resetWorkshop()">Back</button>
</span>
<span class="pull-right">
<button type="submit" class="btn btn-default btn-sm" data-toggle="modal" data-target="#readLogsModal" ng-click="loadPortalLog(workshop.portal)">View Log</button>
<button type="submit" class="btn btn-default btn-sm" data-toggle="modal" data-target="#fileModal" ng-click="setupNewFile()">New File</button>
<!-- Holding off on toggle commands until a future release. Still need to think through how they should work. -->
<!--<button type="submit" class="btn btn-default btn-sm" data-toggle="modal" data-target="#commandEditorModal" ng-click="loadToggleCommands()">Toggle Commands</button>-->
@ -148,12 +153,14 @@
<table class="table table-striped" align="center">
<thead>
<th>File Name</th>
<th>Size</th>
<th>Edit</th>
<th>Delete</th>
</thead>
<tbody>
<tr ng-repeat="file in workshop.dirContents">
<td>{{ file.name }}</td>
<td>{{ file.size }}</td>
<td><a href="javascript:;" ng-click="loadFileContent(file.path)" data-toggle="modal" data-target="#fileModal">Edit</a></td>
<td><a href="javascript:;" ng-click="workshop.deleteFile = file" data-toggle="modal" data-target="#deleteFileModal" ng-show="file.deletable">Delete</a></td>
</tr>
@ -281,6 +288,17 @@
<fieldset>
<legend>Change Log</legend>
<ul>
<li><b>3.1</b></li>
<ul>
<li class="text-muted">Added ability to write and view logs on a per-portal basis</li>
<li class="text-muted">Created method <i>writeLog($message)</i> that writes to the portal log file</li>
<li class="text-muted">Created method <i>notify($message)</i> that sends a notification to the web ui</li>
<li class="text-muted">Added ability to download files</li>
<li class="text-muted">Tab button in file editor will now insert four spaces</li>
<li class="text-muted">Revamped the file editor modal</li>
<li class="text-muted">Showing file sizes in the portal work bench</li>
<li class="text-muted">Various quality of life improvements</li>
</ul>
<li><b>3.0</b></li>
<ul>
<li class="text-muted">Added ability to route clients to different portals based upon some identifier [ssid, mac vendor, ip, etc...]</li>
@ -333,58 +351,84 @@
// Thanks to sud0nick on forums.hak5.org for this snippet
$(document).delegate('#evilportalEditor', 'keydown', function(e) {
var keyCode = e.keyCode || e.which;
if (keyCode == 9) {
e.preventDefault();
var start = $(this).get(0).selection;
var end = $(this).get(0).selectionEnd;
for (var i = 0; i < 4; i++) {
// get caret position/selection
var start = this.selectionStart;
var end = this.selectionEnd;
// set textarea value to: text before caret + tab + text after caret
$(this).val($(this).val().substring(0, start)
+ "\t"
+ $(this).val().substring(end));
var $this = $(this);
var value = $this.val();
// put caret at right position again
$(this).get(0).selectionStart =
$(this).get(0).selectionEnd = start + 1;
// set textarea value to: text before caret + tab + text after caret
$this.val(value.substring(0, start)
+ " "
+ value.substring(end));
// put caret at right position again (add one for the tab)
this.selectionStart = this.selectionEnd = start + 1;
// prevent the focus lose
e.preventDefault();
}
}
});
</script>
<div class="modal-dialog">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"
ng-click="workshop.editFile = {}">&times;</button>
<h4 class="modal-title" ng-show="!workshop.editFile.isNewFile">Editing File {{ workshop.editFile.name }}</h4>
<h4 class="modal-title" ng-show="!workshop.editFile.isNewFile">Editing File</h4>
<h4 class="modal-title" ng-show="workshop.editFile.isNewFile">Creating New File</h4>
</div>
<div class="modal-body">
<form class="form-horizontal">
<div class="form-group">
<label class="control-label">File Name</label>
<input type="text" class="form-control" ng-model="workshop.editFile.name" placeholder="Notes.txt" ng-disabled="!workshop.editFile.isNewFile">
</div>
<div class="form-group">
<label class="control-label">File Contents</label> <a href="javascript:;" ng-click="workshop.editFile.content = null">Clear</a>
<textarea class="form-control" rows="10" ng-model="workshop.editFile.content"
placeholder="Write some text here" id="evilportalEditor"></textarea>
</div>
</form>
<label class="control-label">File Name</label>
<input type="text" class="form-control" ng-model="workshop.editFile.name" placeholder="Notes.txt" ng-disabled="!workshop.editFile.isNewFile">
<br />
<label class="control-label pull-left">File Contents</label>
<a href="javascript:;" ng-click="workshop.editFile.content = null" class="pull-right">Clear</a>
<textarea class="form-control" rows="20" ng-model="workshop.editFile.content" placeholder="Write some text here" id="evilportalEditor"/>
<label class="control-label pull-right">File Size: {{ workshop.editFile.size }}</label>
<a href="javascript:;" class="pull-left" ng-click="download(workshop.editFile.path)">Download</a>
<br />
<!-- <script src="modules/EvilPortal/includes/js/acejs/ace.js" type="text/javascript" charset="utf-8"></script> -->
</div>
<div class="modal-footer">
<button type="button" ng-click="saveFileContent(workshop.editFile);" class="btn btn-success pull-right" data-dismiss="modal" ng-disabled="workshop.editFile.content == null">Save</button>
<button type="button" class="btn btn-default pull-left" data-dismiss="modal"
ng-click="workshop.editFile = {}">Cancel
</button>
<button type="button" ng-click="saveFileContent(workshop.editFile);" class="btn btn-success pull-right" data-dismiss="modal"
ng-disabled="workshop.editFile.content == null">Save</button>
<button type="button" class="btn btn-default pull-left" data-dismiss="modal" ng-click="workshop.editFile = {}">Cancel</button>
</div>
</div>
</div>
</div>
<!-- Read Logs Modal -->
<div id="readLogsModal" class="modal fade" role="dialog">
<div class="modal-dialog">
<!-- Modal content -->
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" ng-click="activeLog = null;">&times;</button>
<h4 class="modal-title">Viewing <i><b>{{ activeLog.path }}</b></i></h4>
</div>
<div class="modal-body">
<a href="javascript:;" class="pull-left" ng-click="loadLog(activeLog.path)">Refresh</a>
<a href="javascript:;" class="pull-right" ng-click="clearLog()">Clear</a>
<textarea class="form-control" rows="20" ng-model="activeLog.contents" placeholder="Nothing to show." readonly/>
<label class="control-label pull-right">Log Size: {{ activeLog.size }}</label>
<a href="javascript:;" class="pull-left" ng-click="download(activeLog.path)">Download</a>
<br />
</div>
</div>
</div>
</div>
<!-- Delete Portal Modal -->
<div id="deleteModal" class="modal fade" role="dialog">
<div class="modal-dialog">

View File

@ -1,10 +1,10 @@
{
"author": "newbi3",
"description": "An Evil Captive Portal.",
"devices": [
"nano",
"tetra"
],
"title": "Evil Portal",
"version": "3.0"
"author": "newbi3",
"description": "An Evil Captive Portal.",
"devices": [
"nano",
"tetra"
],
"title": "Evil Portal",
"version": "3.1"
}