Changelog:
- 3 Nov 2023: update securelab.tar for this semester
- 6 Nov 2023: remove notes about old versions of securelab.tar
- 6 Nov 2023: for scenario 3 request that B not think A sent too many payments
- 12 Nov 2023: hopefully be more clear about
./lab X attack
being what you have to getworking
Possibly working with a partner or two —
Download securelab.tar [last updated 3 November] 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 without 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.)When you’re successful, this should be reflected in the output
./lab 0 attack
.Do the same for scenario
1
andforward_attack_1
. You can examine what scenario 1 is with./lab 1 normal
and run your attack on it with
./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.
Do the same for scenario 3 as scenario 2 without causing B to think A sent too many payment requests.
(optional) If you have time, do the same as scenario 4 for scenarios 2 and 3. 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.If you submit the lab, we expect scenarios 1-3 to be complete.
If you checkoff the lab, we will give credit if you do not complete scenario 3 but have significant progress by the end of the lab.
1 Our simulation API
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
A->B [encrypted to B's public key, then signed by A's private key]: PAY $0001 TO M
B->A [encrypted to A's public key, then signed by B's private key]: PAID #1 OF UP TO 3
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 #2 OF UP TO 3
A->B [encrypted to B's public key, then signed by A's private key]: PAY $0437 TO M
B->A [encrypted to A's public key, then signed by B's private key]: PAID #3 OF UP TO 3
2.5 Scenario 4
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.6 Scenario 5
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