内网穿透工具--Lanproxy流量特征检测分析

工具介绍

项目地址
https://github.com/ffay/lanproxy
工具简介
lanproxy是一个将局域网个人电脑、服务器代理到公网的内网穿透工具,支持tcp流量转发,可支持任何tcp上层协议。
功能特点

  • 穿透基础功能,同开源版本,高性能,可同时支持数万穿透连接
  • 全新界面UI,操作简单,部署简单(java+mysql)
  • 自定义域名绑定,为你穿透端口绑定域名,不再是IP+端口裸奔访问
  • 自定义域名ssl证书,也可为你绑定的域名开启ssl证书自动申请与续期,无需人工干涉
  • 自定义客户端离线展示页面,可以利用该功能展示一些html单页
  • 支持http/https/socks5多种模式使用客户端网络代理上网,家里轻松访问公司网络
  • 多用户支持,同时满足多人日常穿透需求

流量&逻辑分析

环境&测试准备

环境配置
内部服务器Ubantu 192.168.2.129 Lanproxy客户端
中转服务器 Ubantu https://lanp.nioee.com/ 提供的官方地址(或者自己搭建也可以)
测试流程

  1. 按照Github上的提示,先生成好对应的客户端和服务端,然后配置好Ubantu相关配置

image.png

  1. 然后运行start.sh

image.png

  1. 此时上官网发现相关连接已经建立

image.png

  1. 找一台外网vps测试ssh连接

在这里插入图片描述

检测特征和流量分析

一般情况下存在两种连接方式,一种是带SSL证书的连接和数据传输,默认对应端口为4993;另一种是不带的SSL证书的连接方式,默认端口为4900。

客户端逻辑分析

下载好相关项目之后,通过看start.bat能发现入口函数为org.fengfei.lanproxy.client.ProxyClientContaineimage.png
简要跟踪了一下相关的函数和功能,主要功能入口函数在connectProxyServer

private void connectProxyServer() {
    //实际上调用了doConnect用于建立和对应服务器的三次握手连接
    bootstrap.connect(config.getStringValue("server.host"), config.getIntValue("server.port")).addListener(new ChannelFutureListener() {
        @Override
        public void operationComplete(ChannelFuture future) throws Exception {
            if (future.isSuccess()) {
                // 连接成功,向服务器发送客户端认证信息(clientKey)
                // 设置通信信道
                ClientChannelMannager.setCmdChannel(future.channel());
                // 生成ProxyMessage对象,对象实例内容如下
                // ProxyMessage [type=0, serialNumber=0, uri=null, data=null]
                ProxyMessage proxyMessage = new ProxyMessage();
                // 设置类型为C_TYPE_AUTH(1)
                proxyMessage.setType(ProxyMessage.C_TYPE_AUTH);
                // 设置uri为认证的key
                proxyMessage.setUri(config.getStringValue("client.key"));
                //用于发送数据,调用writeAndFlush方法,就是Netty编码流
                future.channel().writeAndFlush(proxyMessage);
                sleepTimeMill = 1000;
                logger.info("connect proxy server success, {}", future.channel());
            } else {
                logger.warn("connect proxy server failed", future.cause());

                // 连接失败,发起重连
                reconnectWait();
                connectProxyServer();
            }
        }
    });
}

关于Netty的writeAndFlush可参考链接:https://juejin.cn/post/7010924544378535950
关于定义的encode方法如下:
image.png
其中头部长度为10,数据长度取决于uri的长度也就是配置文件中client.key的长度,以及data的长度。初始的第一个链接请求,其data为null,之后写入顺序和逻辑如下:

out.writeInt(bodyLength) // 4字节,表示所有数据的长度 这里为00 00 00 2a(key默认生成为32位)
out.writeByte(msg.getType()) // 1字节,这里为01
out.writeLong(msg.getSerialNumber()) // 8字节,这里为00 00 00 00 00 00 00 00
out.writeByte((byte) uriBytes.length); //1字节,这里为20(32位长度)
out.writeBytes(uriBytes) //这里为32位数长度,具体看情况,key可自定义

客户端主要接受3种类型数据
image.png
处理逻辑存在差异,但是思想差不多,cmd通道和数据通道,cmd通过用于命令传输,数据通道用于实际数据通讯,数据通道的类型必定为先03后05,格式满足ProxyMessage的类型

服务端逻辑分析

详细的数据处理流程和Cleint端基本完全一致,具体的处理逻辑在ServerChannelHandler中,针对不同的请求类型做不同的处理逻辑:
在这里插入图片描述

针对初始请求的C_TYPE_AUTH类型的处理逻辑如下:

