Skip to content

Commit 8e8f0bd

Browse files
committed
added searchHashes helper
1 parent 20f760e commit 8e8f0bd

File tree

2 files changed

+154
-1
lines changed

2 files changed

+154
-1
lines changed

src/api/v2/index.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,9 +314,10 @@ public static function addCORSheaders(Request $request, $response) {
314314
require __DIR__ . "/../../inc/apiv2/helper/recountFileLines.routes.php";
315315
require __DIR__ . "/../../inc/apiv2/helper/resetChunk.routes.php";
316316
require __DIR__ . "/../../inc/apiv2/helper/resetUserPassword.routes.php";
317+
require __DIR__ . "/../../inc/apiv2/helper/searchHashes.routes.php";
317318
require __DIR__ . "/../../inc/apiv2/helper/setUserPassword.routes.php";
318319
require __DIR__ . "/../../inc/apiv2/helper/taskExtraDetails.routes.php";
319-
require __DIR__ . "/../../inc/apiv2/helper/unassignAgent.routes.php";
320+
require __DIR__ . "/../../inc/apiv2/helper/unassignAgent.routes.php";add
320321

321322

322323
$app->run();
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
<?php
2+
3+
use DBA\ContainFilter;
4+
use DBA\Factory;
5+
use DBA\Hash;
6+
use DBA\Hashlist;
7+
use DBA\JoinFilter;
8+
use DBA\LikeFilterInsensitive;
9+
use DBA\QueryFilter;
10+
11+
require_once(dirname(__FILE__) . "/../common/AbstractHelperAPI.class.php");
12+
13+
class SearchHashesHelperAPI extends AbstractHelperAPI {
14+
public static function getBaseUri(): string {
15+
return "/api/v2/helper/searchHashes";
16+
}
17+
18+
public static function getAvailableMethods(): array {
19+
return ['POST'];
20+
}
21+
22+
public function getRequiredPermissions(string $method): array {
23+
return [Hashlist::PERM_READ, Hash::PERM_READ];
24+
}
25+
26+
public function getFormFields(): array {
27+
return [
28+
"searchData" => ["type" => "str"], # base64 encoded search input
29+
"separator" => ['type' => 'str'],
30+
"isSalted" => ['type' => 'bool'],
31+
];
32+
}
33+
34+
public static function getResponse(): array {
35+
return [
36+
["found" => False,
37+
"query" => "12345678",
38+
],
39+
["found" => True,
40+
"query" => "54321",
41+
"matches" => [[
42+
"hashlistId" => 4,
43+
"hash" => "5432173922",
44+
"salt" => "",
45+
"plaintext" => "plain",
46+
"timeCracked" => 0,
47+
"chunkId" => null,
48+
"isCracked" => true,
49+
"crackPos" => 0,
50+
],
51+
[
52+
"hashlistId" => 4,
53+
"hash" => "12345654321",
54+
"salt" => "",
55+
"plaintext" => "",
56+
"timeCracked" => 0,
57+
"chunkId" => null,
58+
"isCracked" => false,
59+
"crackPos" => 0,
60+
]
61+
],
62+
]
63+
];
64+
}
65+
66+
/**
67+
* Endpoint to import cracked hashes into a hashlist.
68+
* @throws HttpError
69+
*/
70+
public function actionPost($data): object|array|null {
71+
$search = base64_decode($data['searchData']);
72+
$isSalted = $data['isSalted'];
73+
$separator = $data['separator'];
74+
75+
if (strlen($search) == 0) {
76+
throw new HttpError("Search query cannot be empty!");
77+
}
78+
else if ($search === false) {
79+
throw new HttpError("Search query is not valid base64!");
80+
}
81+
else if ($isSalted && strlen($separator) == 0) {
82+
throw new HttpError("Salt separator cannot be empty!");
83+
}
84+
85+
$search = str_replace("\r\n", "\n", $search);
86+
$search = explode("\n", $search);
87+
$resultEntries = array();
88+
$userHashlists = HashlistUtils::getHashlists(Login::getInstance()->getUser(), false);
89+
$userHashlists += HashlistUtils::getHashlists(Login::getInstance()->getUser(), true);
90+
foreach ($search as $searchEntry) {
91+
if (strlen($searchEntry) == 0) {
92+
continue;
93+
}
94+
95+
// test if hash contains salt
96+
if ($isSalted) {
97+
$split = explode($separator, $searchEntry);
98+
$hash = $split[0];
99+
unset($split[0]);
100+
$salt = implode($separator, $split);
101+
}
102+
else {
103+
$hash = $searchEntry;
104+
$salt = "";
105+
}
106+
107+
// TODO: add option to select if exact match or like match
108+
109+
$filters = array();
110+
$filters[] = new LikeFilterInsensitive(Hash::HASH, "%" . $hash . "%");
111+
$filters[] = new ContainFilter(Hash::HASHLIST_ID, Util::arrayOfIds($userHashlists), Factory::getHashFactory());
112+
if (strlen($salt) > 0) {
113+
$filters[] = new QueryFilter(Hash::SALT, $salt, "=");
114+
}
115+
$jF = new JoinFilter(Factory::getHashlistFactory(), Hash::HASHLIST_ID, Hashlist::HASHLIST_ID);
116+
$joined = Factory::getHashFactory()->filter([Factory::FILTER => $filters, Factory::JOIN => $jF]);
117+
118+
$qF1 = new LikeFilterInsensitive(Hash::PLAINTEXT, "%" . $searchEntry . "%");
119+
$qF2 = new ContainFilter(Hash::HASHLIST_ID, Util::arrayOfIds($userHashlists), Factory::getHashFactory());
120+
$joined2 = Factory::getHashFactory()->filter([Factory::FILTER => [$qF1, $qF2], Factory::JOIN => $jF]);
121+
/** @var $hashes Hash[] */
122+
$hashes = $joined2[Factory::getHashFactory()->getModelName()];
123+
for ($i = 0; $i < sizeof($hashes); $i++) {
124+
$joined[Factory::getHashFactory()->getModelName()][] = $joined2[Factory::getHashFactory()->getModelName()][$i];
125+
$joined[Factory::getHashlistFactory()->getModelName()][] = $joined2[Factory::getHashlistFactory()->getModelName()][$i];
126+
}
127+
128+
$resultEntry = [];
129+
/** @var $hashes Hash[] */
130+
$hashes = $joined[Factory::getHashFactory()->getModelName()];
131+
if (sizeof($hashes) == 0) {
132+
$resultEntry["found"] = false;
133+
$resultEntry["query"] = $searchEntry;
134+
}
135+
else {
136+
$resultEntry["found"] = true;
137+
$resultEntry["query"] = $searchEntry;
138+
$matches = [];
139+
for ($i = 0; $i < sizeof($hashes); $i++) {
140+
/** @var $hash Hash */
141+
$hash = $joined[Factory::getHashFactory()->getModelName()][$i];
142+
$matches[] = $hash;
143+
}
144+
$resultEntry["matches"] = $matches;
145+
}
146+
$resultEntries[] = $resultEntry;
147+
}
148+
return $resultEntries;
149+
}
150+
}
151+
152+
ImportCrackedHashesHelperAPI::register($app);

0 commit comments

Comments
 (0)