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.