5

Recently we competed in the X-MAS 2019 CTF and many of the challenges included a proof of work (PoW) check to avoid Denial of Service (DoS) attacks against their servers. The most common was we were given a 6 character suffix and asked to find anything where the hash ended in that suffix:

Provide a hex string X such that sha256(X)[-6:] = abcdef

The suffix abcdef would change every time you connected to the server. An example of a correct response:

hash = e38450c7008711d86a4d6c2039c8633a1ed637281b96888d7d9ff257aaabcdef
x = 4cbab1bbb03b4a10aef586b6

Can this be done using hashcat?

Kristopher Ives
  • 161
  • 1
  • 5
  • Are you looking for ways to circumvent this security check, or are you literally just asking if hashcat could answer this query? The former is a good fit here, but the later is really just a programming question and you would be better off asking it on SO – Conor Mancone Dec 23 '19 at 13:42
  • 1
    My understanding is that questions about using `hashcat` are well accepted here, hence the tag that already existed. – Kristopher Ives Dec 23 '19 at 15:30

3 Answers3

4

This is not possible using hashcat, unless you're ready to change the source code to suit your needs.

For example, you can adapt s3inlc's fork which added an option to check for hashes with some specific properties (starting / ending with as much 0 as possible, etc.).

Benoit Esnard
  • 13,942
  • 7
  • 65
  • 65
2

Someone else may correct me, but I don't believe there is any way to answer that query except by brute force (which is, of course, the point). A strong hashing algorithm returns effectively random output for even small changes in the input, so they are specifically designed so that you can't do exactly what you are trying to do. If you were able to guess an input that hashed with a certain suffix, you would effectively be well on your way to a collision attack, which SHA256 is still secure against. However, if you know what their proof of work system is, then you can take advantage of that simple fact.

This is what I would do:

  1. I would calculate the SHA256 hash of 10-20 million numbers (however many my computer can calculate in a reasonable amount of time).
  2. If, for instance, I calculated the SHA256 hash of the first 16 million numbers, I would expect that to give me roughly 5 million unique 6 character suffixes (I'm guessing wildly here)
  3. That means that, if asked for a string that hashes to a random 6 character suffix, then my list with ~5 million unique 6 character suffixes has a 30% chance of containing the answer.
  4. Since the lookup is effectively instant, that means that I can answer the challenge 30% of the time with no further effort.
  5. Therefore, while my DoS has lost some of its effectiveness, I can still get through!

If you have a fast CPU and can calculate hashes quickly then you can try to increase your pool of unique siffixes (i.e. hash more numbers). Note that it doesn't actually matter what you hash - only the number of unique things you hash, since each one will give you a new hash and, potentially, a new unique suffix.

The key here is that trying to match a random 6 character suffix will take you on average ~32,000,000 attempts. However, if you just store the random suffixes of those attempts and build a lookup table, you'll likely end up with a database that gives you a decent-enough success ratio without having to do further hashing calculations.

How successful something like this would be depends a lot on the specifics of the problem, but it's probably your best bet. For reference, hashing rates for SHA256 on "standard" GPUs are usually somewhere between 1 Mhash/s and 1000 Mhash/s. This means that if you have even a low-end GPU laying around (1 Mhash/s) you can hash 100,000,000 unique inputs in just a couple of minutes. Estimating off the top of my head, I expect that with 100,000,000 hashes you could probably land a hit in your lookup table for about 75% of their challenges.

Conor Mancone
  • 29,899
  • 13
  • 91
  • 96
2

For what it's worth, here's a short script in python that will do this POW:

import hashlib
import random

h=None
while(h is None or h[-6:]!='abcdef'):
    p=random.randrange(1, 0xffffffffffffffffffffffff)
    h=hashlib.sha256(p.to_bytes(12, 'big')).hexdigest()

print('SHA256(' + hex(p) + ')=' + h)

This took about 15 seconds to run on my (decent, fairly new) laptop, and produced:

SHA256(0xea04518919c6caf94ee194e0)=51e6239c174b2cb27f40a06ebfd6e3ab2caab5a36769bfc64d26b1cbbcabcdef

To verify:

import hashlib

p=0xea04518919c6caf94ee194e0
h=hashlib.sha256(p.to_bytes(12, 'big')).hexdigest()
print('SHA256(' + hex(p) + ')=' + h)

Produces

SHA256(0xea04518919c6caf94ee194e0)=51e6239c174b2cb27f40a06ebfd6e3ab2caab5a36769bfc64d26b1cbbcabcdef

and likewise for p=0x4cbab1bbb03b4a10aef586b6

mti2935
  • 19,868
  • 2
  • 45
  • 64
  • you only need to calculate around 2^24 hashes to find that. Did you count that? – kelalaka Dec 23 '19 at 18:32
  • 1
    That's correct. I added a counter to the script above, and ran it again. It made 27607043 iterations, which just a little more than 2^24. – mti2935 Dec 23 '19 at 20:03
  • This is similar to the code we used at first but eventually we wrote it in C++ for speed. I was mainly asking here if `hashcat` could do it before I published the source, https://github.com/krisives/shapow/ – Kristopher Ives Dec 23 '19 at 21:05
  • 2
    Does it get faster if you just use a counter instead of randomness? – Paŭlo Ebermann Dec 24 '19 at 00:52
  • 2
    Good question. Yes, it does. It found a valid N such that SHA256(N)=*abcdef in about half the time. SHA256(0xacf05e)=b8396d1d5d77da768d6e814e3f70a9cff8582bfb24aae2c67a99dc8e2dabcdef. N=0xacf05e, which is 11333726 in decimal, which is about 2^23.4. – mti2935 Dec 24 '19 at 11:15