Disassembling C, replace operation code - c

I was trying to learn the basics of reversing and I tried to disassemble a small C program. I'm working under MacOS 10.7.2 (64 bit - Intel) and using gcc 4.2.1.
#include <stdio.h>
int main() {
char word[20];
scanf("%s",word);
if (strcmp(word,"password")==0)
printf("Correct\n");
else
printf("Fail\n");
}
I compiled with gcc -o test test.c, then, working a while with gdb, I placed a breakpoint after the strcmp call (100000e8e) and get (what I think is) the relevant assembly code:
0x0000000100000e40 <main+0>: push %rbp
0x0000000100000e41 <main+1>: mov %rsp,%rbp
0x0000000100000e44 <main+4>: sub $0x30,%rsp
0x0000000100000e48 <main+8>: mov 0x1e9(%rip),%rax # 0x100001038
0x0000000100000e4f <main+15>: mov (%rax),%rax
0x0000000100000e52 <main+18>: mov %rax,-0x8(%rbp)
0x0000000100000e56 <main+22>: lea -0x20(%rbp),%rax
0x0000000100000e5a <main+26>: mov %rax,%rcx
0x0000000100000e5d <main+29>: xor %dl,%dl
0x0000000100000e5f <main+31>: lea 0xda(%rip),%rsi # 0x100000f40
0x0000000100000e66 <main+38>: mov %rsi,%rdi
0x0000000100000e69 <main+41>: mov %rcx,%rsi
0x0000000100000e6c <main+44>: mov %rax,-0x28(%rbp)
0x0000000100000e70 <main+48>: mov %dl,%al
0x0000000100000e72 <main+50>: callq 0x100000eee <dyld_stub_scanf>
0x0000000100000e77 <main+55>: mov -0x28(%rbp),%rcx
0x0000000100000e7b <main+59>: xor %dl,%dl
0x0000000100000e7d <main+61>: lea 0xbf(%rip),%rsi # 0x100000f43
0x0000000100000e84 <main+68>: mov %rcx,%rdi
0x0000000100000e87 <main+71>: mov %dl,%al
0x0000000100000e89 <main+73>: callq 0x100000ef4 <dyld_stub_strcmp>
0x0000000100000e8e <main+78>: mov %eax,%ecx
0x0000000100000e90 <main+80>: cmp $0x0,%ecx
0x0000000100000e93 <main+83>: jne 0x100000ea6 <main+102>
0x0000000100000e95 <main+85>: lea 0xb0(%rip),%rax # 0x100000f4c
0x0000000100000e9c <main+92>: mov %rax,%rdi
0x0000000100000e9f <main+95>: callq 0x100000ee8 <dyld_stub_puts>
0x0000000100000ea4 <main+100>: jmp 0x100000eb5 <main+117>
0x0000000100000ea6 <main+102>: lea 0xa7(%rip),%rax # 0x100000f54
0x0000000100000ead <main+109>: mov %rax,%rdi
0x0000000100000eb0 <main+112>: callq 0x100000ee8 <dyld_stub_puts>
0x0000000100000eb5 <main+117>: mov -0xc(%rbp),%eax
0x0000000100000eb8 <main+120>: mov 0x179(%rip),%rcx # 0x100001038
0x0000000100000ebf <main+127>: mov (%rcx),%rcx
0x0000000100000ec2 <main+130>: mov -0x8(%rbp),%rdx
0x0000000100000ec6 <main+134>: cmp %rdx,%rcx
0x0000000100000ec9 <main+137>: mov %eax,-0x2c(%rbp)
0x0000000100000ecc <main+140>: jne 0x100000ed7 <main+151>
0x0000000100000ece <main+142>: mov -0x2c(%rbp),%eax
0x0000000100000ed1 <main+145>: add $0x30,%rsp
0x0000000100000ed5 <main+149>: pop %rbp
0x0000000100000ed6 <main+150>: retq
0x0000000100000ed7 <main+151>: callq 0x100000edc <dyld_stub___stack_chk_fail>
Now, according to my comprehension of assembler, things should be easy: after the strcmp call at 100000e89 the value of %ecx is saved and then compared with 0.
If they are not equal (jne) there is the jump (else), otherwise it should continue (if).
Very well, I thought modifying jne into a je I should get "Correct" even with wrong input.
Actually I didn't. Trying to understand what the problem may be, I tried to examine the operation codes and I get a strange (to me) output:
(gdb) x/8x 0x100000e93
0x100000e93 <main+83>: 0x8d481175 0x0000b005 0xc7894800 0x000044e8
0x100000ea3 <main+99>: 0x480feb00 0x00a7058d 0x89480000 0x0033e8c7
No sign of the 0f85 that should be the code for jne.
I'm a little bit confused then...Shouldn't I get the operation code for the jump? May someone explain me my error?

