前言
在使用nmap
挂nps
的socks5
时候
proxychains nmap -Pn -sT 127.0.0.1 -p 99
会扫任何端口都打开
socks5认证部分请参考
socks5认证过程
代码查看
在nps\server\proxy\socks5.go
中
首先打开start
,调用handleConn
//start
func (s *Sock5ModeServer) Start() error {
return conn.NewTcpListenerAndProcess(s.task.ServerIp+":"+strconv.Itoa(s.task.Port), func(c net.Conn) {
if err := s.CheckFlowAndConnNum(s.task.Client); err != nil {
logs.Warn("client id %d, task id %d, error %s, when socks5 connection", s.task.Client.Id, s.task.Id, err.Error())
c.Close()
return
}
logs.Trace("New socks5 connection,client %d,remote address %s", s.task.Client.Id, c.RemoteAddr())
s.handleConn(c)
s.task.Client.AddConn()
}, &s.listener)
}
跟入handleConn
//new conn
func (s *Sock5ModeServer) handleConn(c net.Conn) {
buf := make([]byte, 2)
if _, err := io.ReadFull(c, buf); err != nil {
logs.Warn("negotiation err", err)
c.Close()
return
}
if version := buf[0]; version != 5 {
logs.Warn("only support socks5, request from: ", c.RemoteAddr())
c.Close()
return
}
nMethods := buf[1]
methods := make([]byte, nMethods)
if len, err := c.Read(methods); len != int(nMethods) || err != nil {
logs.Warn("wrong method")
c.Close()
return
}
if (s.task.Client.Cnf.U != "" && s.task.Client.Cnf.P != "") || (s.task.MultiAccount != nil && len(s.task.MultiAccount.AccountMap) > 0) {
buf[1] = UserPassAuth
c.Write(buf)
if err := s.Auth(c); err != nil {
c.Close()
logs.Warn("Validation failed:", err)
return
}
} else {
buf[1] = 0
c.Write(buf)
}
s.handleRequest(c)
}
解析socks5
并且认证,完成后调用handleRequest
//req
func (s *Sock5ModeServer) handleRequest(c net.Conn) {
/*
The SOCKS request is formed as follows:
+----+-----+-------+------+----------+----------+
|VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
+----+-----+-------+------+----------+----------+
| 1 | 1 | X'00' | 1 | Variable | 2 |
+----+-----+-------+------+----------+----------+
*/
header := make([]byte, 3)
_, err := io.ReadFull(c, header)
if err != nil {
logs.Warn("illegal request", err)
c.Close()
return
}
switch header[1] {
case connectMethod:
s.handleConnect(c)
case bindMethod:
s.handleBind(c)
case associateMethod:
s.handleUDP(c)
default:
s.sendReply(c, commandNotSupported)
c.Close()
}
}
解析socks5
的method
正常使用的是handleConnect
//conn
func (s *Sock5ModeServer) handleConnect(c net.Conn) {
s.doConnect(c, connectMethod)
}
调用doConnect
//do conn
func (s *Sock5ModeServer) doConnect(c net.Conn, command uint8) {
addrType := make([]byte, 1)
c.Read(addrType)
var host string
switch addrType[0] {
case ipV4:
ipv4 := make(net.IP, net.IPv4len)
c.Read(ipv4)
host = ipv4.String()
case ipV6:
ipv6 := make(net.IP, net.IPv6len)
c.Read(ipv6)
host = ipv6.String()
case domainName:
var domainLen uint8
binary.Read(c, binary.BigEndian, &domainLen)
domain := make([]byte, domainLen)
c.Read(domain)
host = string(domain)
default:
s.sendReply(c, addrTypeNotSupported)
return
}
var port uint16
binary.Read(c, binary.BigEndian, &port)
// connect to host
addr := net.JoinHostPort(host, strconv.Itoa(int(port)))
var ltype string
if command == associateMethod {
ltype = common.CONN_UDP
} else {
ltype = common.CONN_TCP
}
s.DealClient(conn.NewConn(c), s.task.Client, addr, nil, ltype, func() {
s.sendReply(c, succeeded)
}, s.task.Flow, s.task.Target.LocalProxy)
return
}
解析地址格式之后把地址传输给客户端,调用DealClient
在nps\server\proxy\base.go
//create a new connection and start bytes copying
func (s *BaseServer) DealClient(c *conn.Conn, client *file.Client, addr string, rb []byte, tp string, f func(), flow *file.Flow, localProxy bool) error {
link := conn.NewLink(tp, addr, client.Cnf.Crypt, client.Cnf.Compress, c.Conn.RemoteAddr().String(), localProxy)
if target, err := s.bridge.SendLinkInfo(client.Id, link, s.task); err != nil {
logs.Warn("get connection from client id %d error %s", client.Id, err.Error())
c.Close()
return err
} else {
if f != nil {
f()
}
conn.CopyWaitGroup(target, c.Conn, link.Crypt, link.Compress, client.Rate, flow, true, rb)
}
return nil
}
这里调用SendLinkInfo
发送地址
在nps\bridge\bridge.go
func (s *Bridge) SendLinkInfo(clientId int, link *conn.Link, t *file.Tunnel) (target net.Conn, err error) {
//if the proxy type is local
if link.LocalProxy {
target, err = net.Dial("tcp", link.Host)
return
}
if v, ok := s.Client.Load(clientId); ok {
//If ip is restricted to do ip verification
if s.ipVerify {
ip := common.GetIpByAddr(link.RemoteAddr)
if v, ok := s.Register.Load(ip); !ok {
return nil, errors.New(fmt.Sprintf("The ip %s is not in the validation list", ip))
} else {
if !v.(time.Time).After(time.Now()) {
return nil, errors.New(fmt.Sprintf("The validity of the ip %s has expired", ip))
}
}
}
var tunnel *nps_mux.Mux
if t != nil && t.Mode == "file" {
tunnel = v.(*Client).file
} else {
tunnel = v.(*Client).tunnel
}
if tunnel == nil {
err = errors.New("the client connect error")
return
}
if target, err = tunnel.NewConn(); err != nil {
return
}
if t != nil && t.Mode == "file" {
//TODO if t.mode is file ,not use crypt or compress
link.Crypt = false
link.Compress = false
return
}
if _, err = conn.NewConn(target).SendInfo(link, ""); err != nil {
logs.Info("new connect error ,the target %s refuse to connect", link.Host)
return
}
} else {
err = errors.New(fmt.Sprintf("the client %d is not connect", clientId))
}
return
}
首先是进行一系列判断,包括功能和白名单
这里调用SendInfo
,应该是新建一个多路复用的tunnel之后传输数据
nps\lib\conn\conn.go
//send info
func (s *Conn) SendInfo(t interface{}, flag string) (int, error) {
/*
The task info is formed as follows:
+----+-----+---------+
|type| len | content |
+----+---------------+
| 4 | 4 | ... |
+----+---------------+
*/
raw := bytes.NewBuffer([]byte{})
if flag != "" {
binary.Write(raw, binary.LittleEndian, []byte(flag))
}
b, err := json.Marshal(t)
if err != nil {
return 0, err
}
lenBytes, err := GetLenBytes(b)
if err != nil {
return 0, err
}
binary.Write(raw, binary.LittleEndian, lenBytes)
return s.Write(raw.Bytes())
}
回到DealClient
//create a new connection and start bytes copying
func (s *BaseServer) DealClient(c *conn.Conn, client *file.Client, addr string, rb []byte, tp string, f func(), flow *file.Flow, localProxy bool) error {
link := conn.NewLink(tp, addr, client.Cnf.Crypt, client.Cnf.Compress, c.Conn.RemoteAddr().String(), localProxy)
if target, err := s.bridge.SendLinkInfo(client.Id, link, s.task); err != nil {
logs.Warn("get connection from client id %d error %s", client.Id, err.Error())
c.Close()
return err
} else {
if f != nil {
f()
}
conn.CopyWaitGroup(target, c.Conn, link.Crypt, link.Compress, client.Rate, flow, true, rb)
}
return nil
}
可以看到在调用SendLinkInfo
之后,只是确认服务端和客户端通信,不是按照socks5来回传地址是否可连接,而是直接返回socks5可连接。