Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ci/tests/integration/MaxAgentsTest.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public function run() {
HashtopolisTestFramework::log(HashtopolisTestFramework::LOG_INFO, "Running " . $this->getTestName() . "...");
$this->prepare();
try {
$response = $this->addHashlist(["name" => "NotSecureList", "isSecure" => false]);
$response = $this->addHashlist(["name" => "NotSecureList", "isSecure" => false])["hashlist"];
$hashlistId = $response["hashlistId"];

$this->testTaskMaxAgents($hashlistId);
Expand Down
2 changes: 1 addition & 1 deletion ci/tests/integration/RuleSplitTest.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ private function testRuleSplit() {
$file_id2 = $this->getFile('best64.rule');

# Create hashlist
$response = $this->addHashlist(["isSecure" => false]);
$response = $this->addHashlist(["isSecure" => false])["hashlist"];
$hashlistId = $response["hashlistId"];

# Create task with rule/wordlist
Expand Down
6 changes: 5 additions & 1 deletion src/inc/apiv2/common/AbstractBaseAPI.php
Original file line number Diff line number Diff line change
Expand Up @@ -1625,7 +1625,7 @@ static function createJsonResponse(array $data = [], array $links = [], array $i
/**
* Get single Resource
*/
protected static function getOneResource(object $apiClass, object $object, Request $request, Response $response, int $statusCode = 200): Response {
protected static function getOneResource(object $apiClass, object $object, Request $request, Response $response, int $statusCode = 200, array|null $creationInformation = null): Response {
$apiClass->preCommon($request);

$validExpandables = $apiClass->getExpandables();
Expand Down Expand Up @@ -1664,6 +1664,10 @@ protected static function getOneResource(object $apiClass, object $object, Reque
if ($apiClass->permissionErrors !== null) {
$metaData["Include errors"] = $apiClass->permissionErrors;
}
if(is_array($creationInformation)) {
$metaData["creationInformation"] = $creationInformation;
}

// Generate JSON:API GET output
$ret = self::createJsonResponse($dataResources[0], $links, $includedResources, $metaData);

Expand Down
23 changes: 18 additions & 5 deletions src/inc/apiv2/common/AbstractModelAPI.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,13 @@ abstract class AbstractModelAPI extends AbstractBaseAPI {
abstract static public function getDBAClass();

abstract protected function createObject(array $data): int;

abstract protected function deleteObject(object $object): void;

protected function createObjectAndGetResult(array $data): array {
return [];
}

/**
* Available 'expand' parameters on $object
*/
Expand Down Expand Up @@ -1208,11 +1212,20 @@ public function post(Request $request, Response $response, array $args): Respons

// Remove key aliases and sanitize to 'db values and request creation
$mappedData = $this->unaliasData($attributes, $allFeatures);
$pk = $this->createObject($mappedData);


// Request object again, since post-modified entries are not reflected into object.
$object = $this->getFactory()->get($pk);
return self::getOneResource($this, $object, $request, $response, 201);
$object = null;

if (isset($data["getCreationInformation"])) {
$creationResult = $this->createObjectAndGetResult($mappedData);
$object = $this->getFactory()->get($creationResult["pk"]);
return self::getOneResource($this, $object, $request, $response, 201, $creationResult["creationInformation"]);
}
else {
$pk = $this->createObject($mappedData);
$object = $this->getFactory()->get($pk);
return self::getOneResource($this, $object, $request, $response, 201);
}
}


Expand Down
22 changes: 17 additions & 5 deletions src/inc/apiv2/model/HashlistAPI.php
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,15 @@ public function getFormFields(): array {
* @throws HTException
*/
protected function createObject(array $data): int {
return $this->createObjectAndGetResult($data)["pk"];
}

/**
* @throws HttpErrorException
* @throws HttpError
* @throws HTException
*/
protected function createObjectAndGetResult(array $data): array {
// Cast to createHashlist compatible upload format
$dummyPost = [];
switch ($data["sourceType"]) {
Expand All @@ -139,7 +148,7 @@ protected function createObject(array $data): int {
}
}

$hashlist = HashlistUtils::createHashlist(
$hashlistData = HashlistUtils::createHashlist(
$data[Hashlist::HASHLIST_NAME],
$data[Hashlist::IS_SALTED],
$data[Hashlist::IS_SECRET],
Expand All @@ -159,11 +168,14 @@ protected function createObject(array $data): int {

// Modify fields not set on hashlist creation
if (array_key_exists("notes", $data)) {
HashlistUtils::editNotes($hashlist->getId(), $data["notes"], $this->getCurrentUser());
};
HashlistUtils::setArchived($hashlist->getId(), $data[UQueryHashlist::HASHLIST_IS_ARCHIVED], $this->getCurrentUser());
HashlistUtils::editNotes($hashlistData["hashlist"]->getId(), $data["notes"], $this->getCurrentUser());
}
HashlistUtils::setArchived($hashlistData["hashlist"]->getId(), $data[UQueryHashlist::HASHLIST_IS_ARCHIVED], $this->getCurrentUser());

return $hashlist->getId();
$creationResult["pk"] = $hashlistData["hashlist"]->getId();
$creationResult["creationInformation"] = $hashlistData["statistics"];

return $creationResult;
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/inc/handlers/HashlistHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ public function handle($action) {
AccessControl::getInstance()->getUser(),
(isset($_POST["useBrain"]) && intval($_POST["useBrain"]) == 1) ? 1 : 0,
(isset($_POST['brain-features'])) ? intval($_POST['brain-features']) : 0
);
)["hashlist"];
header("Location: hashlists.php?id=" . $hashlist->getId());
die();
case DHashlistAction::CREATE_SUPERHASHLIST:
Expand Down
2 changes: 1 addition & 1 deletion src/inc/user_api/UserAPIHashlist.php
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ private function createHashlist($QUERY) {
$this->user,
$QUERY[UQueryHashlist::HASHLIST_USE_BRAIN],
$QUERY[UQueryHashlist::HASHLIST_BRAIN_FEATURES]
);
)["hashlist"];
$this->sendResponse(array(
UResponseHashlist::SECTION => $QUERY[UQuery::SECTION],
UResponseHashlist::REQUEST => $QUERY[UQuery::REQUEST],
Expand Down
90 changes: 74 additions & 16 deletions src/inc/utils/HashlistUtils.php
Original file line number Diff line number Diff line change
Expand Up @@ -782,7 +782,8 @@ public static function export($hashlistId, $user) {
* @param User $user
* @param int $brainId
* @param int $brainFeatures
* @return Hashlist
* @param boolean $writeResultsToNotes
* @return array
* @throws HTException
*/
public static function createHashlist($name, $isSalted, $isSecret, $isHexSalted, $separator, $format, $hashtype, $saltSeparator, $accessGroupId, $source, $post, $files, $user, $brainId, $brainFeatures) {
Expand Down Expand Up @@ -851,33 +852,62 @@ public static function createHashlist($name, $isSalted, $isSecret, $isHexSalted,
Factory::getAgentFactory()->getDB()->rollback();
throw new HttpError("Hashlist has too many lines!");
}

$file = fopen($tmpfile, "rb");
if (!$file) {
Factory::getAgentFactory()->getDB()->rollback();
throw new HttpError("Failed to open file!");
}

if ($format == DHashlistFormat::PLAIN && $salted) {
// find out if the file contains a salt separator at all
rewind($file);

$saltSeparatorFound = false;
while (($currentLine = fgets($file)) !== false) {
if (strpos($currentLine, $saltSeparator) !== false) {
$saltSeparatorFound = true;
break;
}
}

if ($saltSeparatorFound === false) {
fclose($file);
unlink($tmpfile);
Factory::getAgentFactory()->getDB()->rollback();

throw new HttpError("Salted hashes separator not found at all in the hashlist! Hashlist not created.");
}
}
else {
$saltSeparator = "";
}

Factory::getAgentFactory()->getDB()->commit();

$added = 0;
$preFound = 0;
$hashlistStatistics = [];
$hashlistStatistics["uploadedTotalLines"] = 0;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using static strings as the key names is maybe not ideal, as it easily can happen that typos remain undiscovered and it's more difficult to discover errors.

Maybe it makes sense to define either constants for these keys or create a data class type object to handle the different values and then return the class object.

$hashlistStatistics["uploadedEmptyLines"] = 0;
$hashlistStatistics["uploadedValidHashes"] = 0;
$hashlistStatistics["uploadedValidHashesWithoutExpectedSalt"] = 0;
$hashlistStatistics["uploadedInvalidHashes"] = 0;

switch ($format) {
case DHashlistFormat::PLAIN:
if ($salted) {
// find out if the first line contains field separator
rewind($file);
$bufline = stream_get_line($file, 1024);
if (strpos($bufline, $saltSeparator) === false) {
throw new HttpError("Salted hashes separator not found in file!");
}
}
else {
$saltSeparator = "";
}
rewind($file);

Factory::getAgentFactory()->getDB()->beginTransaction();
$values = array();
$bufferCount = 0;

while (!feof($file)) {
$line = trim(fgets($file));
$hashlistStatistics["uploadedTotalLines"]++;

if (strlen($line) == 0) {
$hashlistStatistics["uploadedEmptyLines"]++;
continue;
}
$hash = $line;
Expand All @@ -888,8 +918,12 @@ public static function createHashlist($name, $isSalted, $isSecret, $isHexSalted,
$hash = substr($line, 0, $pos);
$salt = substr($line, $pos + 1);
}
else {
$hashlistStatistics["uploadedValidHashesWithoutExpectedSalt"]++;
}
}
if (strlen($hash) == 0) {
$hashlistStatistics["uploadedEmptyLines"]++;
continue;
}
//TODO: check hash length here
Expand All @@ -906,13 +940,15 @@ public static function createHashlist($name, $isSalted, $isSecret, $isHexSalted,
}
}
}

if ($found == null) {
$values[] = new Hash(null, $hashlist->getId(), $hash, $salt, "", 0, null, 0, 0);
}
else {
$values[] = new Hash(null, $hashlist->getId(), $hash, $salt, $found->getPlaintext(), time(), null, 1, 0);
$preFound++;
}

$bufferCount++;
if ($bufferCount >= 5000) {
$result = Factory::getHashFactory()->massSave($values);
Expand All @@ -921,27 +957,42 @@ public static function createHashlist($name, $isSalted, $isSecret, $isHexSalted,
$bufferCount = 0;
}
}

if (sizeof($values) > 0) {
$result = Factory::getHashFactory()->massSave($values);
$added += $result->rowCount();
}

fclose($file);
unlink($tmpfile);

if ($added === 0) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this check should be done at the very end (as the same could be checked for BINARY and WPA hashes). The $added has to be incremented on line 1047 as well, but for WPA it's already done and can be used.

Factory::getAgentFactory()->getDB()->rollback();
Factory::getHashlistFactory()->delete($hashlist);
Factory::getAgentFactory()->getDB()->commit();
throw new HttpError("No valid hashes found! Hashlist not created.");
}

Factory::getHashlistFactory()->mset($hashlist, [Hashlist::HASH_COUNT => $added, Hashlist::CRACKED => $preFound]);
Util::createLogEntry("User", $user->getId(), DLogEntry::INFO, "New Hashlist created: " . $hashlist->getHashlistName());
Util::createLogEntry("User", $user->getId(), DLogEntry::INFO, "New Hashlist created: " . $hashlist->getHashlistName() . ". Total lines: " . $hashlistStatistics["uploadedTotalLines"] . " Empty lines: " . $hashlistStatistics["uploadedEmptyLines"] . " Valid hashes: " . $hashlistStatistics["uploadedValidHashes"] . " Valid hashes without expected salt: " . $hashlistStatistics["uploadedValidHashesWithoutExpectedSalt"] . " Invalid hashes: " . $hashlistStatistics["uploadedInvalidHashes"]);

NotificationHandler::checkNotifications(DNotificationType::NEW_HASHLIST, new DataSet(array(DPayloadKeys::HASHLIST => $hashlist)));
break;
case DHashlistFormat::WPA:
$added = 0;
$values = [];

while (!feof($file)) {
$hashlistStatistics["uploadedTotalLines"]++;

if ($hashlist->getHashTypeId() == 2500) { // HCCAPX hashes
$data = fread($file, 393);
if (strlen($data) == 0) {
$hashlistStatistics["uploadedInvalidHashes"]++;
break;
}
if (strlen($data) != 393) {
$hashlistStatistics["uploadedInvalidHashes"]++;
UI::printError("ERROR", "Data file only contains " . strlen($data) . " bytes!");
}
// get the SSID
Expand Down Expand Up @@ -969,11 +1020,13 @@ public static function createHashlist($name, $isSalted, $isSecret, $isHexSalted,
$mac_cli = Util::bintohex($mac_cli);
$hash = new HashBinary(null, $hashlist->getId(), $mac_ap . SConfig::getInstance()->getVal(DConfig::FIELD_SEPARATOR) . $mac_cli . SConfig::getInstance()->getVal(DConfig::FIELD_SEPARATOR) . Util::bintohex($network), Util::bintohex($data), null, 0, null, 0, 0);
Factory::getHashBinaryFactory()->save($hash);
$hashlistStatistics["uploadedValidHashes"]++;
$added++;
}
else { // PMKID hashes
$line = trim(fgets($file));
if (strlen($line) == 0) {
$hashlistStatistics["uploadedEmptyLines"]++;
continue;
}
if (strpos($line, "*") !== false) {
Expand All @@ -992,14 +1045,15 @@ public static function createHashlist($name, $isSalted, $isSecret, $isHexSalted,
}
$hash = new HashBinary(null, $hashlist->getId(), $identification, Util::bintohex($line . "\n"), null, 0, null, 0, 0);
Factory::getHashBinaryFactory()->save($hash);
$hashlistStatistics["uploadedValidHashes"]++;
$added++;
}
}
fclose($file);
unlink($tmpfile);

Factory::getHashlistFactory()->set($hashlist, Hashlist::HASH_COUNT, $added);
Util::createLogEntry("User", $user->getId(), DLogEntry::INFO, "New Hashlist created: " . $hashlist->getHashlistName());
Util::createLogEntry("User", $user->getId(), DLogEntry::INFO, "New Hashlist created: " . $hashlist->getHashlistName() . ". Total lines: " . $hashlistStatistics["uploadedTotalLines"] . " Empty lines: " . $hashlistStatistics["uploadedEmptyLines"] . " Valid hashes: " . $hashlistStatistics["uploadedValidHashes"] . " Valid hashes without expected salt: " . $hashlistStatistics["uploadedValidHashesWithoutExpectedSalt"] . " Invalid hashes: " . $hashlistStatistics["uploadedInvalidHashes"]);

NotificationHandler::checkNotifications(DNotificationType::NEW_HASHLIST, new DataSet(array(DPayloadKeys::HASHLIST => $hashlist)));
break;
Expand All @@ -1008,17 +1062,21 @@ public static function createHashlist($name, $isSalted, $isSecret, $isHexSalted,
$data = fread($file, Util::filesize($tmpfile));
$hash = new HashBinary(null, $hashlist->getId(), "", Util::bintohex($data), "", 0, null, 0, 0);
Factory::getHashBinaryFactory()->save($hash);
$hashlistStatistics["uploadedValidHashes"]++;
}

fclose($file);
unlink($tmpfile);

Factory::getHashlistFactory()->set($hashlist, Hashlist::HASH_COUNT, 1);
Util::createLogEntry("User", $user->getId(), DLogEntry::INFO, "New Hashlist created: " . $hashlist->getHashlistName());
Util::createLogEntry("User", $user->getId(), DLogEntry::INFO, "New Hashlist created: " . $hashlist->getHashlistName() . ". Total lines: " . $hashlistStatistics["uploadedTotalLines"] . " Empty lines: " . $hashlistStatistics["uploadedEmptyLines"] . " Valid hashes: " . $hashlistStatistics["uploadedValidHashes"] . " Valid hashes without expected salt: " . $hashlistStatistics["uploadedValidHashesWithoutExpectedSalt"] . " Invalid hashes: " . $hashlistStatistics["uploadedInvalidHashes"]);

NotificationHandler::checkNotifications(DNotificationType::NEW_HASHLIST, new DataSet(array(DPayloadKeys::HASHLIST => $hashlist)));
break;
}

Factory::getAgentFactory()->getDB()->commit();
return $hashlist;
return ["hashlist" => $hashlist, "statistics" => $hashlistStatistics];
}

/**
Expand Down