Tags: hash 

Rating:

*([Original write-up](https://security.meta.stackexchange.com/a/3085/95381) by [@rawsec](https://twitter.com/rawsec/))*

## secure-hash (269, crypto)

We get a C++ implementation of a register/login system. The auth works as follows:

You can register with a username and password. The app then stores a `sha512` hash based on the credential pair in a hash table. When logging in, the app calculates a hash of the supplied credentials and looks it up in the table. If found, you're in. The task is to log in with user `root`, however the app disallows registering with the username `root`.

Let's see how the hash is calculated:

std::string sha512sum(const std::string& name, const std::string& password) {
EVP_MD_CTX *mdctx;
const EVP_MD *md;
unsigned char md_value[EVP_MAX_MD_SIZE];
unsigned int md_len;

mdctx = EVP_CREATE_FN();
md = EVP_get_digestbyname("sha512");
EVP_MD_CTX_init(mdctx);
EVP_DigestInit_ex(mdctx, md, NULL);
EVP_DigestUpdate(mdctx, name.c_str(), name.size());
EVP_DigestUpdate(mdctx, password.c_str(), password.size());
EVP_DigestFinal_ex(mdctx, md_value, &md_len);
EVP_DESTROY_FN(mdctx);

return std::string(reinterpret_cast<char*>(md_value), md_len);
}

So, username and password are supplied via `EVP_DigestUpdate()`. Under the hood, that's simply a concatenation of the given values until the hash is produced with `EVP_DigestFinal_ex()`. So we simply need to find `a, b, c` with `a != "root"` so that `sha512(a || b) == sha512("root" || c)`.

That would be satisfied by `a="roo", b="tX", c="X"`. Let's do it:

$ nc secure-hash.ctf.hackover.de 1337
Main menu:
1 - Register new user
2 - Login
1
Name: roo
Password: tX
Main menu:
1 - Register new user
2 - Login
2
Name: root
Password: X
Success! Logged in as root
You win, the flag is hackover18{00ps_y0u_mu5t_h4ve_h1t_a_v3ry_unlikely_5peci4l_c4s3}