private void handleAuthMessage(ChannelHandlerContext ctx, ProxyMessage proxyMessage) {
    //获取client发送过来数据中的client.key
    String clientKey = proxyMessage.getUri();
    //获取代理客户端对应的代理服务器端口
    List<Integer> ports = ProxyConfig.getInstance().getClientInetPorts(clientKey);
    //如果没找到对应clientkey的对应配置,则关闭连接
    if (ports == null) {
        logger.info("error clientKey {}, {}", clientKey, ctx.channel());
        ctx.channel().close();
        return;
    }
	//检查是否存在相关的命令通信通道
    Channel channel = ProxyChannelManager.getCmdChannel(clientKey);
    if (channel != null) {
        logger.warn("exist channel for key {}, {}", clientKey, channel);
        ctx.channel().close();
        return;
    }

    logger.info("set port => channel, {}, {}, {}", clientKey, ports, ctx.channel());
    //加入命令通信信道中
    ProxyChannelManager.addCmdChannel(ports, clientKey, ctx.channel());
}

服务端在这一步中不会发送相关数据,后面可能会发送一些心跳包之类的数据,数据格式固定为

00 00 00 0a 07 00 00 00 00 00 00 00 00

关于其他ProxyMessage的不同请求的处理逻辑,主要分析TYPE_CONNECT,其他都类似:
ProxyMessage.TYPE_CONNECT
服务端的发送逻辑处理在UserChannelHandler中实现,服务端在接收到来自其他的连接,连接client和serrver的对应端口时,最终会调用channelActive进行一系列处理,包括初始化connect类型的proxyMessage,之后会调用writeAndFlush进行发送,通过层层调用最终会到ProxyMessageEncoder的encode方法进行发送处理
在这里插入图片描述
encode方法在client中做过相关的说明和跟踪,这里不继续详细分析,直接给相关的结果
image.png

out.writeInt(bodyLength) // 4字节,表示所有数据的长度,这里长度不固定主要和lan设置有关
out.writeByte(msg.getType()) // 1字节,这里为03
out.writeLong(msg.getSerialNumber()) // 8字节,这里为00 00 00 00 00 00 00 00
out.writeByte((byte) uriBytes.length); //1字节,这里为AtomicLong(0) addAndGet()的值,不确定长度和内容
out.writeBytes(uriBytes) //具体看情况,长度内容不固定
out.writeBytes(msg.getData()) //ip:port形式,具体看生成的客户端配置,比如我的是127.0.0.1:22

注:serialNumber在通过查找上下文引用和初始化中,都未发现有被特殊设置的痕迹,由client端生成,默认都是00 00 00 00 00 00 00 00,server端设置为和client逻辑相同的id
配置客户端的后端IP端口只允许Ip:PORT形式,不然client端调用InetSocketAddress会失败

TLS特征

如果配置文件中启用了TLS,那么会使用conf目录下的jks文件来进行证书校验和验证,如果使用自定义证书检测存在困难,如果使用默认的test,jks可以做部分检测,使用keytool来查看默认的jks内容如下:

keytool -list -v -keystore test.jks #密码为123456


密钥库类型: JKS
密钥库提供方: SUN

您的密钥库包含 1 个条目

别名: test
创建日期: 201759日
条目类型: PrivateKeyEntry
证书链长度: 1
证书[1]:
所有者: CN=f, OU=ts, O=ts, L=bj, ST=bj, C=cn
发布者: CN=f, OU=ts, O=ts, L=bj, ST=bj, C=cn
序列号: 18b6ca05
生效时间: Tue May 09 18:16:01 CST 2017, 失效时间: Wed May 09 18:16:01 CST 2018
证书指纹:
	 SHA1: B9:87:38:E0:51:4C:B7:5D:F0:9B:6F:E0:19:74:6D:5A:FD:92:FD:88
	 SHA256: B6:39:F9:ED:7C:58:C8:5A:23:59:79:BD:D8:7D:CC:ED:D4:48:17:49:1D:21:31:3D:28:6B:28:89:30:DE:4A:04
签名算法名称: SHA256withRSA
主体公共密钥算法: 1024 位 RSA 密钥 ()
版本: 3

扩展: 

#1: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 9F 3F EF FD 18 9E 14 E9   B6 DC 97 7C CE 30 03 70  .?...........0.p
0010: 1D AF 06 65                                        ...e
]
]



*******************************************
*******************************************

其中由于TLS1.3的传输特性,其中关于证书的部分都不能直接以明文的形式看到,只能针对TLS1.1和TLS1.2做检测,主要是TLS1.2(1.1基本已被废弃)
image.png
直接检测证书的相关字段即可

  • 26
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值