C Windows/Linux Buffer Overflow Exploits - linux

I have looked at how the buffer overflows work and have to determine that the program (with gcc) must compile with the following parameters: -fno-stackprotector and -z execstack.
You have to tell the kernel that it does not randomly allocate the addresses every time you start the program (which, however, is not absolutely necessary, just makes the buffer overflow easier)
Should a person now write an exploit e. g. against Apache, the Apache Developers will not compile the program with the above parameters.
How would these exploits still work?

-fno-stack-protector disables canary which is a random value placed between a function frame and saved instruction pointer. -zexecstack makes the stack memory region executable so that code execution is easy. With both these protections disabled its much easier to write exploits. Sometimes Address Space Layout Randomization (ASLR) is also disabled which means that all offsets in memory will be same on each execution.
For people who are starting with exploit development these protections are turned off so that they appreciate the techniques and attack vectors.
However with actual softwares such as apache, they are usually compiled with all protections (additionally PIE, RELRO). But there exist techniques that will help you to get code execution. One of them is called Return Oriented Programming(ROP) when used properly helps you defeat NX(non executale memory regions). Additionally to bypass ASLR/PIE you'll need one more leak primitive to get addresses from the memory. Its not impossible to write exploits for modern software its just difficult.

Related

Why is eBPF said to be safer than LKM?

when talking about ebpf advantage, it always mentions safe than lkm.
I read some documentation, ebpf ensures safe by verifying code before it loaded.
these are checklists that verify to do:
loops
out of range jumps
unreachable instructions
invalid instructions
uninitialized register access
uninitialized stack access
misaligned stack access
out of range stack access
invalid calling convention
most of these checks I can understand, but it's all reason that lkm cause kernel panic? if do these can ensure safe?
I have 120000 servers in production, this question is the only reason to prevent me to migrate from traditional hids to ebpf hids. but if it can cause a kernel panic on a large scale, only one time, our business will be over.
Yes, as far as I know, the BPF verifier is meant to prevent any sort of kernel crash. That however doesn't mean you can't break things unintentionally in production. You could for example freeze your system by attaching BPF programs to all kernel functions or lose all connectivity by dropping all received packets. In those cases, the verifier has no way to know that you didn't mean to perform those actions; it won't stop you.
That being said, any sort of verification is better than no verification as in traditional kernel modules. With kernel modules, not only can you shoot yourself in the foot as I've described above, but you could also crash the whole system because of a subtle bug somewhere in the code.
Regardless of what you're using, you should obviously test it extensively before deploying to production.

modular kernel vs micro kernel / monolitic kernel

