-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathconvertASPdotNETIdentityHash2hashcatFormat.py
More file actions
executable file
·492 lines (425 loc) · 19.8 KB
/
Copy pathconvertASPdotNETIdentityHash2hashcatFormat.py
File metadata and controls
executable file
·492 lines (425 loc) · 19.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
#!/usr/bin/env python3
# Needs at least python 3.10
# Copyright (c) 2026, Yannic Hemmer, Pentagrid AG
#
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# The views and conclusions contained in the software and documentation are those
# of the authors and should not be interpreted as representing official policies,
# either expressed or implied, of the project.
#
# NON-MILITARY-USAGE CLAUSE
# Redistribution and use in source and binary form for military use and
# military research is not permitted. Infringement of these clauses may
# result in publishing the source code of the utilizing applications and
# libraries to the public. As this software is developed, tested and
# reviewed by *international* volunteers, this clause shall not be refused
# due to the matter of *national* security concerns.
import os
import sys
import base64
import getopt
import logging
from enum import Enum
from typing import ClassVar
from dataclasses import dataclass
from abc import ABC, abstractmethod
@dataclass(frozen=True)
class AspDotNetCIHTestCase:
asp_dot_net_cih: str
hashcat_format: str
# ASP.NET Core Identity hash TESTCASES
testcases = [
## identity-to-hashcat
## Link: https://github.com/edernucci/identity-to-hashcat.git
## License: GPL-3.0 license
## Note: uses bash, only supports sha1 and sha256
AspDotNetCIHTestCase(
asp_dot_net_cih="AGKcecrgyBNHkgjKqDlQPW7YlimD+VXPfpAx+gLSR0n1cOdIxFHaxKEF8SgAoaEtqQ==",
hashcat_format="sha1:1000:Ypx5yuDIE0eSCMqoOVA9bg==:2JYpg/lVz36QMfoC0kdJ9XDnSMRR2sShBfEoAKGhLak="
),
## ASP.NET Identity - Hashcat
## Link: https://github.com/brunobritodev/Hashcat.ASPNET.Identity.git
## License: none
## Also:
## Link: https://github.com/NetDevPack/NetDevPack/blob/master/src/NetDevPack/Utilities/AspNetIdentityHashInfo.cs
## License: MIT license
## Note: uses C#
AspDotNetCIHTestCase(
asp_dot_net_cih="AQAAAAEAACcQAAAAEG7xx8smhzcYFaAhPSRj1rgxfAoqKBv4WM/4R+Z0SvFxtxuMkfgBS28p1MQzvV0OeQ==",
hashcat_format="sha256:10000:bvHHyyaHNxgVoCE9JGPWuA==:MXwKKigb+FjP+EfmdErxcbcbjJH4AUtvKdTEM71dDnk=",
),
## ASP.NETIdentity2hashcat
## Link: https://github.com/Yeeb1/ASP.NETIdentity2hashcat.git
## License: none
## Note: uses python, only supports sha1 and sha256
AspDotNetCIHTestCase(
asp_dot_net_cih="ALT8uRA+E0bciDkDuRx/VLNOd4MpeEcccPMRc11YtE8z9EtDJHksDChDdmcNFXgCAQ==",
hashcat_format="sha1:1000:tPy5ED4TRtyIOQO5HH9Usw==:TneDKXhHHHDzEXNdWLRPM/RLQyR5LAwoQ3ZnDRV4AgE=",
),
## IdentityV3PasswordHasher
## Link: https://www.blinkingcaret.com/2017/11/29/asp-net-identity-passwordhash/
## License: none
## Also:
## Link: https://github.com/ruidfigueiredo/IdentityV3PasswordHasher/tree/master
## License: MIT license
## Note: other use case
## Correctness of test case confirmed with hashcat
AspDotNetCIHTestCase(
asp_dot_net_cih="AQAAAAEAACcQAAAAEFWLthQDW2xiWaS3vLgY4ItJdModbW0kzKtb8IVuXBY3fFaIntkbbdqTj8mTXH4mmA==:cutecats",
hashcat_format="sha256:10000:VYu2FANbbGJZpLe8uBjgiw==:SXTKHW1tJMyrW/CFblwWN3xWiJ7ZG23ak4/Jk1x+Jpg=",
),
## Correctness of test case confirmed with hashcat
AspDotNetCIHTestCase(
asp_dot_net_cih="AQAAAAEAACcQAAAAEDz3Wuf1QjDt14gWSdya6u5D6X8sBqbNJdNjeqGJBO52AIp3RYKXeBzDiPfeL1LPkQ==:this is a long password",
hashcat_format="sha256:10000:PPda5/VCMO3XiBZJ3Jrq7g==:Q+l/LAamzSXTY3qhiQTudgCKd0WCl3gcw4j33i9Sz5E=",
),
## Correctness not confirmed due to unkown password
AspDotNetCIHTestCase(
asp_dot_net_cih="AQAAAAEAACcQAAAAEAco3n3jFZc+9/IXf5IFpCnLz7Yx/c8lAw+cZ3JvFcWA8xL1Oa90gX/yzB6h6KsUgg==",
hashcat_format="sha256:10000:ByjefeMVlz738hd/kgWkKQ==:y8+2Mf3PJQMPnGdybxXFgPMS9TmvdIF/8sweoeirFII=",
),
## OWN hashes with PBKDF2+HMAC-SHA512
AspDotNetCIHTestCase(
asp_dot_net_cih="AQAAAAIAAYagAAAAEPcrvDihsovZNHxRFrv1y3i4qfjKsXn+JzKD7AOIlxe5YSb/s+tMXJVQeU1//VQXow==:pentagrid",
hashcat_format="sha512:100000:9yu8OKGyi9k0fFEWu/XLeA==:uKn4yrF5/icyg+wDiJcXuWEm/7PrTFyVUHlNf/1UF6M=",
),
]
# CLI predefined OUTPUTS
INFO_HELP = """This program converts ASP.NET Core Identity hashes to hashcat format.
usage: convertASPdotNETIdentityHash2hashcatFormat.py [-h] [-v] [-t] [-o output_file] input_file
options:
-h Output help information.
-v Verbose output with DEBUG log level.
-i Display information on ASP.NET Core Identity hashes.
-t Run predefined ASP.NET Core Identity hash test cases.
-u Prepend the annotation from the input to the formated hash. Hashcat must be then used with option --username.
-o Path to output directory to which the hashes (hashcat format) are written to files split by PRF.
input_file: Path to input file that includes the ASP.NET Core Identity hashes.
* The ASP.NET Core Identity hashes must be in (normal) base64 encoded format.
* The ASP.NET Core Identity hashes can be in-line appended with an annotation (e.g. username, email) using ":".
* Empty lines will be filtered.
Examples:
# Get information about ASP.NET Core Identity structure
python3 convertASPdotNETIdentityHash2hashcatFormat.py -i
# Parse ASP.NET hashes from input_file
python3 convertASPdotNETIdentityHash2hashcatFormat.py obtainedASPdotNEThashes.txt
# Parse ASP.NET hashes from input_file to output folders structure
python3 convertASPdotNETIdentityHash2hashcatFormat.py -o outDir obtainedASPdotNEThashes.txt
# This will create a local folder "outDir" with sucessfull parsed ASP.NET Core Identity hashes split by PRF.
.
└── outDir
├── hashes.sha256
└── hashes.sha512
# To crack such hashes with hashcat use "hashcat outDir/hashes.sha512 wordlist.txt"
# Parse ASP.NET hashes from input_file with usernames
python3 convertASPdotNETIdentityHash2hashcatFormat.py -u obtainedASPdotNEThashes.txt
# This will prepend the in-line annotation from the input file to the hashcat formated hash
# To crack such hashes (here: sha512 in folder "outDir") with hashcat
hashcat -m 12100 --username outDir/hashes.sha512 wordlist.txt
# To show cracked hashes with the annotations
hashcat --username --show outDir/hashes.sha512
This programs support the following ASP.NET Core Identity hash formats.
| ASP.NET version | Hash-Type | Hash-Code |
| --------------- | ------------------ | --------- |
| v2,v3 | PBKDF2+HMAC-SHA1 | 12000 |
| v3 | PBKDF2+HMAC-SHA256 | 10900 |
| v3 (.NET > 7.0) | PBKDF2+HMAC-SHA512 | 12100 |
This program is developed Yannic Hemmer at Pentagrid AG, Buchs SG.
"""
INFO_ASPdotNET_IDENTITY_HASH_STRUCTURE = """ASP.NET Core Identity Hashes are base64 decoded to a byte array. Byte positions are denoted as intervals.
| [0] | ...
|
'--> 0x00 ASP.NET Core Identity hashing logic version v2
| | [0] | [1,16] | [17,49] |
| | 0x00 | SALT | SUBKEY |
|
'--> 0x01 ASP.NET Core Identity hashing logic version v3
| [0] | [1,4] | [5,8] | [9,12] | [13,(13+SALTLEN-1)] | [(13+SALTLEN),EOL] |
| 0x01 | PRF | ITER | SALTLEN | SALT | SUBKEY |
| |
| ' --> 10,000 (HMAC-SHA256 .NET < 7.0 default)
| ' --> 100,000 (HMAC-SHA512 .NET >= 7.0 default)
|
' --> 0x00 HMAC-SHA1
' --> 0x01 HMAC-SHA256
' --> 0x02 HMAC-SHA512
ASP.NET_IDENTITY_HASH_...
SALT (32byte) --> Password salt used in PBKDF2+HMAC-SHA{1,256,512} function
SUBKEY (32byte) --> Result of PBKDF2+HMAC-SHA{1,256,512}
(only v2 -- method HashPasswordV2 in [1])
Number of iterations: 1,000 (static)
Length of the salt: 16 bytes (static)
Length of the subkey: 32 bytes (static)
(only v3 -- methods HashPasswordV3 in [1])
PRF (uint32BE) --> Pseudo-random function used for key derivation (see [2])
ITER (uint32BE) --> Number of iterations the hashing algorithm is applied
SALTLEN (uint32BE) --> Length of the salt (saltSize)
REF[1]: https://github.com/dotnet/aspnetcore/blob/c0c2230799a3f876aa673a66bc008bf9b803acac/src/Identity/Extensions.Core/src/PasswordHasher.cs
REF[2]: https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.cryptography.keyderivation.keyderivationprf?view=aspnetcore-10.0
For python list addressation:
| Notation | python |
| [a,b] | [a,(b+1)] or slice(a, (b+1)) |
Additional information:
* https://www.strathweb.com/2023/03/beware-of-the-default-aspnet-identity-settings/
* https://www.blinkingcaret.com/2017/11/29/asp-net-identity-passwordhash/
* https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2
"""
SLICE_ASP_DOT_NET_IDENTITY_HASH_VERSION = slice(0,1)
## v2
ASP_DOT_NET_CIH_V2_ITER = 1000
ASP_DOT_NET_CIH_V2_SALTLEN = 16
ASP_DOT_NET_CIH_V2_SUBKEYLEN = 32
SLICE_ASP_DOT_NET_CIH_V2_SALT = slice(
1,
1+ASP_DOT_NET_CIH_V2_SALTLEN
)
SLICE_ASP_DOT_NET_CIH_V2_SUBKEY = slice(
1+ASP_DOT_NET_CIH_V2_SALTLEN,
(1+ASP_DOT_NET_CIH_V2_SALTLEN+ASP_DOT_NET_CIH_V2_SUBKEYLEN)
)
## v3
SLICE_ASP_DOT_NET_CIH_V3_PRF = slice(1,5)
SLICE_ASP_DOT_NET_CIH_V3_ITER = slice(5,9)
SLICE_ASP_DOT_NET_CIH_V3_SALTLEN = slice(9,13)
INDEX_ASP_DOT_NET_CIH_SALT = 13
# ASP.NET Core Identity hash CONSTANTS
class AspDotNetCIHversion(Enum):
v2 = 0x00
v3 = 0x01
class AspDotNetCIHprf(Enum):
sha1 = 0x00000000
sha256 = 0x00000001
sha512 = 0x00000002
@dataclass(frozen=True)
class AspDotNetCIHAbstractBase(ABC):
version: ClassVar[AspDotNetCIHversion]
salt: bytes
subkey: bytes
@property
@abstractmethod
def prf(self) -> AspDotNetCIHprf:
...
@property
@abstractmethod
def iterations(self) -> int:
...
@property
@abstractmethod
def salt_len(self) -> int:
...
def hashcat_format(self) -> str:
return (
f"{self.prf.name}:"
f"{self.iterations}:"
f"{b64enc(self.salt)}:"
f"{b64enc(self.subkey)}"
)
def debug_format(self) -> dict :
return {
"version":f"{self.version.name}",
"prf":self.prf.name,
"prf_long_format":f"PBKDF2+HMAC-{self.prf.name}",
"iterations":f"{self.iterations:,}",
"salt_len":f"{self.salt_len} bytes",
"salt":self.salt.hex(),
"salt_b64":b64enc(self.salt),
"subkey_len":f"{len(self.subkey)} bytes",
"subkey":self.subkey.hex(),
"subkey_b64":b64enc(self.subkey),
"hashcat_format":self.hashcat_format()
}
@dataclass(frozen=True)
class AspDotNetCIHv2(AspDotNetCIHAbstractBase):
version: ClassVar[AspDotNetCIHversion] = AspDotNetCIHversion.v2
@property
def prf(self):
return AspDotNetCIHprf.sha1
@property
def iterations(self):
return ASP_DOT_NET_CIH_V2_ITER
@property
def salt_len(self):
return ASP_DOT_NET_CIH_V2_SALTLEN
@dataclass(frozen=True)
class AspDotNetCIHv3(AspDotNetCIHAbstractBase):
version: ClassVar[AspDotNetCIHversion] = AspDotNetCIHversion.v3
_prf: AspDotNetCIHprf
_iterations: int
_salt_len: int
@property
def prf(self):
return self._prf
@property
def iterations(self):
return self._iterations
@property
def salt_len(self):
return self._salt_len
def read_asp_dot_net_cih_from_file(filepath: str) -> list:
"""Reads the file from filepath and returns a list of lines."""
file = open(filepath,'r')
return file.readlines()
def b64enc(asp_dot_net_cih_salt_or_subkey: bytes) -> str:
"""Encodes bytes in Base64 in utf-8 format."""
return base64.b64encode(asp_dot_net_cih_salt_or_subkey).decode('utf-8')
def b64dec_remove_comment(asp_dot_net_cih_b64: str) -> bytes:
"""Returns the contents of a string until the first ":" and base64 decodes it.
The extracted (first) part should be a valid ASP.NET Core Identity hash.
"""
return base64.b64decode(asp_dot_net_cih_b64.split(":")[0])
def b64dec_extract_comment(asp_dot_net_cih_b64: str) -> str:
"""Return the contents of a string after the first ":".
The extracted (second) part is a comment annotation. If no comment exists return empty string.
"""
try:
return asp_dot_net_cih_b64.split(":")[1].strip()
except IndexError:
return ""
def get_slice_asp_dot_net_cih_v3_salt(asp_dot_net_cih_v3_saltlen: int) -> slice:
"""Returns the slice for the salt byte structure based on the length of the salt."""
return slice(INDEX_ASP_DOT_NET_CIH_SALT,(INDEX_ASP_DOT_NET_CIH_SALT+asp_dot_net_cih_v3_saltlen))
def get_slice_asp_dot_net_chi_v3_subkey(asp_dot_net_cih_v3_saltlen: int) -> slice:
"""Returns the calculated slice for the subkey byte structure based on the length of the salt.
None must be set in slice to read until the last byte (EOL) of the byte structure, since the length of the subkey is not known/given as a value.
"""
return slice((INDEX_ASP_DOT_NET_CIH_SALT+asp_dot_net_cih_v3_saltlen),None)
def get_asp_dot_net_cih_version(asp_dot_net_cih: bytes) -> AspDotNetCIHversion:
"""Returns the version (AspDotNetCIHversion) of an ASP.NET Core Identity hash based on the interger value of a byte slice."""
try:
return AspDotNetCIHversion(int.from_bytes(asp_dot_net_cih[SLICE_ASP_DOT_NET_IDENTITY_HASH_VERSION], byteorder='big'))
except ValueError:
raise ValueError(f"Version number {asp_dot_net_cih[SLICE_ASP_DOT_NET_IDENTITY_HASH_VERSION]} not supported!")
def get_asp_dot_net_cih_prf(asp_dot_net_cih_v3: bytes) -> AspDotNetCIHprf:
"""Returns the PRF (AspDotNetCIHprf) of an ASP.NET Core Identity hash based on the interger value of a byte slice."""
try:
return AspDotNetCIHprf(int.from_bytes(asp_dot_net_cih_v3[SLICE_ASP_DOT_NET_CIH_V3_PRF], byteorder='big'))
except ValueError:
raise ValueError(f"PRF {asp_dot_net_cih_v3[SLICE_ASP_DOT_NET_CIH_V3_PRF]} not supported!")
def parse_asp_dot_net_cih_v2(asp_dot_net_cih_v2: bytes) -> AspDotNetCIHv2:
"""Return an ASP.NET Core Identity hash v2 (AspDotNetCIHv2) instance from a byte structure."""
return AspDotNetCIHv2(
salt=asp_dot_net_cih_v2[SLICE_ASP_DOT_NET_CIH_V2_SALT],
subkey=asp_dot_net_cih_v2[SLICE_ASP_DOT_NET_CIH_V2_SUBKEY],
)
def parse_asp_dot_net_cih_v3(asp_dot_net_cih_v3: bytes) -> AspDotNetCIHv3:
"""Return an ASP.NET Core Identity hash v3 (AspDotNetCIHv3) instance from a byte structure."""
salt_len = int.from_bytes(asp_dot_net_cih_v3[SLICE_ASP_DOT_NET_CIH_V3_SALTLEN], byteorder='big')
return AspDotNetCIHv3(
_prf=get_asp_dot_net_cih_prf(asp_dot_net_cih_v3),
_iterations=int.from_bytes(asp_dot_net_cih_v3[SLICE_ASP_DOT_NET_CIH_V3_ITER], byteorder='big'),
_salt_len=salt_len,
salt=asp_dot_net_cih_v3[get_slice_asp_dot_net_cih_v3_salt(salt_len)],
subkey= asp_dot_net_cih_v3[get_slice_asp_dot_net_chi_v3_subkey(salt_len)],
)
def parse_asp_dot_net_cih(asp_dot_net_cih: bytes) -> AspDotNetCIHAbstractBase:
"""Instantiates either an AspDotNetCIHv2 or AspDotNetCIHv3 instance under the abstract base class AspDotNetCIHAbstractBase, based on version/format marker of an ASP.NET Core Identity hash."""
match get_asp_dot_net_cih_version(asp_dot_net_cih):
case AspDotNetCIHversion.v2:
logging.debug("Identified v2 ASP.NET Core Identity hash")
return parse_asp_dot_net_cih_v2(asp_dot_net_cih)
case AspDotNetCIHversion.v3:
logging.debug("Identified v3 ASP.NET Core Identity hash")
return parse_asp_dot_net_cih_v3(asp_dot_net_cih)
def cli_run_unit_tests():
"""Run the predefined unit tests."""
logging.info(f"Unit testing for {len(testcases)} predefined ASP.NET Core Identity hash test cases.")
for testcase in testcases:
asp_dot_net_cih = b64dec_remove_comment(testcase.asp_dot_net_cih)
asp_dot_net_cih = parse_asp_dot_net_cih(asp_dot_net_cih)
logging.debug(asp_dot_net_cih.debug_format())
assert asp_dot_net_cih.hashcat_format() == testcase.hashcat_format, f"Test case {testcase} did not pass. Check parser implementation."
logging.info("All test passed")
def cli_usage():
"""Prints the usage information to CLI."""
print(INFO_HELP)
def cli_asp_dot_net_cih_info():
"""Prints general information about ASP.NET Core Identity hashes to CLI."""
print(INFO_ASPdotNET_IDENTITY_HASH_STRUCTURE)
def is_empty_line(asp_dot_net_cih_b64) -> bool:
"""Returns True if line is empty else False."""
return not asp_dot_net_cih_b64.strip()
def main(argv):
print_help = False
print_info = False
run_test_cases = False
verbose_output = False
username_output = False
output_filepath = None
options = "hvtiuo:"
try:
opts, args = getopt.getopt(argv, options)
except getopt.error as err:
logging.error(str(err))
cli_usage()
exit(1)
for opt, arg in opts:
if opt == "-h":
print_help = True
if opt == "-t":
run_test_cases = True
if opt == "-v":
verbose_output = True
if opt == "-i":
print_info = True
if opt == "-u":
username_output = True
if opt == "-o":
output_filepath = arg
if verbose_output:
logging.basicConfig(encoding='utf-8', level=logging.DEBUG)
else:
logging.basicConfig(encoding='utf-8', level=logging.INFO)
if run_test_cases:
cli_run_unit_tests()
return
if print_help:
cli_usage()
return
if print_info:
cli_asp_dot_net_cih_info()
if len(args) != 1:
logging.error("No input file argument provided. See usage:.\n")
cli_usage()
return
input_file = args[0]
for asp_dot_net_cih_b64 in read_asp_dot_net_cih_from_file(input_file):
if is_empty_line(asp_dot_net_cih_b64):
continue
asp_dot_net_cih = parse_asp_dot_net_cih(b64dec_remove_comment(asp_dot_net_cih_b64))
logging.debug(asp_dot_net_cih.debug_format())
if username_output:
annotation = b64dec_extract_comment(asp_dot_net_cih_b64)
print(
f"{annotation}:"
f"{asp_dot_net_cih.hashcat_format()}"
)
else:
print(asp_dot_net_cih.hashcat_format())
if output_filepath:
if not os.path.exists(output_filepath):
os.makedirs(output_filepath)
with open(f".{os.path.sep}{output_filepath}{os.path.sep}hashes.{asp_dot_net_cih.prf.name}", 'a') as of:
of.write(f"{asp_dot_net_cih.hashcat_format()}\n")
if __name__ == "__main__":
main(sys.argv[1:])