See opcode for JNE here:
http://ref.x86asm.net/coder64.html
So, it is what is expected:
0x8d481175
since its dword printed, and architecture is little endian,
you having following sequence of bytes starting from that address:
75 11 48 8d
and 75 is opcode for JNE in 64-bit mode with 8-bit rel offset.
Verification: a jump address is calculated from address of next instruction + offset.
So,
0x0000000100000e95 + 0x11 = 0x0000000100000eA6
which is exactly what gdb shows

Related

Overwriting the GOT entry for __stack_chk_fail in assembly via sprintf format string vulnerability and/or buffer overflow

I've got an NX enabled, canary enabled x64 ELF and can only view the assembly, not the source code but I do know its written in c. When run, it only accepts command line args and returns nothing. Inside of the main function there's only one function call of note, to evil;
0x000000000040060e <+0>: push rbp
0x000000000040060f <+1>: mov rbp,rsp
0x0000000000400612 <+4>: sub rsp,0x10
0x0000000000400616 <+8>: mov DWORD PTR [rbp-0x4],edi
0x0000000000400619 <+11>: mov QWORD PTR [rbp-0x10],rsi
0x000000000040061d <+15>: mov rax,QWORD PTR [rbp-0x10]
0x0000000000400621 <+19>: add rax,0x8
0x0000000000400625 <+23>: mov rax,QWORD PTR [rax]
0x0000000000400628 <+26>: mov rdi,rax
0x000000000040062b <+29>: call 0x4005a7 <evil>
0x0000000000400630 <+34>: mov eax,0x0
0x0000000000400635 <+39>: leave
0x0000000000400636 <+40>: ret
and inside evil, it pulls the command line args and checks... something... against 0xdeadbeef and if they match, passes a "you win!" message, then verifies the canary and if either of those fail, it runs call __stack_chk_fail#plt, which indirects through a GOT entry which is in writeable memory somewhere.
0x00000000004005a7 <+0>: push rbp
0x00000000004005a8 <+1>: mov rbp,rsp
0x00000000004005ab <+4>: sub rsp,0x70
0x00000000004005af <+8>: mov QWORD PTR [rbp-0x68],rdi
0x00000000004005b3 <+12>: mov rax,QWORD PTR fs:0x28
0x00000000004005bc <+21>: mov QWORD PTR [rbp-0x8],rax
0x00000000004005c0 <+25>: xor eax,eax
0x00000000004005c2 <+27>: mov DWORD PTR [rbp-0x54],0x0
0x00000000004005c9 <+34>: mov rdx,QWORD PTR [rbp-0x68]
0x00000000004005cd <+38>: lea rax,[rbp-0x50]
0x00000000004005d1 <+42>: mov rsi,rdx
0x00000000004005d4 <+45>: mov rdi,rax
0x00000000004005d7 <+48>: mov eax,0x0
0x00000000004005dc <+53>: call 0x4004b0 <sprintf#plt>
0x00000000004005e1 <+58>: mov eax,DWORD PTR [rbp-0x54]
0x00000000004005e4 <+61>: cmp eax,0xdeadbeef
0x00000000004005e9 <+66>: jne 0x4005f7 <evil+80>
0x00000000004005eb <+68>: lea rdi,[rip+0xd6] # 0x4006c8
0x00000000004005f2 <+75>: call 0x400490 <puts#plt>
0x00000000004005f7 <+80>: nop
0x00000000004005f8 <+81>: mov rax,QWORD PTR [rbp-0x8]
0x00000000004005fc <+85>: xor rax,QWORD PTR fs:0x28
0x0000000000400605 <+94>: je 0x40060c <evil+101>
0x0000000000400607 <+96>: call 0x4004a0 <__stack_chk_fail#plt>
0x000000000040060c <+101>: leave
0x000000000040060d <+102>: ret
In ghidra and with cyclic strings I'm able to verify that the buffer is 72 characters. I've found a bunch of old info from liveoverflow that's about 5 years old now with the exact same problem (protostar format0), except his buffer is 64. For some reason, this buffer mismatch is causing me all sorts of problems I believe.
I've tried hundreds of inputs to achieve the winning statement;
I've tried overwriting the buffer of 72 with 72 A's followed by variations of 0xdeadbeef such as little endian, strings, hex, etc
I've played around with the buffer and offset, so for example putting 0xdeadbeef and then the buffer after, or putting 72 A's with a nop sled of 8 or so after it then 0xdeadbeef
I've tried following liveoverflow's method of overwriting __stack_chk_fail's GOT entry completely, via a format-string vulnerability like %1640d which you can see here, but either have the wrong numbers or am misunderstanding how it works/if it will work on my binary and machine
None of these have given me the winning statement, and I'd really like to understand the why and how and the assembly reasoning behind it.
edit
I've noticed that if I pass python -c 'print ("%x."*5)' as an argument, my stack fills up with ffffe374.f7fa3658.4006b0.f7fcbb60.0, which repeats if I up the number from 5. When checking these with x 0x4006b0, 0x4006b0 is the only value that reuturns anything of use - 0x4006b0 <__libc_csu_fini>: "\363", <incomplete sequence \303>. I've tried creating a rop chain using this and looking up ret2csu guides but I've had no luck so far.

