Posted:

21 May 2024

Thu 30 May @ 18:00

In this assignment, you’ll examine some of the software security features that we discussed in the first module of the course.

Warning

GAI statement:

You may use grammar assistants such as Grammarly and generative AI tools such as LLMs when wording your answers to this assignment. However, the intellectual input must be your own. For example, you can ask an LLM how to rephrase an example of a security policy, but the example itself must come from your own brain. All students must declare any such tools they have used at the beginning of their assignment, e.g.:

Activity Tools / assistance

Search

DuckDuckGo

Ideation

none

Calculation

GNU bc, Calculator.app (Programmer mode)

Grammar

Grammarly, classmates in the course

Debugging

GitHub Copilot (prompts and responses in Appendix A)

Other

none

If you choose to use GAI tools, you must provide an appendix to this assignment that shows all your prompts and their respective responses.

  1. A family member asks you, "is my computer secure?" Answer this question at two levels of depth.

  2. Give an example of two security policies that might be required for electronics in a vehicle.

  3. Explain the following terms, using an example for each (not one I gave in class):

    1. Threat

    2. Vulnerability

    3. Adversary

    4. Attack

  4. Give an example other than one given in the course notes of a leaky abstraction.

  5. Explain, with reference to a diagram (which may be hand-drawn or computer-generated) how return-oriented programming can be used to defeat the W^X policy.

  6. Assume the following C program has been compiled:

    #include <stdio.h>
    #include <string.h>
    
    void foo(const int data[], int len)
    {
    	printf("data len: %d\n", len);
    	printf("data: %d %d\n", data[0], data[1]);
    }
    
    void bar(const char *buffer, int len)
    {
    	const int *integers = (int*) buffer;
    
    	foo(integers, len / 4);
    }
    
    int main()
    {
    	char message[16];
    	strcpy(message, "Hello, world!\n");
    	int len = strnlen(message, sizeof(message));
    
    	bar(message, len);
    
    	return 0;
    }

    yielding the following symbols as revealed by nm(1):

    0000000000201670 T _start
    00000000002019f0 T bar
    00000000002019b0 T foo
    0000000000201960 T main

    This program is then executed in a debugger, with execution paused at the beginning of the bar function. At that point, the contents of (a portion of) the stack are:

    0x7fffffffe900: 60 e9 ff ff 04 00 00 00 60 e9 ff ff ff 7f 00 00  `.......`.......
    0x7fffffffe910: 40 e9 ff ff ff 7f 00 00 dd 19 20 00 00 00 00 00  @......... .....
    0x7fffffffe920: 30 2b 20 00 00 00 00 00 60 e9 ff ff ff 7f 00 00  0+ .....`.......
    0x7fffffffe930: 30 2b 20 00 10 00 00 00 60 e9 ff ff ff 7f 00 00  0+ .....`.......
    0x7fffffffe940: 80 e9 ff ff ff 7f 00 00 99 19 20 00 00 00 00 00  .......... .....
    0x7fffffffe950: e8 e9 ff ff ff 7f 00 00 60 e9 ff ff ff 7f 00 00  ........`.......
    0x7fffffffe960: 48 65 6c 6c 6f 2c 20 77 6f 72 6c 64 21 0a 00 00  Hello, world!...
    0x7fffffffe970: e8 e9 ff ff ff 7f 00 00 01 00 00 00 00 00 00 00  ................
    0x7fffffffe980: c0 e9 ff ff ff 7f 00 00 70 17 20 00 00 00 00 00  ........p. .....
    1. What is the address of message?

    2. To what address will the program return when leaving bar?

    3. If buffer were to be overflowed, which function’s return would be affected?

    4. How many integers could foo write into data without overwriting any return addresses?

ENGI 9823 only

  1. Explain, with reference to a diagram, one mechanism that can be used to defend against stack smashing.

  2. The file vulnerable.c describes a program that loads a data file, checks to see whether a user can prove their identity with a password [1] and then, if access is granted, reports the sum of the bytes in the file. You can compile this file with debugging information and stack canaries, then run it with an input data file, using the following Unix commands:

    $ cc -g -fstack-protector-all vulnerable.c -o hackme
    $ echo "hi" > stuff.dat
    $ ./hackme stuff.dat
    1. Using the nm(1) program, identify the offsets of the main, login and sum_bytes functions, as well as the static SECRET variable declared in login.

    2. Using your favourite debugger, pause the execution of the program just before the fread(3) call within sum_bytes. Identify the contents of the stack from the main function’s argc and argv down to the local variables of sum_bytes (using, e.g., mem read $sp $sp+0x100 in LLDB). Visualize these results using an image of your stack similar to that seen in lecture 3.

    3. Repeat the stack memory printing from the above question in a fresh execution of the hackme program. Compare the stack contents from the two executions (e.g., using a combination of wdiff and colordiff, or a graphical tool such as MELD), paying particular attention to the memory between the stack frames of sum_bytes_ and main, as well as between main and the code that invokes it. What is the value of the stack canary used in each invocation?

    4. Suppose that an attacker needed to find a ROP gadget to pop a value from the stack into the register rbp (opcode: 5d). Find an instance of such a gadget (5d followed by a ret instruction, i.e., opcode c3) within your hackme binary. Explain how you found it.


1. The approach to password validation in this example is terrible! We will learn about how passwords should actually be validated in the next module on host security.