参考:https://puzzor.github.io/%E5%88%A9%E7%94%A8AFL-Fuzz-Server

对tcp/udp通信的程序进行fuzz,可进行如下方式改写为本地读取消息

原程序demo

#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include<arpa/inet.h>
const int MAXLINE = 1024;
int main(void) {
    int sockfd, clientfd;
    socklen_t cliaddr_len;
    struct sockaddr_in server_addr, client_addr;
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == -1) {
        perror("Something wrong\n");
        exit(1);
    }
    bzero(&server_addr, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(1024);
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);

    int br = bind(sockfd, (struct sockaddr *) &server_addr, sizeof(server_addr));
    if (br == -1) {
        perror("Something wrong\n");
        exit(1);
    }

    if ((listen(sockfd, 20)) == -1) {
        perror("Something wrong\n");
        exit(1);
    }

    char buf[MAXLINE];
    for (;;) {
        clientfd = accept(sockfd, (struct sockaddr *) &client_addr,
                          &cliaddr_len);
        printf("server get connection from %s.\n", inet_ntoa(
                client_addr.sin_addr));
        int readize = 0;
        while ((readize = read(clientfd, buf, MAXLINE)) > 0) {
            printf("Content:%.*s", readize,buf);
            printf("Length:%d...\n", readize);
        }
        write(clientfd, buf, readize);
        close(clientfd);
    }
    return 0;
}

程序本身是一个简单的Server端的socket程序,其监听1024端口并接收数据,接收成功后将数据长度以及内容打印出来。

验证如下

echo "aaaaaa" > /dev/tcp/127.0.0.1/1024

我们首先定位accept,然后在accept之前创建一个线程,此线程所做的工作是从本地读取一个文件,并将其内容通过socket方式发送到原程序监听的端口上。我们将上述程序修改如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include<arpa/inet.h>
#include <pthread.h>
#include<sys/time.h>
const int MAXLINE = 1024;
void *thread(void *arg){
    int sockfd,sock_dt;
    struct sockaddr_in my_addr;
    struct sockaddr_in dest_addr;
    int destport =1024;
    int n_send_len;
    printf("thread is going to run and send sth to origin socket\n");
    sleep(1);
    sockfd=socket(AF_INET,SOCK_STREAM,0);
    dest_addr.sin_family=AF_INET;
    dest_addr.sin_port=htons(destport);
    dest_addr.sin_addr.s_addr=inet_addr("127.0.0.1");
    memset(&dest_addr.sin_zero,0,8);
    connect(sockfd,(struct sockaddr*)&dest_addr,sizeof(struct sockaddr));
    n_send_len = send(sockfd,"Content sent from thread\n",strlen("Content sent from thread\n"),0);
    printf("%d bytes sent\n",n_send_len);
    close(sockfd);
    return NULL;
}
int main(void) {
    int sockfd, clientfd;
    socklen_t cliaddr_len;
    struct sockaddr_in server_addr, client_addr;
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == -1) {
        perror("Something wrong\n");
        exit(1);
    }
    bzero(&server_addr, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(1024);
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);

    int br = bind(sockfd, (struct sockaddr *) &server_addr, sizeof(server_addr));
    if (br == -1) {
        perror("Something wrong\n");
        exit(1);
    }

    if ((listen(sockfd, 20)) == -1) {
        perror("Something wrong\n");
        exit(1);
    }

    char buf[MAXLINE];
    for (;;) {
        pthread_t th;
        pthread_create(&th,NULL,thread,NULL);
        clientfd = accept(sockfd, (struct sockaddr *) &client_addr,
                          &cliaddr_len);
        sleep(1);
        printf("server get connection from %s.\n", inet_ntoa(
                client_addr.sin_addr));
        int readize = 0;
        while ((readize = read(clientfd, buf, MAXLINE)) > 0) {
            printf("Content:%.*s", readize,buf);
            printf("Length:%d...\n", readize);
        }
        write(clientfd, buf, readize);
        close(clientfd);
    }
    return 0;
}

编译: gcc change.c -lpthread

可以看到我们在accept函数执行之前创建了线程,线程会主动发起连接请求并发送数据。 程序输出结果如下:

[root@localhost home]# ./a.out 
thread is going to run and send sth to origin socket
25 bytes sent
server get connection from 0.0.0.0.
Content:Content sent from thread
Length:25...
thread is going to run and send sth to origin socket
25 bytes sent
server get connection from 127.0.0.1.
Content:Content sent from thread
Length:25...
thread is going to run and send sth to origin socket
25 bytes sent
server get connection from 127.0.0.1.
Content:Content sent from thread
附udp demo
/*client.c*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <arpa/inet.h>
#define MAXBUF 256
int main(int argc, char const *argv[])
{
    int s = 0;
    int n = 0;
    int reuse = 1;
    int port = 1024;
    struct sockaddr_in srv;
    char buf[MAXBUF] = {0};
    /*解析参数*/
    if (argc != 2)
    {
        printf("Usage:%s ServerIP\n", argv[0]);
        return -1;
    }

    bzero(&srv, sizeof(srv));
    srv.sin_family = PF_INET;
    srv.sin_addr.s_addr = inet_addr(argv[1]);
    srv.sin_port = htons(port);
    /*创建 UDP 套节字*/
    s = socket(AF_INET, SOCK_DGRAM, 0);
    if(s<0){
        perror("socket");
        return -1;
    }

    while(1){
        memset(buf, 0, MAXBUF);
        /*读取用户输入到buf中*/
        fgets(buf, MAXBUF, stdin);

        /*通过套节字 s 向服务器发送数据*/
        if ((n = sendto(s, buf, strlen(buf), 0, (struct sockaddr *) &srv, sizeof(struct sockaddr))) < 0)
        {
            perror("sendto");
            return -1;
        }else{
            printf("send to %s(port=%d) len %d:%s\n", argv[1], port, n, buf);
        }
    }
}

/*server.c*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <arpa/inet.h>

#define MAXBUF 256
int main(int argc, char const *argv[])
{
    int s = 0;
    int n = 0;
    int reuse = 1;
    int cli_len = sizeof(struct sockaddr);
    int port = 1024;
    char buf[MAXBUF] = {0};
    struct sockaddr_in addr, cli;

    /*初始化本地监听端口信息*/
    bzero(&addr, sizeof(addr));
    addr.sin_family = PF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port = htons(port);

    /*创建UDP套节字*/
    s = socket(AF_INET, SOCK_DGRAM, 0);
    if (s<0)
    {
        perror("socket");
        return -1;
    }

    /*允许端口复用*/
    setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));

    /*绑定指定端口*/
    if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0)
    {
        perror("bind");
        return -1;
    }

    while(1){
        memset(buf, 0, MAXBUF);
        /*从套节字s中读取数据*/
        n = recvfrom(s, buf, MAXBUF, 0, (struct sockaddr *)&cli, &cli_len);
        if(n<0){
            perror("recvfrom");
            return -1;
        }else{
            printf("receive msg from %s(port=%d) len %d: %s\n",inet_ntoa(cli.sin_addr), port, n, buf);
        }
    }

    return 0;
}