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