diff options
Diffstat (limited to 'CamilStaps-s4498062-Assignment-3/ex3')
-rw-r--r-- | CamilStaps-s4498062-Assignment-3/ex3 | 98 |
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. + |