Evil code
#11
(02-10-2016, 12:21 AM)xoft Wrote: Tell me, Linux enthisiasts, how does this reflect the "Linux is safer" mantra that I hear oh so often? How could Linux be so safe if the default configuration allows programs to execute data, and thus is very prone to buffer over-/underflow attack vectors?

OK, I will answer you =) But before reading my explanation, try to answer by yourself: is there really any harm in executing read-only data?

Look at code. "main" is defined as const. That this means? Lets dig into C2011 standart, paragraph 6.7.3:
Quote:If an attempt is made to modify an object defined with a const-qualified type through use of an lvalue with non-const-qualified type, the behavior is undefined.
The implementation may place a const object that is not volatile in a read-only region of
storage. Moreover, the implementation need not allocate storage for such an object if its address is
never used.
So, basically, object with const modifier can't be directly modified through C code and that's all; anything beyond is up to compiler/OS. So, let's see how different toolchain handles this.
Simple code example:
Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

const char * const msg = "Hello, world!";

int main(int argc, char *argv[])
{
 memset(msg, 'T',strlen(msg));
 puts(msg);
 return 0;
}
What we doing here? We declared constant pointer to constant string ang trying to modify it with memset. Very strange, yes, but what this yields to?
Windows 7 64-bit, MSVC 14.0 (from Visual Studio 13, if I remember correctly) compiles code fine, but gives a warning about mismatch of types because memset isn't expect to take const memory location. If we a trying to run code, this is what we will get:
Code:
TTTTTTTTTTTTT
Working as we expected to. But is it secure? No. We can only assume that object is can't be modified by direct assigninment to it and nothing more. Word-by-word interpretation of standart. Someone who isn't familiar with standart (and most C coders isn't) can place something valuable, like RSA key, into const object and assume it's consistency. Very stupid thing to do, but I've seen that.
Now, Linux. Kernel 4.5.0 x86-64, GCC 4.9.3. Compiler emits the same warning about const qualifier, but code compiles. Let's run it:
Code:
Segmentation fault
What happened? Program crashed. Why? Well, it's all about const. By default, GCC on Linux put const objects in .rodata section. Then program is being loaded by dynamic linker this section goes into CODE segment on x86/x86-64. And, of course, it's read-only. But we are trying to modify it. That emits to instant program abort: you can't modify code. To the question "why const objects put together with code" I can't give resolutive answer. Maybe it's because speed: then code references const object in case both .text (code) and .rodata sections put in one x86 segment there will be no need to several segment checks (check for boundary, check for rights, etc) that are performed by CPU because all access goes within same segment. Maybe it's because there are only two segments for user programs by default in Linux x86/x86-64, code (read-only, executable) and data (read-write, not executable) and authors of dynamic linker decided to put .rodata in read-only region. But it's okay, because C2011 standart allows to do so.
Now, I can answer your second question:

(02-10-2016, 12:21 AM)xoft Wrote: On Windows, the original code doesn't work simply because the "code" for main is stored within the data segment, which is non-executable by default, thus making it safe. Why doesn't this happen on Linux, too?
Because on Linux .data section is modifiable. By the way, on Windows too, as you can see. Windows loads read-only data into DATA x86 segment, thus not restricting it to change. So, that is better - allow read-only data to be changed (Windows way) or to be executed (Linux-way)? I think Linux way is better, because answer to my question ("Is there any harm in executing read-only data?") is no. This data isn't going to change as program executes. It just can't. No stack or buffer overflows can do any harm to read-only segment. And there is no way in mistakingly run variable, don't you think? But this is very simple to modify read-only data by mistake. Linux will not allow you to do that. Windows will. Refer to my example with RSA key. It's bad practice, but it will work on Linux. On Windows someone can modify your key with buffer over/underflow. So, is Linux safer or not? =)
Reply
Thanks given by:
#12
This is all fine as long as you have *const* data. The problem is, normally you don't. Quite a few programs have a global "buffer" object of a kind that they use for whatever buffering is needed, naturally it's stored in the writable data section. But on Windows, this still means code cannot execute from that location, so attacks cannot simply stuff data into that buffer and make it execute. On Linux, such an attack vector (oh so common for all the zero-day exploits) is perfectly viable. That, in my opinion, is very unsafe.
Reply
Thanks given by:
#13
I wonder if there's a technical justification for this decision.
Reply
Thanks given by:
#14
(02-12-2016, 05:12 AM)xoft Wrote: This is all fine as long as you have *const* data. The problem is, normally you don't. Quite a few programs have a global "buffer" object of a kind that they use for whatever buffering is needed, naturally it's stored in the writable data section. But on Windows, this still means code cannot execute from that location, so attacks cannot simply stuff data into that buffer and make it execute. On Linux, such an attack vector (oh so common for all the zero-day exploits) is perfectly viable. That, in my opinion, is very unsafe.
Try to remove const qualifier from your code and see what happens. I will predict you: Segmentation fault. Why? Well, try to figure by yourself, it's not very interesting to read someone's ready explanation, don't you think? =D You can find an answer by looking at readelf output on binary; pay attention to the section main belongs in both cases and figure out where each section is loaded in memory, especially to which segment.
In short, there is no way to run modifiable data as code on Linux. You can run constant data, and it's okay: that data is defined on compilation time and can even be viewed as a part of code itself. It can't be modified. And you can have read-write data, but you will not be able to run it. Never. Yes, there are some tricks to surpass that (mremap), but it must be done by application manually. In Windows there too are some techniques for that as I remember (VirtualProtect or so), but I can't say exactly, I never done it on Windows.
To the last: Why do you really though that people so experienced as Linux kernel and Glibc developers can be stupid enough to allow execution of dynamically changeable data? It's very unclear for me...
Reply
Thanks given by:
#15
Sorry to disappoint your overconfidence:
Code:
xoft@akima:~$ gcc --version
gcc (Ubuntu 4.8.2-19ubuntu1) 4.8.2
xoft@akima:~$ echo "long long main = -4323769003089592391;" | gcc -x c - && ./a.out
xoft@akima:~$
No error reported, a.out executing for 20 seconds.
Reply
Thanks given by:
#16
I'm not too familiar with ELF file format and it's the first time I've encountered the readelf tool, so I have no idea what I'm looking at. I see a long list of sections and then a long list of symbols within the .symtab section, but there's no indication about what symbol is in what section. If I follow the "main" symbol's address, it falls in the ".data" section that has "WA" flags (write, alloc), so I'd guess the compiler produces a correct executable, but the runtime fails to abort with an error. Could this be due to an old processor, perhaps not supporting some feature needed to make this work?
Reply
Thanks given by:
#17
Ah, found it:
Code:
root@akima:/proc# check-bios-nx --verbose
WARNING: the NX bit is not available for this CPU.
No wonder there's no security, thenTongue
Reply
Thanks given by:
#18
On a RasPi the non-const version fails with "segmentation fault", as expected, while the const version fails with "illegal instruction", which is expected as well, since the code is x86-specific.
Reply
Thanks given by:
#19
(02-12-2016, 06:56 PM)xoft Wrote: If I follow the "main" symbol's address, it falls in the ".data" section that has "WA" flags (write, alloc)
And if you investigate a little bit more, you will see that .data section falls into "Program header" with flags "RW" and .rodata into "R E".
(02-12-2016, 06:56 PM)xoft Wrote: No wonder there's no security, then
=D Then, this is very good that you found an answer. By the way, NX bit isn't the only option to disable data execution on x86/x86-64, there are also mechanism of segmentation but it's considered deprecated about 15 years ago and modern OS (both Linux and Windows) don't use it (or, to be more precise, use it in "Flat way" where are 4 segments identical in size and mapped on same physical memory location but different in access rights).
Reply
Thanks given by:
#20
This is a very informative thread Smile
Reply
Thanks given by: Schwertspize




Users browsing this thread: 6 Guest(s)