Basic buffer overflow tutorial

I'm learning about basic buffer overflows, and I have the following C code:
int your_fcn()
{
char buffer[4];
int *ret;
ret = buffer + 8;
(*ret) += 16;
return 1;
}
int main()
{
int mine = 0;
int yours = 0;
yours = your_fcn();
mine = yours + 1;
if(mine > yours)
printf("You lost!\n");
else
printf("You won!\n");
return EXIT_SUCCESS;
}
My goal is to bypass the line mine = yours + 1;, skip straight to the if statement comparison, so I can "win". main() cannot be touched, only your_fcn() can.
My approach is to override the return address with a buffer overflow. So in this case, I identified that the return address should be 8 bytes away from buffer, since buffer is 4 bytes and EBP is 4 bytes. I then used gdb to identify that the line I want to jump to is 16 bytes away from the function call. Here is the result from gdb:
(gdb) disassemble main
Dump of assembler code for function main:
0x0000054a <+0>: lea 0x4(%esp),%ecx
0x0000054e <+4>: and $0xfffffff0,%esp
0x00000551 <+7>: pushl -0x4(%ecx)
0x00000554 <+10>: push %ebp
0x00000555 <+11>: mov %esp,%ebp
0x00000557 <+13>: push %ebx
0x00000558 <+14>: push %ecx
0x00000559 <+15>: sub $0x10,%esp
0x0000055c <+18>: call 0x420 <__x86.get_pc_thunk.bx>
0x00000561 <+23>: add $0x1a77,%ebx
0x00000567 <+29>: movl $0x0,-0xc(%ebp)
0x0000056e <+36>: movl $0x0,-0x10(%ebp)
0x00000575 <+43>: call 0x51d <your_fcn>
0x0000057a <+48>: mov %eax,-0x10(%ebp)
0x0000057d <+51>: mov -0x10(%ebp),%eax
0x00000580 <+54>: add $0x1,%eax
0x00000583 <+57>: mov %eax,-0xc(%ebp)
0x00000586 <+60>: mov -0xc(%ebp),%eax
0x00000589 <+63>: cmp -0x10(%ebp),%eax
0x0000058c <+66>: jle 0x5a2 <main+88>
0x0000058e <+68>: sub $0xc,%esp
0x00000591 <+71>: lea -0x1988(%ebx),%eax
I see the line 0x00000575 <+43>: call 0x51d <your_fcn> and 0x00000583 <+57>: mov %eax,-0xc(%ebp) are four lines away from each other, which tells me I should offset ret by 16 bytes. But the address from gdb says something different. That is, the function call starts on 0x00000575 and the line I want to jump to is on 0x00000583, which means that they are 15 bytes away?
Either way, whether I use 16 bytes or 15 bytes, I get a segmentation fault error and I still "lose".
Question: What am I doing wrong? Why don't the address given in gdb go by 4 bytes at a time and what's actually going on here. How can I correctly jump to the line I want?
Clarification: This is being done on a x32 machine on a VM running linux Ubuntu. I'm compiling with the command gcc -fno-stack-protector -z execstack -m32 -g guesser.c -o guesser.o, which turns stack protector off and forces x32 compilation.
gdb of your_fcn() as requested:
(gdb) disassemble your_fcn
Dump of assembler code for function your_fcn:
0x0000051d <+0>: push %ebp
0x0000051e <+1>: mov %esp,%ebp
0x00000520 <+3>: sub $0x10,%esp
0x00000523 <+6>: call 0x5c3 <__x86.get_pc_thunk.ax>
0x00000528 <+11>: add $0x1ab0,%eax
0x0000052d <+16>: lea -0x8(%ebp),%eax
0x00000530 <+19>: add $0x8,%eax
0x00000533 <+22>: mov %eax,-0x4(%ebp)
0x00000536 <+25>: mov -0x4(%ebp),%eax
0x00000539 <+28>: mov (%eax),%eax
0x0000053b <+30>: lea 0xc(%eax),%edx
0x0000053e <+33>: mov -0x4(%ebp),%eax
0x00000541 <+36>: mov %edx,(%eax)
0x00000543 <+38>: mov $0x1,%eax
0x00000548 <+43>: leave
0x00000549 <+44>: ret
x86 has variable length instructions, so you cannot simply count instructions and multiply by 4. Since you have the output from gdb, trust it to determine the address of each instruction.
The return address from the function is the address after the call instruction. In the code shown, this would be main+48.
The if statement starts at main+60, not main+57. The instruction at main+57 stores yours+1 into mine. So to adjust the return address to return to the if statement, you should add 12 (that is, 60 - 48).
Doing that skips the assignments to both yours and mine. Since they are both initialized to 0, it will print "You won".