I am C programmer and new to the Linux kernel programming. I could find there are 3 type of kernel monolithic,micro and modular kernel.while googling i could find some website say linux is having monolithic kernel (in Stack overflow) and some other says micro kernel and the rest say hybrid kernel. So i am totally confused while reading the modular concept which say new module for driver can be added without recompiling the kernel, which is against my assumption that Linux uses monolithic kernel. monolithic kernel runs in single address space and as a single processes this is also bit confusing if so
Before you try to understand those differences, you have to understand other concepts first:
1. Modular programming.
Module is a functionally complete part of a program. Module usually has following properties:
Separation of interface and implementation.
Initialization and deinitialization routines. Both are optional. Deinitialization routine is likely to be missing in environment with GC (Garbage Collector).
Modules used by a program compose directed acyclic graph a.k.a. dependency graph (you might have heard about this - cyclic dependencies are not allowed, dependency is initialized before dependent module).
Modular programming is essential when building large systems. Every big kernel is a modular kernel, regardless of whether it is monolithic, hybrid or microkernel.
Sometimes modules can be loaded and unloaded dynamically. Dynamic modules are essential part of any extensible system. Those can be plugins or, if we talk about kernels, drivers that are developed and distributed separately from the kernel.
2. Safe and unsafe languages.
Safe languages very strictly define what can happen in a program. Most importantly they have no concept of malformed program (or meaningless program). Every program is valid and its execution always follows language specification. Whether program does what programmer expects it to do or not, is irrelevant in this context.
Common traits of safe languages:
They use garbage collection.
They have no pointer arithmetics. That means that writing or reading to an arbitrary address is not allowed.
They prevent out of range array access (if there is such concept). Exceptions or similar mechanisms can be used to signal and recover from such failures.
References (or pointers) have only two possible states: null reference and reference to a valid object. This is guaranteed by garbage collector. In fact, GC is the key component here. Some languages go even further and do no allow null references at all.
Every object (memory chunk in use) has type information assigned to it, object can only be accessed through a reference of appropriate type e.g. you cannot access array of integers through reference to a string.
You can add more entries to this list, but basic idea is to guarantee that program can only access valid memory regions using valid operations. Keep in mind that some unsafe languages can share some or even all of those traits.
Examples of safe languages: Python, Java, safe subset of C#.
Unsafe languages define what can and cannot be done in a program, but there is usually little to nothing to stop programmer from doing the wrong thing. Program that violates those rules is called a malformed program. From language point of view such program is meaningless and language does not even try to define its behavior, as it is usually near to impossible to do. In terms of C such program's behavior is undefined.
Examples of unsafe languages: Assembler, C, C++, Pascal.
3. Hardware is unsafe and thus has to be programmed using unsafe language.
Most hardware does nothing to provide you with a safe environment. There were some processors that used to attach type information to every memory cell (see tagged architecture), but modern ones do not do this as it complicates hardware, making it slower, more expensive and less generic.
Still, some features are provided to make it possible to implement safe environments within unsafe environment of hardware, such as memory protection, separate address spaces and separation of execution modes on user mode and kernel mode (a.k.a. supervisor mode).
Kernel is what runs on bare metal and thus much of it has to be written in unsafe languages like C and Assembly. Another reason is performance - safe environments imply huge overhead.
Microkernel and Monolithic kernel
Monolithic kernel and its modules run in a single shared address space. And since everything is usually written in an unsafe language, it is possible for any part of the kernel to access (and damage) memory that belongs to another part of the kernel due to bugs in code. Unsafe nature of this environment makes it impossible to detect or recover from those failures and most importantly predict kernel behavior after such failures.
Microkernel is an attempt to overcome those limitation by moving various parts of the kernel to a separate address spaces, effectively isolating them from each other, but providing safe way to communicate with each other (usually through message passing). Such separation creates safe environment composed from multiple unsafe processes, allowing kernel to recover from failure of some of its subsystems.
At the same time monolithic kernel can be able to run parts of it in a separate address space (FUSE), while nothing stops microkernel from being able to support modules that share address space with the main part of the kernel.
If most of the kernel runs in a single address space, it is considered to be a monolithic kernel. If most of it runs in separate address spaces, such kernel is considered to be a microkernel. If kernel is somewhere inbetween and actively uses both approaches, you have a hybrid kernel.
Hybrid kernel
Concept of hybrid kernel implies combining best of both worlds and was invented by Microsoft to boost sales of Windows NT in 90s. Joke! But this is almost true. Every important part of Windows NT runs in a shared address space, so it is another example of monolithic design.
Many people seem to use this term when describing monolithic kernel that is able to dynamically load modules. This is because in the past monolithic kernels didn't support dynamic module loading and had to be recompiled every time module is added to the kernel. Microkernels are not about dynamic module loading, but about reliability of the kernel, about its ability to recover from failues of its subsystems.
The answer: Linux is a monolithic kernel.
Monolithic kernel can be modular and can dynamically load modules. Microkernel, on the other hand, has to be modular and has to be able to dynamically load modules - the whole idea is about running them in a separate address space.
Microkernel is not the only way of overcoming unsafe nature of monolithic kernel. Another way is to write monolithic kernel in a safe language. One problem with such approach is that safe environment should be either provided by hardware (and will be very limited) or should be implemented in software using unsafe languages. Implementation of such environment will be extremely complex and will most likely have many bugs (think of all bugs found in JVM).
Example of this would be experimental OS Singularity.
Well, considering that I may have a quiz on this tomorrow, I should be able to help you out. However, I am still learning, and while my post may have some technical mistakes, it should be conceptually sound
Basically, as you may understand, there are different type of kernels for an OS.
Monolithic kernels have all their system functionalities and services together in one single giant program, occupying a single address space.
Microkernels on the other hand, have the bare minimum system programs and services on the microkernel. Most of the services that were previously considered as a part of the kernel (during the monolithic kernel version) such as process scheduler, etc. are now in the user space, and are termed as servers. these servers communicate with each other through the micro-kernel, using the inter-process communication, a form of communication that is laid down by the microkernel.
The modular approach builds on this, by making these "servers" dynamically loadable. Thus, one can have a particular "server" (in this type of kernel, called a module) dynamically loaded, without the kernel requiring to re-compile itself.
Linux Kernel is a monolithic kernel, but most flavours of Linux such as Ubuntu, Solaris, use a hybrid kernel, i.e. a mix of the monolithic and modular kernel approach. This is quite common, has different kernel structure has different pros and cons, and a hybrid structure is required to strike a balance
See this prior StackOverflow question for some information about your question. Briefly, it sounds like you're wondering...
... reading the modular concept which say new module for driver can be added without recompiling the kernel, which is against my assumption that Linux uses monolithic kernel. monolithic kernel runs in single address space and as a single process ...
These two concepts ("modular kernel" and "single address space") are not actually contradictory. You can build a new kernel module without recompiling the entire Linux kernel. When you load this new kernel module, it will actually be loaded into the same address space as the running kernel itself. From the link above...
Do not confuse the term modular kernel to be anything but monolithic. Some monolithic kernels can be compiled to be modular (e.g Linux), what matters is that the module is inserted to and runs from the same space that handles core functionality (kernel space).
As you have found, there are several ways to classify kernels and the different types are not necessarily mutually exclusive.

