Sigreturn Oriented Programming (SROP) Attack Principle
I wrote an article last year to introduce the BROP attack. The response was quite good and helped many people understand this very smart attack principle. Of course, you can also go to my blog to see the replay of this attack.
This time I would like to introduce another type of drop-down attack, called sdrop. Like BROP, this job was also published at the top international security conference in Oakland 2014 and was selected as the Best Student Papers of the year. The title of this paper is 'framing Signals-A Return to Portable shellcode'. The author is Erik Bosman from Vrije Universiteit Amsterdam. below is the link to the relevant paper and slides:
Paper
Slides
First, I think this is also a very interesting attack. Although it was published last year, I have not found much relevant information on the Internet, there have not been many references to this field in recent years, so I think it is necessary to promote it. However, after reading paper, I felt that this paper was not easy to understand and many sentences were obscure, but these are not important. What is important is that, this attack is indeed effective. It is mentioned that the Signal mechanism has been used for more than 40 years in different versions of Unix systems (such as GNU Linux, BSD, iOS/Mac OS X, there is a design defect that can be easily exploited by attackers, and the corresponding attack caused by this defect, that is, the SROP described in this article, is simpler and more reliable than the traditional ROP attack, portable.
Next, I will start from the drop-down attack and introduce the sdrop attack principle, which will involve the background of the Signal mechanism. Finally, I will introduce some corresponding defense measures.
Background
The emergence of a ROP attack
Stack Overflow and DEP (Data Execution Prevention) I will not introduce them here. Simply put, the earliest code injection attack is basically unavailable in the current operating system, so the following occurs, that is, the so-called Return Oriented Programming, which also includes the early Return-to-libc. The main idea of ROP is that attackers do not need to inject code themselves (because the injected code cannot be executed under the protection of DEP), but use the existing code snippets of the system to construct attacks. The reason for this is that the method for changing the control flow is to use the return command in the system (for example, 'ret 'in x86 ').
Here, we need to note that in stack operations, the return command is the only command that can change the instruction stream by controlling the data on the stack. Its effect is equivalent:
pop %eaxjmp %eax
Set the IP address to a value on the stack. Therefore, if we can control the data on the stack, we can control the execution stream.
Principle of ROP attacks
Here is a simple example to illustrate how to use ROP to implement a memory assignment statement: 'mem [v2] = v1', where 'v2' is a memory address, 'v1 'is a value.
Here we first convert the memory assignment statement 'mem [v2] = v1 'into assembly code:
mov %eax v1; mov %ebx v2;mov [%ebx], %eax
Assign the value of 'v1 'to' % eax ', and assign the value of 'v2' (memory address) to' % ebx ', finally, the value of '% eax' is assigned to the memory pointed to by '% ebx' address.
We will translate this code into code that can be executed through the drop operation:
addr1: pop eax; retaddr2: pop ebx; retaddr3: mov [ebx], eax
'Addr1', 'addr2', and 'addr3' are the memory addresses of the corresponding commands. Each row is called a "gadget ". By constructing the data on the stack as in, you can achieve the same effect as the above compilation. If you are not sure, you can try it by yourself. I will not detail it here.
You may ask, what is the purpose of constructing a memory assignment statement? The most widely used one is to assign values to parameters. Suppose we want to call a system call using the method of ROP, then we can use this method to fill in the parameter for this system call, finally, fill in the memory address of the 'syscall' (or 'int 80') command on the stack.
Prerequisites
At this point, if you have a preliminary understanding of the above, you may feel that the drop action is really amazing. You can call system call at Will (for example, call 'execve' to open a shell; or call 'mprotect 'to set the stack to executable ). However, in fact, drop is not as simple as you think. To complete a successful ROP attack, there are many prerequisites. Here are several of the most important:
1. First, there must be a buffer overflow Vulnerability (of course, basically all attacks must exist). 2. The attacker needs to determine all the gadgets involved in the attack process in advance. For the value assignment operation mentioned above, only three gadgets are required in total, and each gadget has up to two commands. However, if complicated operations are required, many gadgets may be required; in addition to the large number of gadgets, the number of commands for a single gadget must also be considered; 3. attackers need to find all the first addresses of these gadgets in the process address space where the attacked program is located, and fill them in the appropriate positions of the stack.
These three prerequisites make it difficult for traditional ROP attackers to do so. In addition, there are a series of protection mechanisms (such as ASLR) in the operating system ), this makes it harder to find the address of the gadgets. Moreover, for attackers, they need to carefully construct a large number of gadgets to attack each different application, which also makes the reusability of ROP very poor.
All of the above are the problems that SROP wants to solve.
SROP attack Principle
The full name of sdrop is Sigreturn Oriented Programming. Here 'sigrecall' is a system call, which is indirectly called when signal occurs in unix systems.
Before introducing the SROP attack principles, we need to briefly describe the signal background.
Signal in Unix-like System
The Signal mechanism was proposed and integrated into the UNIX kernel in the 1970 s. It is widely used in the current operating system, for example, the kernel needs to kill a process ('Kill-9 $ PID '), set a timer for the process, or notify the process of some abnormal events.
As shown in, when the kernel initiates a signal (deliver) to a process, the process will be temporarily suspended (suspend) and enter the kernel (1 ), then the kernel saves the corresponding context for the process and jumps to the previously registered signal handler to process the corresponding signal (2). When the signal handler returns (3 ), the kernel restores the saved context for the process and finally restores the execution of the process (4 ).
In these four steps, the third step is the key, that is, how to make the user-state signal handler smoothly return to the kernel state after the execution is complete. In UNIX-like systems, this process is somewhat different, but the general process is the same. Linux is used as an example:
In step 2, the kernel will help the user process store the context on the stack of the process, and then fill in an address 'rt _ sigreturn 'on the top of the stack, which points to a piece of code, in this Code, the 'sigrecall' system call is called. Therefore, after the signal handler is executed, the stack pointer points to 'rt _ sigreturn '. Therefore, the last 'ret 'command of the signal handler function redirects the execution stream to this sigreturn code and passively calls the 'sigrecall' system. Displays the user process context and signal information stored on the stack, and 'rt _ sigreturn ':
We call this memory a 'signal framework '.
In the kernel 'sigrecall' System Call handler, the process context is restored Based on the 'signal framework' pointed to by the current stack pointer, the user State is returned, and the execution is resumed from the starting point.
Do you see any problems? If not, I suggest you think about it again, because I am about to reveal the answer.
Signal mechanism defect Exploitation
Okay. Let's take a look at the role of the kernel in this process? First, the kernel saves the context of the user process to 'signal framework' for the user process. Then, the kernel uses this 'signal framework' to restore the context of the user process, done! So the question is:
1. the 'signal framework' is stored in the address space of the user process and can be read and written by the user process;
2. the kernel does not compare the stored and recovered processes. That is to say, in the processing function called by the 'sigrecall' system, the kernel does not determine whether the current 'signal framework' is the 'signal framework' stored by the kernel for the user process '.
According to the slides, "kernel agnostic about signal handlers" is both an advantage, because the kernel does not need to spend the effort to record its initiated signal, but this is also a disadvantage, it is precisely because the kernel cannot understand it that malicious user processes can forge it!
Example: A simple attack
Let's assume that an attacker can control the stack of user processes, and then it can forge a 'signal framework', as shown in:
In this forged 'signal framework', set 'rax' to 59 (that is, the 'execve' system call number ), set 'rdi 'to the address of'/bin/Sh' (the string can be written by attackers on the stack ), set 'Rip 'to the memory address of the System Call Command 'syscall'. Finally, manually set 'rt _ sigrecall' to the memory address of the 'sigrecall' system call. Then, after the counterfeit 'sigrecall' system calls and returns, the corresponding register is set to a value that can be controlled by attackers. In this example, once 'sigrecall' returns, it will execute the 'execve' system call to open a shell.
This is the simplest attack. In this attack, there are four prerequisites:
First, attackers can control the content on the stack through vulnerabilities such as stack overflow. Second, they need to know the stack address (for example, the address of the self-constructed string '/bin/Sh ); third, you need to know the address of the 'syscall' command in the memory; fourth, you need to know the memory address of the 'sigrecall' system call.
Compared with traditional ROP, this simplest SROP attack only needs to find two gadgets. However, in this attack, the attacker can only call one syscall. When syscall returns, it loses control over the execution stream, which obviously cannot meet most of the requirements.
Construct a System call chain using SROP)
So, how can we use the above mechanism to continuously call the system? In fact, the method is very simple. In addition to the above steps, you only need to add an additional control on the stack pointer 'rsp ', as shown in:
In addition, we need to replace the original 'syscall' gadget with 'syscall; ret 'gadget. In this process, every time 'syscall' is returned, the stack pointer will point to the next 'signal framework'. Therefore, execute the 'ret 'command at this time, the 'sigrecall' system call is called again. In this way, the operation stack can be used to achieve continuous system calls.
Two important gadgets
So where can we find the two gadgets mentioned above?
For the first gadget 'sigreturn ', we need to mention that the 'sigreturn' system call is different from other system calls, that is, the general application will not actively call it, as described earlier, the kernel fills in the corresponding address on the stack, making the application process call passively. Therefore, there is usually a piece of code specifically used to call 'sigreturn 'in the system. The author found that in different UNIX-like systems, this code will appear in different places, as shown in:
In 'linux <3.11 ARM '(that is, the kernel used by most Android systems) and 'freebsb 9.2 x86_64', you can find this gadget in a fixed memory address, in other systems, it is generally stored in the memory of the 'libc' library. If ASLR protection is available, it does not seem easy to find.
For the second gadget 'syscall; ret ', as shown in:
For 'linux <3.3 x86_64 '(default kernel In Debian 7.0, Ubuntu Long Term Support, and CentOS 6 ), you can find this code snippet in the fixed address [vsyscall. 'Vsyscall' is the mechanism used to accelerate the calls of the 'Time () ', 'gettimeofday ()', and 'getcpu () 'systems, although it has been replaced by the 'vsyscall-receive' and 'vdso' mechanisms, it is still supported by default in earlier kernels. For details, refer to the post of lwn.
Except the two mentioned above may exist in the fixed-address gadgets, these two gadgets do not seem to be so easy to find in other systems, especially in systems protected by ALSR. However, if we compare it with the traditional ROP, we can find that it lowers the overall attack cost by a grade, no wonder the author thinks SROP "is among the lowest hanging fruit available to an attacker."
'Sigrecall' is this gadget required?
If we look at 'sigrecall' as a system call, this separate gadget is not necessary. Because we can set the 'rax' register to 15 (the system call number of sigreturn) and then call a 'syscall', the effect is the same as that of calling a 'sigrecall. Then the problem is changed from "How to find a 'sigreturn 'gadget" to "how to control the value of the Register 'rax ". The 'rax' register is very special. It is used not only to specify the call number of a system call, but also to store the function return value. Therefore, we can use the return value of the control function to control the value of the 'rax' register. The specific practice varies from person to person, but this provides an attacker with another idea. If you are interested, you can read the paper. In this paper, the author transfers different quantities of bytes to control the returned values of 'read' calls when the victim process calls the 'read' System Call to the network file descriptor, to achieve the effect of controlling the 'rax' register!
SROP application scenarios
As mentioned above, with sdrop, we can construct a series of system call strings. With this method, we can do a lot of things.
Application Scenario 1: Backdoor)
First, the author proposes that a backdoor can be constructed using this method ). A backdoor means that an attacker hides a trigger point in the system. When some rare operations occur, an action is triggered, this action is usually to open a port and allow attackers to connect to the system through the network to control the system. The most important feature of backdoors is concealment, which is not easily detected by anti-virus software. Therefore, if the backdoor is a piece of executable code, it is easily detected by anti-virus software. If the backdoor is hidden in the data domain, it can be very hidden. SROP provides a powerful tool: system call chain. The entire process is shown in:
In this process, attackers use 'inotify API 'to monitor a file. When the file is accessed (for example, the attacker reads the content of the file in a request ), then the subsequent System Call chain is triggered. Open a socket and wait for the connection. At the same time, set a timer through a 'alarm 'system call. If no one initiates a connection request to this socket within five seconds, disable it; otherwise, establish a connection, and call the 'execve' system call to open a shell.
This is a typical backdoor that can be completed with SROP.
Application Scenario 2: System call proxy)
In this application scenario, the author mentions how to bypass the code signing protection mechanism of systems such as 'ios 'and 'mac OS x. Under the Code signature protection mechanism, the program cannot contain malicious code snippets (otherwise, it cannot be officially verified by Apple ). But sdrop can solve this problem. We can establish a network connection in the code and then execute some malicious operations by constructing specific requests. Here, the author proposes a system call proxy mechanism, which is similar to the principle of the system call string I introduced earlier, you can set parameters by executing a series of 'read' system calls. Finally, you can use these parameters to construct a specific system call, as shown in the following figure:
SROP prevention
Finally, let's take a look at SROP prevention. From three perspectives, the author proposes three methods:
Gadgets Prevention
In the 'two important gadgets' chapter, I mentioned that 'sigrecall' and 'syscall; ret 'are very easy to find in several different operating systems, especially when 'vsyscall' is a particularly insecure mechanism. Therefore, we should try to avoid this mechanism and make full use of ASLR and other protection mechanisms, making it difficult for attackers to find these gadgets.
Of course, this method does not solve the SROP problem in essence.
Signal Frame Canaries
This method draws on the stack canaries mechanism, that is, inserting a random byte before the 'rt _ sigreturn 'field of the 'signal framework'. If overflow occurs, the byte is damaged, this will be detected before 'sigrecall' occurs.
Of course, there are also a lot of attacks against stack canaries, which cannot prevent SROP from occurring in essence.
Break kernel agnostic
This will be traced back to the essence of SROP, that is, the kernel's unpredictability to Signal. If we judge whether the current 'signal framework' was created before the kernel when the kernel processes the 'sigreturn 'system call, this problem can be fundamentally solved. Of course, this involves modifying some underlying design of the kernel and may introduce some new problems.
Proof of Concept (PoC)
If you are interested in this attack, you can download a very simple PoC from here. Of course, according to the author, SROP has been integrated into the binjitsu framework. If there is time later, I will also introduce this PoC and the binjitsu framework.