Lots of possibilities, for all we know there's a chance that some "other" salt is used or that either the password or the salt or both are preprocessed in some way. Additionally there could be multiple rounds of hashing one of the combinations ...
So this question literally requires looking at the code and therefore reverse code engineering to give you more than some wild guess.
I tried the most obvious cases and there was no match (tested with DSA, DSA-SHA, MD4, MD5, RIPEMD160, SHA, SHA1, SHA224, SHA256, SHA384, SHA512, dsaEncryption, dsaWithSHA, ecdsa-with-SHA1, md4, md5, ripemd160, sha, sha1, sha224, sha256, sha384, sha512, whirlpool):
#!/usr/bin/env python3
from hashlib import new as hashnew, algorithms_available
# Several possibilities
# - assuming the password is ASCII and the calculated hash is the hex
# representation of the digest (or parts thereof)
# 1. the salt could be used as ASCII string
# 2. as array of bytes of 32 bytes size
# 3. some other nondescript way
# - Salt could be appended or prepended
# - Output could be part of the hex digest of some hash algo that yields
# larger digests than MD5
DATA = [
("pass1234", "33b9d87b83c097ce3ea6e6f84a298f839aafc6d7aa23d909a43ac94bdee35ddf", "61477724c136fb285918114792685a4d"),
("test1234", "33b9d87b83c097ce3ea6e6f84a298f839aafc6d7aa23d909a43ac94bdee35ddf", "00281231e8928af48c34ca03e09eebe5"),
("simple123", "b68053c65b06b1b322b6cbc53fce1f614eee9366384a12aa01188bdaa8f14ac3", "0c9e564acfe36f51b54b5050741f351c"),
("pass1234", "b68053c65b06b1b322b6cbc53fce1f614eee9366384a12aa01188bdaa8f14ac3", "0fc692cc78a51a9650c896c735a1b577"),
]
results = {}
print("Testing", ", ".join(sorted(algorithms_available)))
# Go through supported hash algos
for algo in sorted(algorithms_available):
print(algo)
for password, salt, expected in DATA:
cases = []
# password + salt
cases.append(password.encode('ascii') + salt.encode('ascii'))
# salt + password
cases.append(salt.encode('ascii') + password.encode('ascii'))
# password + bytes(salt)
cases.append(password.encode('ascii') + bytes.fromhex(salt))
# bytes(salt) + password
cases.append(bytes.fromhex(salt) + password.encode('ascii'))
reslist = []
for case in cases:
h = hashnew(algo, case)
hexd = h.hexdigest()
if expected.lower() in hexd.lower():
highlight = hexd.lower().replace(expected.lower(), "[%s]" % (expected.lower()))
print("Match for algorithm %s: %s" % (algo, highlight))
Admittedly a naïve approach, but as you can see just from this, there's a multitude of possibilities in which the password and salt can be represented and combined and there are a multitude of hash algorithms to test.
Given the above did not include BLAKE2, I'd strongly recommend testing that one as well.
Additionally I'd like to draw your attention to key derivation algorithms like those supported in hashlib: pbkdf2_hmac
and scrypt
.
Anyway, short of brute-forcing (my above attempt and variations on that) you won't get around looking at the actual code. For starters you may be able to figure out the algorithm used by certain signatures. But moreover you may find out ways by which the password, the salt or both are mutated prior to the hashing.
And considering key derivation algorithms the fact that you can run them an arbitrary number of iterations provides for another "dimension" in the permutations possible ...