Changelog:
- 23 February 2023: mark scenario 3 as
if you have time
and as tentatively won’t be required - 24 February 2023: complete
tries to use a nonce
astries to use a nonce to prevent replay attacks
- 28 February 2023 11:50p: update
securelab.tar
to improve error messages (by removing text about internals of the lab; no functionality changes) - 1 March 2023: correct description of what signature is in
struct message
(in lab writeup and comments in lab.h; no functionality changes):using from’s public key
->using from’s private key
- 1 March 2023: correct scenario 3 hint to state that you should use A to decrypt messages from B (since A will talk to you and not B) rather than the other way around
- 1 March 2023: make it clearer that
new_message
can encrypt messages - 1 March 2023: update securelab.tar to not give cryptic error when encrypted messages are much shorter than expected; add note about problems with manipulating encrypted messages; add missing ) in example code
- 1 March 2023: note possible crash from earlier securelab.tar
Download securelab.tar [last updated 2023-03-01 3:45p] and extract it.
In this lab, you will act as a network attacker on several examples of attempts to communicate securely. Since this a new lab, I’m not sure how many are easily doable in the lab time. Do as many as you can within the lab time. I recommend working with a partner.
Run
make
to build the testing program and then run
./lab 0 normal
to see the first scenario. (It is also listed below in this document.) You’ll see a transcript showing an exchange where A asks B to pay M $1000, then asks B to pay $438 dollars.
At the end of the simulation, we report how much M is paid by B.
You are playing the role of the network attacker M and will demostrate how you get B to pay you more by having control over the network.
When sending messages over the network, our testing program can call one of the
forward_...()
functions inattack.c
. In this function, you can process the messages sent between A and B, including changing messages, adding new messages, or discarding messages.When you run
./lab N normal
, we’ll forward messages wihtout changes function for scenario N (where N is 0, 1, 2, 3, or 4).When you run
./lab N attack
, we’ll use theforward_attack_N
function to decide what to do with sent messages.For scenario 0, Modify the
forward_attack_0
function so B thinks A asked them to pay M (you) $10001438 instead of $1438. (Hint: This does not require sending a lot of messages.)Do the same for scenario
1
andforward_attack_1
, which you can run with./lab 1 normal
(non-attacked version) or
./lab 1 attack
Scenario 2 is trickier. Get B to pay at least $2000 (we don’t care about the exact amount) instead of $1438.
(optional) If you have time, do the same as scenario
2
for scenario3
. In this scenario, B initiates the communication with A and tries to use a nonce to prevent replay attacks. It will help that A will also accept messages from you (M), but will always indicate that there are no more payments. You can use this to run a man-in-the-middle attack on B and A; see also the famous flaws of the Needham-Schroeder protocol. The basic idea is that because A will communicate with you (M), you can get them to decrypt B’s message to A for you.Submit your
attack.c
to the submission site or show your code to a TA for checkoff.(This is a new lab, so I’m not sure how much typical students will complete. But I’m expecting that most students will not complete scenario
3
within the lab time, and so it will not be expected to be complete for submission to the submission site.)
1 Our simulation API
[Note added 1 March:] If your simulation crashes with an error like
thread, then this is from sending a message that is marked as encrypted but whose data is too short. Versions ofpanicked atrange start index 18446744073709551594 out of range for slice of length 10securelab.tar
after 2023-03-01 3:45pm give a better error message in this case.
The code for forward_normal looks like:
void forward_normal(struct message *message) {
send_message(message);
}
It just forwards the message without making changes.
You can modify this to change the messages in various ways, for example:
change the contents of certain message
void forward_change_to_B(struct message *message) { if (message->to == 'B') { strcpy(message->data, "This replaces the original message."); message->data_size = strlen("This replaces the original message."); } send_message(message); }
change the recipient or sender of certain message:
void forward_send_A_back_to_B(struct message *message) { if (message->to == 'B' && message->from == 'A') { message->to = 'A'; message->from = 'B'; } send_message(message); }
add a brand new message before the first message:
static int counter = 0; void forward_with_additional(struct message *message) { if (counter == 0) { send_message(new_message( 'A', 'B', "This is an additional message." false, false )) counter += 1; } send_message(message); }
1.1 Manipulating and creating messages
The struct message
is defined as follows:
struct message {
char from; // message sender
char to; // message recipient
bool is_encrypted;
bool is_signed; // if true, 'data' is signed using from's private key
char data[1024];
size_t data_size;
char signature[64]; // only used if is_signed is true
};
to
indicates where the network will attempt to deliver
the message when you can send_message()
.
Signed messages will have signature
set to some
meaningful data.
You can construct messages (including encrypting them) using the following utility function:
struct message *new_message(
char from,
char to,
const char *data, // NULL-terminated string
bool is_encrypted, // encrypt the message
bool is_signed // sign the message --- only supported if 'from' is 'M'
);
You can decrypt messages encrypted to M
using:
void decrypt_message_for_M(
const struct message *message,
char *buffer,
size_t buffer_size
);
which will copy the decrypted contents of the message into
buffer
(truncating the contents if the buffer_size is too
small).
Encrypting a message will public-key encrypt it to to
’s
public key. new_message
has a list of all the public keys
to do this.
If you manipulate the data
or data_size
of
an encrypted message directly or unset the is_encrypted
field of an existing message (instead of encrypting a new message to the
public key), then most likely the resulting message will be considered
malformed.
Signing a message will only work if from
is M;
new_message
does not have any other private keys.
2 The scenarios
2.1 Scenario 0
A->B [not encrypted]: PAY $1000 TO M
B->A [not encrypted]: PAID
A->B [not encrypted]: PAY $0438 TO M
B->A [not encrypted]: PAID
2.2 Scenario 1
A->B [encrypted to B's public key]: PAY $1000 TO M
B->A [encrypted to A's public key]: PAID
A->B [encrypted to B's public key]: PAY $0438 TO M
B->A [encrypted to A's public key]: PAID
2.3 Scenario 2
A->B [encrypted to B's public key, then signed by A's private key]: PAY $1000 TO M
B->A [encrypted to A's public key, then signed by B's private key]: PAID
A->B [encrypted to B's public key, then signed by A's private key]: PAY $0438 TO M
B->A [encrypted to A's public key, then signed by B's private key]: PAID
2.4 Scenario 3
B->A [encrypted to A's public key]: START [random number 1]
A->B [encrypted to B's public key]: [random number 1] PAY $1000 TO M
B->A [encrypted to B's public key]: [random number 1] PAID
B->A [encrypted to A's public key]: START [random number 2]
A->B [encrypted to B's public key]: [random number 2] PAY $0438 TO M
B->A [encrypted to B's public key]: [random number 2] PAID
B->A [encrypted to A's public key]: START [random number 3]
A->B [encrypted to B's public key]: [random number 3] NO MORE PAYMENTS FOR YOU
2.5 Scenario 4
B->A [encrypted to A's public key, then signed by B's private key]: START [random number 1]
A->B [encrypted to B's public key, then signed by A's private key]: [random number 1] PAY $1000 TO M
B->A [encrypted to A's public key, then signed by B's private key]: [random number 1] PAID
B->A [encrypted to A's public key, then signed by B's private key]: START [random number 2]
A->B [encrypted to B's public key, then signed by A's private key]: [random number 2] PAY $0438 TO M
B->A [encrypted to A's public key, then signed by B's private key]: [random number 1] PAID
B->A [encrypted to A's public key, then signed by A's private key]: START [random number 3]
A->B [encrypted to B's public key, then signed by A's private key]: [random number 3] NO MORE PAYMENTS FOR YOU