前几天在看日系图书《网络是怎么连接的》,今天就遇到了 Socket 的问题。算是浏览器连接的最基本的问题。

服务端(控制端)

accept() 是一个阻塞方法,等待客户端的连接,有程序连进来就可以使用 socket 发送指令了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from socket import *

# 1. 创建一个套接字
S = socket() # 创建 TCP 套接字对象

# 2. 套接字绑定一个端口
S.bind(('0.0.0.0', 8888)) # 绑定所有网卡,端口 8888

# 3. 开启监听
S.listen() # 开始监听传入连接(默认挂起队列 5 个)

# 4. 如果木马上线,申请连接后台
s, addr = S.accept() # 阻塞等待客户端连接
# 返回新套接字 s 和客户端地址 addr

# 设置功能
print('1.关机 2.重启')
choice = input('请选择:') # 控制台等待管理员输入 1 或 2
s.send(choice.encode()) # 将字符串编码为 bytes,通过 socket 发送给客户端

客户端(被控端 / 木马)

其实到这里还是最简单的 socket 通讯。黑客的部分是需要植入木马:把整个客户端程序在后台偷偷启动起来,然后连接到指定服务器的地址。

公司/家庭的防火墙通常拦截外部连入,但放行内部主动连出。所以肉鸡主动连黑客,更容易穿透(有点反弹 shell 那味了)。

1024 是缓冲区大小,表示本次 recv 最多读取 1024 字节

这里的 demo 使用 127.0.0.1。连接之后就可以从服务器收指令了,然后写业务逻辑,用 print 替代了原本的 os 模块。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import os
from socket import *

# 1. 创建一个套接字
s = socket()

# 套接字申请连接后台的号码
s.connect(('127.0.0.1', 8888)) # 主动连接服务器 IP + 端口

# 设置功能
choice = s.recv(1024).decode() # 从服务器接收数据(最多 1024 字节),解码为字符串

if choice == '1':
print("执行关机指令")
elif choice == '2':
print("执行重启指令")

核心要点总结

要点说明
S.accept()阻塞等待,返回新套接字 s 和客户端地址
0.0.0.0监听所有网卡
127.0.0.1本地回环,仅本机测试
反向连接肉鸡主动连控制端,穿透防火墙
recv(1024)单次最多读 1024 字节,超量需多次接收
print 替代 os.system学习阶段安全做法