aboutsummaryrefslogtreecommitdiff
path: root/CamilStaps-s4498062-Assignment-3/ex3
diff options
context:
space:
mode:
Diffstat (limited to 'CamilStaps-s4498062-Assignment-3/ex3')
-rw-r--r--CamilStaps-s4498062-Assignment-3/ex398
1 files changed, 98 insertions, 0 deletions
diff --git a/CamilStaps-s4498062-Assignment-3/ex3 b/CamilStaps-s4498062-Assignment-3/ex3
new file mode 100644
index 0000000..d1da7dc
--- /dev/null
+++ b/CamilStaps-s4498062-Assignment-3/ex3
@@ -0,0 +1,98 @@
+a
+ $ ./auth0 $(python -c 'print "A"*512')
+ ERROR: password incorrect
+ Segmentation fault
+
+b
+ With optimisation, `correct` isn't really a variable anymore. The
+ compiler recognises the moments it can change (i.e. at definition time
+ and after the strcmp), and when it is used (the last if), and manages
+ to do the same without the variable.
+
+ It now uses code similar to the following control flow:
+
+ if (strcmp(hash1,hash2) == 0) {
+ printf("Starting root shell\n");
+ setuid(0);
+ setgid(0);
+ system("/bin/sh");
+ } else {
+ printf("ERROR: password incorrect\n");
+ }
+
+ So, `correct` has been eliminated. Therefore, the buffer overflow
+ attack doesn't work anymore: there is no variable to overwrite.
+
+ We can see this in auth3.s. The `bool correct = false;` statement doesn't
+ translate to anything in assembly, while it does in auth0.s.
+
+ More interestingly, we can see the control flow:
+
+ 19:auth.c **** if (strcmp(hash1,hash2) == 0) {
+ 99 .loc 1 19 0
+ 100 001f BF000000 mov edi, OFFSET FLAT:.LC1 # tmp90,
+ 100 00
+ 101 0024 B96B0000 mov ecx, 107 # tmp91,
+ 101 00
+ 102 0029 4889C6 mov rsi, rax # hash1, hash1
+ 103 .LBE7:
+ 104 002c F3A6 repz cmpsb
+ 105 .LVL4:
+ 106 002e 7530 jne .L6 #,
+ 107 .LVL5:
+ 108 .LBB8:
+ 109 .LBB9:
+ 20:auth.c **** correct = true;
+ 21:auth.c **** } else {
+ 22:auth.c **** printf("ERROR: password incorrect\n");
+ 23:auth.c **** }
+ 24:auth.c ****
+ 25:auth.c **** if (correct) {
+ 26:auth.c **** printf("Starting root shell\n");
+ 110 .loc 1 26 0
+ 111 0030 BF000000 mov edi, OFFSET FLAT:.LC3 #,
+ 111 00
+ 112 0035 E8000000 call puts #
+ 112 00
+ 113 .LVL6:
+ 27:auth.c **** setuid(0);
+ 114 .loc 1 27 0
+ 115 003a 31FF xor edi, edi #
+ 116 003c E8000000 call setuid #
+ 116 00
+
+ The jump in 106 goes to .L6 where the error printf assembly is stored:
+
+ 22:auth.c **** }
+ 138 .loc 1 22 0
+ 139 0060 BF000000 mov edi, OFFSET FLAT:.LC2 #,
+ 139 00
+ 140 0065 E8000000 call puts #
+ 140 00
+
+ If we don't jump (i.e. if the result from strcmp was 0), we continue in
+ .LVL5, .LBB8, .LBB9 where we printf "Starting root shell" and after do
+ the set{u,g}id stuff and start the root shell.
+
+ In auth0.s we see a control flow that stays much closer to the C code.
+ There is an actual variable `correct` which is set if the strcmp returns 0:
+
+ 19:auth.c **** if (strcmp(hash1,hash2) == 0) {
+ 76 .loc 1 19 0
+ 77 004f 488B55E8 mov rdx, QWORD PTR [rbp-24] # tmp88, hash2
+ 78 0053 488B45F0 mov rax, QWORD PTR [rbp-16] # tmp89, hash1
+ 79 0057 4889D6 mov rsi, rdx #, tmp88
+ 80 005a 4889C7 mov rdi, rax #, tmp89
+ 81 005d E8000000 call strcmp #
+ 81 00
+ 82 0062 85C0 test eax, eax # D.3514
+ 83 0064 7506 jne .L2 #,
+ 20:auth.c **** correct = true;
+ 84 .loc 1 20 0
+ 85 0066 C645FF01 mov BYTE PTR [rbp-1], 1 # correct,
+
+ Finally: the compiler changes the control flow because it is more efficient.
+ In this case, I think, it is both timewise and spacewise more efficient: we
+ store one bool less, and also don't lose time in setting and checking its
+ value.
+