3a. Shadow Stack Examples - TJAndHisStudents/Griffin-Trace GitHub Wiki

Previous Steps

If you just found your way to this page first, you can view the previous steps before diving into reviewing the Griffin Trace results:

Collecting Parseable Traces

Change your working directory to ./griffin-tests/, then run the annotate-shadow-stack.sh bash script.

Annotated Traces

When viewing the traces, you'll receive all of the basic blocks in the order that they were executed. These blocks will also note when PLT trampolines and system calls are executed.

Start of trace:

process: tgid=1430, cmd=attack-return.out
thread: tgid=1430, pid=1430
xpage: tgid=1430, base=400000, size=1000
xpage: tgid=1430, base=7ffff7ddc000, size=20000
xpage: tgid=1430, base=7ffff7ffa000, size=2000
xpage: tgid=1430, base=7ffff7a33000, size=3a9000
buffer: pid=1430, size=960
  PSB packet found
  block: 7ffff7ddd190
  block: 7ffff7de0750
  [...]

Identifying system calls in the trace:

  [...]
  block: 7ffff7b0c619
  syscall: 7ffff7b0c620
  block: 7ffff7b0c620
  [...]

Program main function:

  [...]
  block: 7ffff7a54b01
  block: 40055e : main
  block: 400420
  block: 400426
  block: 400400
  block: 7ffff7df02f0
  [...]

Calling trampoline function from main - where the violation occurs. The program expects to exit here, but instead calls trampoline due to overwriting the return address with an unintended function:

  [...]
  block: 7ffff7a9eb05
  block: 400578
  block: 400546 : trampoline : Shadow Stack Violation
  block: 400420
  block: 7ffff7a9e9f0
  [...]

The reason that trampoline is called is due to the exploit in the code, in which the return address is overwritten with the trampoline address.

In this case, the address to the first local variable, i, is pulled into the ptr variable, which is then incremented to move the pointer to the location of the return address on the stack. This works because the stack starts at the high end of the address space (0xff..ff) and moves downwards, so the return address is stored at a higher address than the variables in the main() function. Then, the function pointer at this instruction is overwritten by the trampoline function pointer, so when main() exits, the trampoline function is called instead of the intended return address.

int main() {
	unsigned long i;
	unsigned long *ptr = &i;

	ptr = ptr + 3;
	*ptr = (unsigned long) trampoline;

	return 0;
}

Debugging

If you run into any issues with the Griffin traces, you can check the dmesg output for debug information. The expected debug information is provided at Debugging Griffin Trace.