Académique Documents
Professionnel Documents
Culture Documents
System
Patrick Carroll
pcarrol1@umbc.edu,
Computer Science Department
University of Maryland, Baltimore County
Baltimore, MD 21250
Abstract from the client. Say that the server reads in fixed-
The goal of this paper is to analyze the security of length message blocks of length 512 Kilobytes. If the
the Linux operating system with regards to keyloggers server does not check the amount of data read, it can
and buffer overflows. This paper will cover how buffer cause a condition known as a buffer overflow. This
overflows are constructed, how code can be protected is where a program can be made to crash by giving it
from them, how keyloggers work and how they can be data of an unexpected length. Furthermore, instead of
run in the Linux environment (including running in just making the server crash, it is possible to run arbi-
both user and kernel space.) Finally, this paper will trary code on the server which may allow an attacker
cover how to find setuid programs as well as how to to completely compromise the system.
hide a process from the ps command.
2 Buffer Overflows in Linux
1 Introduction A buffer overflow is a programming error which can
A keylogger is a program which runs on a computer happen when using a programming language that uses
and records every key typed to a file, device or to the fixed buffer sizes but allows a user to specify a value
network. Keyloggers can be used by malicious indi- larger than the buffer it is supposed to be put in. In
viduals who would like to gather information (such as most cases (such as Linux’s case) this programming
PIN numbers, usernames and passwords, credit card language is something like C or C++. This results
information, etc...) from people physically inputting in the malicious user being able to write data into a
information through the keyboard. To show how a memory location that is outside of the intended data
keylogger can be run by a non-privileged user, I will storage location [4]. The user can write data to mem-
show how a buffer overflow can be used to run arbi- ory because the faulty program allowed them to in-
trary programs with the permission of the root user. put more data then the size of the allocated buffer.
This paper will focus on programs with buffer over- To fix this error, the application programmer should
flows which are setuid-root. That is, programs with have placed more rigorous checks on the incoming data
permissions that when they are run, they are run with to the program. Also, with higher-level programming
the same permissions as the owner of that file. This languages such as Python, Lisp or Perl, buffer over-
can be a common occurrence in poorly written server flows are less common due to the nature of these lan-
software. As an example of this, consider a mail pro- guages not using fixed length buffers and arrays. A
gram which runs on a server and listens on a specified trivial example of a C program with a buffer overflow
port. When a client connects to this port, the server would be: [5]
reads in a message that the client specifies. It then
takes this message and writes it to the user’s account #include <string.h>
stored on the server. Because this mail server will need
to have access to all of the users home directories, the void dumb_function(char *unknown_length_str)
easiest way to write it would be to just make it setuid- {
root. This way, it can easily write to any user’s home char little_buffer[16];
directory because it would be running as the root user.
The problem with this scheme occurs when we ex- /* this will try to copy each byte into
amine the way in which the server takes it’s input buffer until it sees a ’\0’ (which
it won’t see) */ possible to change the execution order of the program.
strcpy(little_buffer, unknown_length_str); This can be done by modifying the stack so that the
} return address of the current function points to a dif-
ferent address. This different address can be set up
to contain target shellcode to be run. Calculating the
int main(int argc, char **argv) return address of a function can be done by examining
{ the program in a debugging environment such as the
char big_string[256]; GNU Project Debugger.
int i; To develop shellcode for attacking a system, the at-
tacker must have some knowledge of the corresponding
for (i = 0; i < 256; i++) assembly code for the action they would like to per-
big_string[i] = ’Z’; form. If the attacker does not know assembly, and
they’re compiler supports it, they could write the at-
dumb_function(big_string); tack in C and compile it to assembly code. Because
return 0; the assembly code to be executed will be stored on the
} stack, it must be stored in a hexadecimal representa-
tion. To do this, the person crafting the shellcode can
This program causes a buffer overflow because
again compile the target attack and use a debugger like
dumb function() will attempt to copy all of the string
GDB to calculate the hexadecimal representations of
unknown string length into little buffer, which
the instructions.
is clearly smaller in allocated size. This will cause ’Z’
As mentioned previously, one final hurdle to over-
characters to be written into memory after strcpy()
come when developing shellcode is to remove any null
goes past the 16th index.
characters. This process consists of examining the
2.1 The Stack hexadecimal output of the shellcode and figuring out
The stack is a dynamic data structure which is ways to replace instructions with instructions which
maintained by the operating system. It is used by perform equivalent actions, however they do not pro-
the operating system to keep track of the local vari- duce null bytes in the shellcode. As an example of
ables and memory allocation of function calls during the various steps in forming shellcode, the example
program execution. Because locally defined arrays or from “Smashing The Stack For Fun And Profit” [5] is
buffers will be put on the stack, if a malicious user included:
is allowed to specify variable length input, it’s possi-
ble that the user can write instructions onto the stack • The assembly code for running the ”/bin/sh” pro-
to be executed. The stack is typically implemented gram is included. This code uses the assembly
in hardware by maintaining a register which holds a JMP and CALL instructions to allow the shellcode
memory reference called the stack pointer. The stack to use relative addressing. If values were not cal-
pointer points to the beginning or end of the stack, culated ahead of time, we would have to figure
and the stack either grows from the stack pointer or out the exact address of memory which the at-
away from it. This is all dependent on the architecture tack needs to jump to.
of the system.
2.2 Shellcode jmp 0x2a # 3 bytes
The code that is to be executed by overwriting the popl %esi # 1 byte
stack is called shellcode. There exist many examples movl %esi,0x8(%esi) # 3 bytes
of shellcode for pretty much all operating systems such movb $0x0,0x7(%esi) # 4 bytes
as BSD, Linux, Solaris and Windows [2]. Shellcode is movl $0x0,0xc(%esi) # 7 bytes
formed by taking assembly code which performs the movl $0xb,%eax # 5 bytes
desired action, converting it to a hexadecimal repre- movl %esi,%ebx # 2 bytes
sentation and removing all of the null bytes from it. leal 0x8(%esi),%ecx # 3 bytes
The reason that the null bytes are removed from it is leal 0xc(%esi),%edx # 3 bytes
because in a programming language like C, null bytes int $0x80 # 2 bytes
are used to signify the end of a string and would there- movl $0x1, %eax # 5 bytes
fore terminate the execution of the desired shellcode. movl $0x0, %ebx # 5 bytes
Given that there is a way for a program to write int $0x80 # 2 bytes
code into the stack of a program being attacked, it is call -0x2f # 5 bytes
.string \"/bin/sh\" # 8 bytes 3.1.2 Cons of User Space Keyloggers
• The function used by the kernel for translat- • As of the 2.6 Linux kernel, the system call table
ing scancodes is called handle scancode(). It is no longer exported and therefore system calls
is possible to intercept this function to record are not as easily intercepted.
all keystrokes. This has the disadvantage that • Because of the nature of kernel space programs,
you will still have to parse the scancodes and there is no easy or standard way of writing to a
figure out what the current keymap is. Also, file system from the kernel. [9] For many various
because handle scancode() is not exported by reasons, code that is run in the kernel is not sup-
default, you will have to use the method de- posed to access the file system and this poses a
scribed later for locating it’s address. There is problem because the keylogger must find a way
also a function put queue() which is used by to output it’s data.
handle scancode() which would be intercepted
as well. • If not implemented and tested carefully, a kernel
module can have very significant effects on the
• Intercept all TTY information. This can performance of the system.
be done by intercepting the tty read() or
receive buf() functions. By intercepting data While you have to build the kernel module specif-
from the TTY information, you gain many advan- ically for the system that you are going to run it on,
tages. For one, you do not have to handle scan- this is really not that hard. Given that an attacker has
codes or keymaps. Also, other data such as SSH a local shell account on the system, they can create a
and telnet sessions work with TTYs and would Makefile containing:
therefore be able to intercept data from more K_DIR := /lib/modules/$(shell uname -r)/build
sources. This approach could be performed by obj-m += klog.o
first intercepting the system call used for open-
ing TTY sessions. You could save a descriptor all:
for each TTY opened and then use that to read make -C $(K_DIR) M=$(PWD) modules
copies of the data being sent.
clean:
3.2.2 Pros of Kernel Space Keyloggers make -C $(K_DIR) M=$(PWD) clean
• Because the kernel does the work of translating If the attacker does not have a local user account,
keycodes into a readable form (i.e. translating but they know the kernel version, they could simply
Unicode, converting between keymaps and lan- install the given version of Linux locally and build the
guages) the keylogger has less work to do. kernel module.
3.2.4 Locating the System Call Table sys_call_off = (idt.off2 << 16) | idt.off1;
As of the Linux 2.6 kernel, the table containing all of memcpy(sc_asm, (void *)sys_call_off, CALLOFF);
the system calls is no longer exported. This makes
the interception of system calls much harder because /* we have syscall routine address now, look for
the attacker must now calculate the location of the syscall table dispatch (indirect call) */
system call table. This causes portability problems p = (char*)memmem(sc_asm, CALLOFF,
because specific characterisitics of the x86 architec- "\xff\x14\x85", 3);
ture are exploited [10] [7]. Some example code [11] for
calculating the location of the system call table is: if (p)
*sct = (void *)*(unsigned*)(p+3);
struct { else
unsigned short limit; *sct = NULL;
unsigned int base; }
} __attribute__ ((packed)) idtr;
struct { Now that you have this code, you simply call
unsigned short off1; find sys call table(sct) and sct will be set to the
unsigned short sel; location of the system call lookup table. Given this,
unsigned char none,flags; the attacker can find the index of the system call which
unsigned short off2; they’d like to replace, and simply do the replacement
} __attribute__ ((packed)) idt; from the initialization code of a kernel module.
[12] “Nmap -
Free Security Scanner For Network Exploration &
Security Audits,” http://insecure.org/nmap/