Main navigation | Main content
This week's lab is again an attack related to cryptography, but this time it's a replay attack against a cryptographic protocol. We've implemented the normal steps of the protocol as C programs, and you'll carry out your attack by manipulating the values that are their outputs/inputs.
The programs this week implement an authentication protocol based on challenge-response and public-key signatures. The purpose of the protocol is for a client to authenticate to a server. This is similar to the garage-door-opener class of protocols we discussed in lecture, where the client is the garage door opener and the server is the garage door. The server and the client both have secret signing keys for a public-key signature scheme, and the corresponding verification keys are also known to the other party.
Abstractly, the protocol looks like this:
S → C: (Chall, SignS(Chall))
C → S: (Chall, SignS(Chall), SignC(Chall), T, N)
The server sends the client a "challenge" which is a nonce, together with a signature to prove that the challenge came from the server. Then the client signs the same challenge with its own signing key, and returns it together with the original challenge and server signature, a timestamp, and a fresh nonce. The sever verifies both its own signature on the challenge (proving that it's a challenge the server sent) and the client's signature (proving that the client produced the signature). It also checks that the timestamp T is recent and that the client-chosen nonce N has never been used before. The timestamp and the client-chosen nonce prevent basic replay attacks. However, this protocol turns out to still be vulnerable to a slightly more complicated kind of relay attack. Can you see what it is?
We've implemented the steps of this protocol in three separate programs named gen-challenge, do-auth, and check-auth. gen-challenge and check-auth together simulate the server: gen-challenge produces the first message, while check-auth implements the server's checking of the second message. do-auth implements the client's behavior: it takes the output of gen-challenge and produces the corresponding response. The challenges and nonces are 256 bits, the public key signatures use the elliptic curve algorithm Ed25519, and the timestamps are in the Unix format of the number of seconds since the start of 1970. The challenges, nonces, and signatures are base-64 encoded, while the timestamp is in decimal. Each part of a message is on a separate line. check-auth keeps its list of previously-used nonces in a file named .4271_replaylab_nonce_db in your home directory (if you'd like you can delete it when you're done with the lab).
To simulate the fact that you as a network attacker would not have access to the client and servers' signing keys, we have pre-compiled the programs so you can run them but not see they keys inside. To save typing, we suggest you abbreviate the directory where these binaries appear with shell variable:
L=/web/classes/Fall-2023/csci4271/labs/13
You can check the normal operation of the protocol by just running the three programs connected with pipes:
$L/gen-challenge | $L/do-auth | $L/check-auth
You should see the final message Authentication succeeded!. On the other hand, if the authentication is more than 5 seconds old it will be rejected, as you can see in:
$L/gen-challenge | $L/do-auth | (sleep 6; $L/check-auth)
Also, the response from do-auth will only be accepted once. If you try to replay the entire response, the second time will be rejected:
$L/gen-challenge | $L/do-auth >resp $L/check-auth <resp $L/check-auth <resp
Your goal for the lab is to implement a replay attack that works. Based on observing a successful authentication, as in the first two steps in the example above, transform the previous response into a new response that will be accepted as fresh. Hint: think about what parts of the response you can generate yourself as the attacker, and which ones only the legitimate server or client can create.
If you're curious to see the implementations of the steps of the protocol, we've put copies of their original C code in the lab directory as gen-challenge-nokey.c, do-auth-nokey.c, and check-auth.c. Note however that the programs with nokey in the names have had the secret key information redacted. The programs are compiled with the LibSodium cryptography library with -lsodium on the compiler command line. If you like programming in C, you should be able to automate your replay attack using code similar to what we used for the legitimate client and server. Or the attack would also be do-able in a scripting language. You might find the following shell command helpful:
dd if=/dev/urandom bs=32 count=1 | base64 | head -c 43