Your mechanical arm needs to be replaced. Unfortunately, Steamshake Inc which is the top mechanical arm transplants has a long waiting list. You have found a SQL injection vulnerability and recovered two tables from their database. Could you take advantage of the information in there to speed things up? Don’t forget, you have a date on Monday!

Category: crypto

Solver: n1k0

Flag: HTB{t3ll_m3_y0ur_s3cr37_w17h0u7_t3ll1n9_m3_y0ur_s3cr37_15bf7w}


In the provided source code we see that we need to provide a signed message (ECDSA) for a specific appointment to get the flag. Additionally, there is a list of appointments and a list of signatures for these appointments. So probably we need to use this to forge a signature. It is suspicious that we also get the 7 least significant bits of the nonce k, which is used for signing. A quick research on malleability and private key recovery of ECDSA signatures [1][2] reveals that not only the reuse of k or a bias in its selection poses a security issue, but also leakage of the nonce, even partially, can be used to recover the private key if enough signatures are provided.

While searching for some implementations, we luckily stumble upon some code [3] that pretty much does what we want to - recovering the private key from a list of signatures and nonce bits.


We write a script to transform the provided text files with appointments and signatures into the JSON format that is expected by [3]. A few modifications to the script are required since we don’t know public key and curve name for the signatures. We also could have recovered the public key from the signatures, but it is easier to just comment out a few lines :) We also need to hard-code the curve order and size.

We run the script and quite fast we get two private key candidates:

candidate 1: 87704817386092187125465123720235289645198280191524790077928665990938948722383
candidate 2: 28087271824264061637232323229172283884798675032610970264493593070129563321986

We sign the message with both keys and put the JSON format together that is required by the challenge. This gives the two following signatures:

cipher0 = ECDSA(87704817386092187125465123720235289645198280191524790077928665990938948722383)

cipher1 = ECDSA(28087271824264061637232323229172283884798675032610970264493593070129563321986)

s1 = { "pt": "william;yarmouth;22-11-2021;09:00",
"r": "328e1aac6106a1da9f495f8321e0306972f19c8ffb642b8c36af26a6be6d37a8",
"s": "b9f4c942ee8bd03553de35c3c55c39a6ed9107e1649c2c0ea79778c171537c77"

s2 = { "pt": "william;yarmouth;22-11-2021;09:00",
"r": "ad2ec3e6ede64ea5afb9eb75ab81fdfdb16a132174621e2b87b5a0ed11a5fd84",
"s": "98f67f179c297ea7b3738a9d1360d8e4aef996300a3eed3522977d00c94fde36"

The second key/signature works, and we retrieve the flag.

Your appointment has been confirmed, congratulations!
Here is your flag: HTB{t3ll_m3_y0ur_s3cr37_w17h0u7_t3ll1n9_m3_y0ur_s3cr37_15bf7w}

Other resources