Multiplication overflow in C

I'm doing some security CTF practice and have this problem which I am stuck on. This is the C source code of the compiled program:
int main(int i, long **a) {
if(*a[1] * 0x1064deadbeef4601u == 0xd1038d2e07b42569u)
//exec shell
return 0;
}
Things confusing me:
long** main argument won't compile when I turn off gcc flags so I can't reproduce problem on my computer. Is this a different compiler being used? Compiled program runs fine on CTF server.
program repeatedly overflows during multiplication before testing equality. I tried using a linear congruence equation (mod 2^64) and extended euclidian algorithm to find the required x input but this did not work for me.
Can someone help me out with this? I'm trying to find *a[1], the correct program argument.
disassembling main in gdb gives:
(gdb) disas main
Dump of assembler code for function main:
0x000000000040050c <+0>: push %rbp
0x000000000040050d <+1>: mov %rsp,%rbp
0x0000000000400510 <+4>: sub $0x10,%rsp
0x0000000000400514 <+8>: mov %edi,-0x4(%rbp)
0x0000000000400517 <+11>: mov %rsi,-0x10(%rbp)
0x000000000040051b <+15>: mov -0x10(%rbp),%rax
0x000000000040051f <+19>: add $0x8,%rax
0x0000000000400523 <+23>: mov (%rax),%rax
0x0000000000400526 <+26>: mov (%rax),%rax
0x0000000000400529 <+29>: mov %rax,%rdx
0x000000000040052c <+32>: movabs $0x1064deadbeef4601,%rax
=> 0x0000000000400536 <+42>: imul %rax,%rdx
0x000000000040053a <+46>: movabs $0xd1038d2e07b42569,%rax
0x0000000000400544 <+56>: cmp %rax,%rdx
0x0000000000400547 <+59>: jne 0x400562 <main+86>
0x0000000000400549 <+61>: mov $0x0,%edx
0x000000000040054e <+66>: mov $0x40061c,%esi
0x0000000000400553 <+71>: mov $0x40061f,%edi
0x0000000000400558 <+76>: mov $0x0,%eax
0x000000000040055d <+81>: callq 0x4003f0 <execl#plt>
0x0000000000400562 <+86>: mov $0x0,%eax
0x0000000000400567 <+91>: leaveq
0x0000000000400568 <+92>: retq
End of assembler dump.
There's no real overflow here -- it's just doing a multiply mod 264 and testing that the result is what is expected. To figure out the desired input, you just need to find the inverse (mod 264) of the factor 0x1064deadbeef4601, and multiply it by 0xd1038d2e07b42569
For power-of-2 modulus, it's generally easiest to find the inverse using Euler's formula:
x-1 (mod m) ≡ xφ(m)-1 (mod m)
when m is a power of two, φ(2k) = 2k-1, so you can calculate this with just 2(k-1) multiplies.