Can anyone explain why NO-OP slide is used in shelllcoding?

An example where NO-OP slide is a must for the exploit to work would be really helpful.
An example of when it is a must is when you want an exploit to be portable when targeting a non-ASLR enabled executable/system. Consider a local privilege escalation exploit where you return to shellcode on the stack. Because the stack holds the environment, the shellcode on the stack will be at slightly different offsets from the top of the stack when executing from within different users' shells, or on different systems. By prefixing the shellcode with, for example, 64k nop instructions, you provide a large margin of error for the stack address since your code will execute the same whether you land on the first nop or the last one.
Using nops is generally not as useful when targeting ASLR enabled systems since data sections will be mapped in entirely different areas of memory

2 questions regarding ASLR

I've been reading about ASLR and I have a couple of questions. I have little programming experience but I am interested in the theory behind it.
I understand that it randomizes where DLLs, stacks and heaps are in the virtual address space so that malicious code doesn't know their location, but how does the actual program know their location when it needs them?
If the legitimate process can locate them, what stops the malicious code doing the same?
and finally, is the malicious code that ASLR tries to prevent running in the user space of the process it is attacking?
Thanks
As background, ASLR is intended to complicate code injection attacks where the attacker tries to utilize your overflow bug to trick your application into running the attacker's code. For example, in a successful stack buffer overflow attack the attacker pushes their code onto the stack and modifies the call frame's return pointer to point to the on-stack code.
Most code injection attacks require the attacker to know the absolute address of some part of your process's memory layout. For stack buffer overflow attacks, they need to know the address of the stack frame of the vulnerable function call so they can set the functions return pointer to point to the stack. For other attacks this could be the address of heap variables, exception tables, etc...
One more important background fact: unlike programming languages, machine code has absolute addresses in it. While your program may call function foo(), the machine code will call address 0x12345678.
but how does the actual program know their location when it needs them?
This is established by the dynamic linker and other operating system features that are responsible for converting your on-disk executable into an in-memory process. This involves replacing references to foo with references to 0x12345678.
If the legitimate process can locate them, what stops the malicious code doing the same?
The legitimate process knows where the addresses are because the dynamic linker creates the process such that the actual addresses are hard-wired into the process. So the process isn't locating them, per se. By the time the process is started, the addresses are all calculated and inserted into the code. An attacker can't utilize this because their code is not modified by the dynamic linker.
Consider the scenario where an attacker has a copy of the same executable that they are trying to attack. They can run the executable on their machine, examine it, and find all of the relevant addresses. Without ASLR, these addresses have a good chance of being the same on your machine when you're running the executable. ASLR randomizes these addresses meaning that the attacker can't (easily) find the addresses.
and finally, is the malicious code that ASLR tries to prevent running in the user space of the process it is attacking?
Unless there's a kernel injection vulnerability (which would likely be very bad and result in patches by your OS vendpr), yes, it's running in the user space. More specifically, it will likely be located on the stack or the heap as this is where user input is stored. Using data execution prevention will also help to prevent successful injection attacks.

Can any current hardware set a RAM page to be executable but not readable?

Just like how NX / DEP allow a page to be set non executable when it is writable, is it possible to tell the CPU that it can execute a page (i.e. read it for instruction fetch) but throw and exception if it is accessed via a mov instruction? The effect will be to make it impossible / very hard to copy the binary data of the instructions just by misusing an exploit.
The possible use I thought something like this might have was in preventing an exploit disclosing the contents of the executable pages, making ROP attacks harder. (this paper kind of makes it seem like all ASLR is just a bandage on a hemorrhage) There are a huge number of ways to trick programs into reading arbitrary memory locations, and patching them all will be a hopeless task.

Resources