您现在的位置: Tracy‘Blog > 博客 > C/C++ > 正文
Linux桩程序

        嗯,CCTF到昨天,已算圆满结束了.从初赛到决赛,大家也都挺不容易的.都辛苦了.不过,玩的都还挺开心,在搭建比赛环境、出题等过程中,也是学到了不少姿势。

        先说说出PWN题目用到的桩程序吧。

        首先需要考虑到,他能够被溢出进行利用,而且容易被打崩掉。如果只是一个进程去开端口监听显然是不合适的。不然,整个比赛过程就进行不下去了。

        所以得用另外一个程序去监听端口,当有连接进来的时候,启动新的子进程去进行回话。

        然后,问题也来了。怎么让子进程与连接方通信呢?

        问了问伟神。伟神给了一句话:

            accept -> fork -> dup2 dup2 -> exec

         就知道了,用dup来复制句柄,让新建的那个子进程的标准输入输出包括错误重定向到socket上来进行回话。然后写了个服务端。

        随意找了个程序当子进程去启动,结果发现,一直要到交互结束才有信息打印出来。伟神说了句,取消stdio的buf。然后就好了。

        具体服务端,也就是桩程序如下:

/* File Name: server.c */  
#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <errno.h>  
#include <sys/socket.h>  
#include <unistd.h>
#include <arpa/inet.h>

//#define DEFAULT_PORT 8000  
#define MAXLINE 4096  
    
int main(int argc, char** argv)  
{  
    if (argc != 4)
    {
        printf("Usage: ./server [path] [program] [port] \n");
        exit(0);
    }
    int    socket_fd, connect_fd;  
    struct sockaddr_in     servaddr; 
    char    buff[4096];  
    int     n;  
    //初始化Socket  
    if( (socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ){  
    printf("create socket error: %s(errno: %d)\n",strerror(errno),errno);  
    exit(0);  
    }  
    //初始化  
    memset(&servaddr, 0, sizeof(servaddr));  
    servaddr.sin_family = AF_INET;  
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//IP地址设置成INADDR_ANY,让系统自动获取本机的IP地址。  

    servaddr.sin_port = htons(atoi(argv[3]));//设置的端口为DEFAULT_PORT  
  
    //将本地地址绑定到所创建的套接字上  
    if( bind(socket_fd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1){  
    printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno);  
    exit(0);  
    }  
    //开始监听是否有客户端连接  
    if( listen(socket_fd, 10) == -1){  
    printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno);  
    exit(0);  
    }  
    printf("======waiting for client's request======\n");  
    while(1)
    {  
        struct sockaddr_in remote;
        socklen_t len = sizeof(struct sockaddr_in);
          //阻塞直到有客户端连接,不然多浪费CPU资源。  
        if( (connect_fd = accept(socket_fd, (struct sockaddr*)&remote, &len)) == -1)
        {  
            printf("accept socket error: %s(errno: %d)",strerror(errno),errno);  
            continue;  
        }  
    
        if(!fork())
        {  
            printf ("Request Ip: %s \n", inet_ntoa(remote.sin_addr));
        
            dup2(connect_fd, 1);
            dup2(connect_fd, 0);
            dup2(connect_fd, 2);

            execlp(argv[1], argv[2], NULL); 
            exit(0);  
        }  
        close(connect_fd);  
    }  
        close(socket_fd);  
}  

        客户端,实际用来pwn的程序,最好是在程序开始的加上几句:

    setbuf(stdin, NULL);
    setbuf(stdout, NULL);
    setbuf(stderr, NULL);

        然后。恩,就酱紫了。使用方法是:

        ./server /bin/ls ls 12345

        然后,nc到12345就可以执行ls。

        恩,就酱紫。记录下~


        Windows下其实也能够实现,不过稍微麻烦些,应该也差不蛮多。上次Alictf决赛用的就是Windows下的桩程序和客户端。大概原理就是,把socket传入到子程序中,进行通信。改天有时间再详细分析一下实现流程,然后再写一些吧。


Tracy_梓朋

2015年05月17日21:43:26

发表评论(0)
姓名 *
电子邮件
QQ
评论内容 *
验证码 *图片看不清?点击重新得到验证码请输入图片后链接字符‘a’