Assembly Code to C Code (Add Instruction Issues)

So for my class project I am given a binary and what I have to do is bypass the security authentication. Now, once you change the binary to bypass the authentication, you have to create a C program that will replicate the "same" binary. Now, I have been struggling to understand what this area of the assembly code dump that I obtained does.
0x08048a59 <function8+54>: mov 0x8049e50,%eax
0x08048a5e <function8+59>: add $0x4,%eax
0x08048a61 <function8+62>: mov (%eax),%eax
0x08048a63 <function8+64>: movl $0x8048cd4,0x4(%esp)
I'm not very familiar with assembly, but I got most of it figured out. This is the original/entire assembly dump that I got using GDB.
0x08048a23 <function8+0>: push %ebp
0x08048a24 <function8+1>: mov %esp,%ebp
0x08048a26 <function8+3>: sub $0x28,%esp
0x08048a29 <function8+6>: movl $0xd6a1a,-0x18(%ebp)
0x08048a30 <function8+13>: mov 0x8(%ebp),%eax
0x08048a33 <function8+16>: mov %eax,-0x14(%ebp)
0x08048a36 <function8+19>: mov 0xc(%ebp),%eax
0x08048a39 <function8+22>: mov %eax,-0x10(%ebp)
0x08048a3c <function8+25>: movl $0x0,-0xc(%ebp)
0x08048a43 <function8+32>: mov -0xc(%ebp),%eax
0x08048a46 <function8+35>: mov %eax,%edx
0x08048a48 <function8+37>: sar $0x1f,%edx
0x08048a4b <function8+40>: idivl -0x18(%ebp)
0x08048a4e <function8+43>: imul -0x14(%ebp),%eax
0x08048a52 <function8+47>: imul -0x10(%ebp),%eax
0x08048a56 <function8+51>: mov %eax,-0xc(%ebp)
0x08048a59 <function8+54>: mov 0x8049e50,%eax
0x08048a5e <function8+59>: add $0x4,%eax
0x08048a61 <function8+62>: mov (%eax),%eax
0x08048a63 <function8+64>: movl $0x8048cd4,0x4(%esp)
0x08048a6b <function8+72>: mov %eax,(%esp)
0x08048a6e <function8+75>: call 0x8048434 <strcmp#plt>
0x08048a73 <function8+80>: test %eax,%eax
0x08048a75 <function8+82>: jne 0x8048a8d <function8+106>
0x08048a77 <function8+84>: mov $0x8048cdc,%eax
0x08048a7c <function8+89>: mov -0xc(%ebp),%edx
0x08048a7f <function8+92>: mov %edx,0x4(%esp)
0x08048a83 <function8+96>: mov %eax,(%esp)
0x08048a86 <function8+99>: call 0x8048414 <printf#plt>
0x08048a8b <function8+104>: jmp 0x8048a99 <function8+118>
0x08048a8d <function8+106>: movl $0x8048cfa,(%esp)
0x08048a94 <function8+113>: call 0x8048424 <puts#plt>
0x08048a99 <function8+118>: mov -0xc(%ebp),%eax
0x08048a9c <function8+121>: leave
0x08048a9d <function8+122>: ret
And so far I have managed to convert it to this in C:
int function8(one, two){
int a = 879130;
int b = one;
int c = two;
int d = 0;
d = (d / a * b * c);
if(strcmp(b, (d + 4)) == 0){
printf("You may enter using token %d", d);
}
else{
puts("You may not enter.");
}
return d;
}
int main(){
int one, two = 0;
function8(one, two);
}
I am know that
0x08048a59 <function8+54>: mov 0x8049e50,%eax
and
0x08048a63 <function8+64>: movl $0x8048cd4,0x4(%esp)
are pointing to a particular address and value respectively (correct me if I'm wrong), but don't know if I have to call it or not. If so, how do I call that particular address?
Any help would be appreciated!
Just in case you guys need it, this is my current output when I create a binary using GCC and then run GDB to get the dump:
0x08048434 <function8+0>: push %ebp
0x08048435 <function8+1>: mov %esp,%ebp
0x08048437 <function8+3>: sub $0x28,%esp
0x0804843a <function8+6>: movl $0xd6a1a,-0x18(%ebp)
0x08048441 <function8+13>: mov 0x8(%ebp),%eax
0x08048444 <function8+16>: mov %eax,-0x14(%ebp)
0x08048447 <function8+19>: mov 0xc(%ebp),%eax
0x0804844a <function8+22>: mov %eax,-0x10(%ebp)
0x0804844d <function8+25>: movl $0x0,-0xc(%ebp)
0x08048454 <function8+32>: mov -0xc(%ebp),%eax
0x08048457 <function8+35>: mov %eax,%edx
0x08048459 <function8+37>: sar $0x1f,%edx
0x0804845c <function8+40>: idivl -0x18(%ebp)
0x0804845f <function8+43>: imul -0x14(%ebp),%eax
0x08048463 <function8+47>: imul -0x10(%ebp),%eax
0x08048467 <function8+51>: mov %eax,-0xc(%ebp)
0x0804846a <function8+54>: mov -0xc(%ebp),%eax
0x0804846d <function8+57>: add $0x4,%eax
0x08048470 <function8+60>: mov %eax,0x4(%esp)
0x08048474 <function8+64>: mov -0x14(%ebp),%eax
0x08048477 <function8+67>: mov %eax,(%esp)
0x0804847a <function8+70>: call 0x8048364 <strcmp#plt>
0x0804847f <function8+75>: test %eax,%eax
0x08048481 <function8+77>: jne 0x8048499 <function8+101>
0x08048483 <function8+79>: mov $0x80485a0,%eax
0x08048488 <function8+84>: mov -0xc(%ebp),%edx
0x0804848b <function8+87>: mov %edx,0x4(%esp)
0x0804848f <function8+91>: mov %eax,(%esp)
0x08048492 <function8+94>: call 0x8048344 <printf#plt>
0x08048497 <function8+99>: jmp 0x80484a5 <function8+113>
0x08048499 <function8+101>: movl $0x80485bd,(%esp)
0x080484a0 <function8+108>: call 0x8048354 <puts#plt>
0x080484a5 <function8+113>: mov -0xc(%ebp),%eax
0x080484a8 <function8+116>: leave
0x080484a9 <function8+117>: ret
strcmp compares two strings that are passed in as pointers. The code here :
0x08048a59 <function8+54>: mov 0x8049e50,%eax
0x08048a5e <function8+59>: add $0x4,%eax
0x08048a61 <function8+62>: mov (%eax),%eax
0x08048a63 <function8+64>: movl $0x8048cd4,0x4(%esp)
0x08048a6b <function8+72>: mov %eax,(%esp)
0x08048a6e <function8+75>: call 0x8048434 <strcmp#plt>
is passing two pointers to strcmp, both of which are static/global data (not local, like you have in your C code). One is at 0x8048cd4 (that's the address of the string.. it's probably something in quotes : "example"). The other is a pointer load + 4 that's dereferenced. I'd recommend : (1) look at those addresses to see what's stored in them, and (2) if you're confused by the assembly pointer chasing, try writing simple C programs that call strcmp and seeing the resulting assembly.
good luck.
This is the part who is calling the strcmp as drivingon9 pointed.
0x08048a59 <function8+54>: mov 0x8049e50,%eax
0x08048a5e <function8+59>: add $0x4,%eax
0x08048a61 <function8+62>: mov (%eax),%eax
0x08048a63 <function8+64>: movl $0x8048cd4,0x4(%esp)
0x08048a6b <function8+72>: mov %eax,(%esp)
0x08048a6e <function8+75>: call 0x8048434 <strcmp#plt>
The result value of the strcmp will be stored in the register EAX.
Thats why we have a test eax,eax in the code bellow:
0x0804847f <function8+75>: test %eax,%eax
0x08048481 <function8+77>: jne 0x8048499 <function8+101>
0x08048483 <function8+79>: mov $0x80485a0,%eax
0x08048488 <function8+84>: mov -0xc(%ebp),%edx
The test eax, eax tests if eax register is equal to 0.
i'm not sure what part of the code will let you do what you want, but you can try change the line
0x08048481 <function8+77>: jne 0x8048499 <function8+101>
and change the instruction to a je(JUMP IF EQUAL) or a incondicional jump(JMP)

Attempting a buffer overflow

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.

Resources