2025第一届轩辕杯--Crypto--WriteUp
2025第一届轩辕杯–Crypto–WriteUp
Crypto
easyrsa
task
e = 65537
n = 1000000000000000000000000000156000000000000000000000000005643
c = 418535905348643941073541505434424306523376401168593325605206
exp
from Crypto.Util.number import inverse, long_to_bytese = 65537
n = 1000000000000000000000000000156000000000000000000000000005643
c = 418535905348643941073541505434424306523376401168593325605206# yafu
p = 1000000000000000000000000000099
q = 1000000000000000000000000000057phi_n = (p - 1) * (q - 1)
d = inverse(e, phi_n)
m = pow(c, d, n)flag = long_to_bytes(m)
print(b'flag{' + flag + b'}') # b'xuanyuanbei_easy_rsa!'
# flag{xuanyuanbei_easy_rsa!}
简单编码
exp
ciphertext = "ABBAABB ABBABAB ABABAAA ABABAAB ABBBBAA ABBAABA ABABBAA ABBAAAA ABBAAAB ABBABAB ABBBAAA ABAABBB ABABBAA ABABABB ABABBAA ABBABBB ABBABAA ABABABA ABAABAB ABBBAAA ABBBABA ABABBAB ABBBBAA ABABBAB ABBBAAA ABBABAB ABBAABA ABABAAA ABABABA AABBAB ABBBABB ABBAABA ABBABAB AABABA ABBBBAA ABBBAAB ABBAABA AABBAB ABABBAA ABBAAAB ABBBAAA ABBABAB ABBABAA ABABABB ABBBABA ABABABB ABBAABB ABBABAA ABBABAB ABBABAB ABABAAA ABBBABA AABABB ABABBAB AABBAB ABABAAA ABBAAAB ABBBBAB ABBBAAA ABABABA ABBAAAA ABABAAB ABABABB ABBABBA ABBABAB AABABA ABBABAA ABBBABA ABBBABA AABBAA ABBBBAA ABBAAAA ABABBBB ABBABAB ABABABB ABAABBB ABBAAAA ABABAAA ABABABB ABBABAA ABBABBA ABABABA ABAABAB ABABABA AABABB ABABBAB ABBBBAA ABBBBAB ABBBAAA ABABAAB ABBABBB ABABAAB ABBAAAA ABAABAB ABBBABB ABBABAA ABBABAB ABABABA ABAABAB ABBBABA ABBAABA AABBAB ABABBAA ABAABAB ABBBAAA ABBABAB ABBBABA ABAABBB ABABBBA ABABABB ABABBAA ABBABBB ABBABAA ABAABAB ABABABA ABBBAAB ABABBAA ABBAABA ABABBAA ABAABAB ABBBAAA ABBABAB ABBABBB ABBBABB ABBBABA ABABBAA ABBABAB ABABABA ABBAABA ABAABAB ABBAABA ABBABBB ABBBAAA ABBAABA ABBBBAA ABBAAAA ABBABAA ABABBAB ABBABAA ABAABBB ABABABA ABABABB ABABABB AABBAB ABBAAAB ABBBBAB ABABABA ABBBABA AABBAB ABABABA ABBABAB ABBBAAB ABBBAAA ABBAAAB ABBBBAA ABBBBAA ABBABAA ABBAABA AABBAB ABBBABA"
my_dict = {"A":"1", "B":"0"} # 注:因为所有的均为A开头,所以A-->1; B-->0
temp = "".join([" " if c == " " else my_dict[c] for c in ciphertext])
print(temp)
此后,利用随波逐流:
二进制转字符–>base32–>URL–>base64–>栅栏(5)
flag{c04d6e34aab689c5c0e68eb51753c843e032efa7c16427f8642ee07ab946e981}
dp
task
n = 110231451148882079381796143358970452100202953702391108796134950841737642949460527878714265898036116331356438846901198470479054762675790266666921561175879745335346704648242558094026330525194100460497557690574823790674495407503937159099381516207615786485815588440939371996099127648410831094531405905724333332751 dp = 3086447084488829312768217706085402222803155373133262724515307236287352098952292947424429554074367555883852997440538764377662477589192987750154075762783925 c = 59325046548488308883386075244531371583402390744927996480498220618691766045737849650329706821216622090853171635701444247741920578127703036446381752396125610456124290112692914728856924559989383692987222821742728733347723840032917282464481629726528696226995176072605314263644914703785378425284460609365608120126 e = 65537
analysis
dp泄露,RSA攻击基础题型。
exp
from Crypto.Util.number import *n = 110231451148882079381796143358970452100202953702391108796134950841737642949460527878714265898036116331356438846901198470479054762675790266666921561175879745335346704648242558094026330525194100460497557690574823790674495407503937159099381516207615786485815588440939371996099127648410831094531405905724333332751
dp = 3086447084488829312768217706085402222803155373133262724515307236287352098952292947424429554074367555883852997440538764377662477589192987750154075762783925
c = 59325046548488308883386075244531371583402390744927996480498220618691766045737849650329706821216622090853171635701444247741920578127703036446381752396125610456124290112692914728856924559989383692987222821742728733347723840032917282464481629726528696226995176072605314263644914703785378425284460609365608120126
e = 65537for i in range(1, e): if(dp * e - 1) % i == 0:if n % (((dp * e - 1) // i) + 1) == 0: p = ((dp * e - 1) // i) + 1q = n // (((dp * e - 1) // i) + 1)phi = (q - 1) * (p - 1) d = inverse(e, phi) m = pow(c, d, n) print(long_to_bytes(m))
# flag{C5G0_1s_the_8eSt_FPS_G@m3}
babyrsa
task
from Crypto.Util.number import *
from gmpy2 import *
from random import choice
flag = b"flag{****************************}"
m = bytes_to_long(flag)
p = getPrime(256)
q = getPrime(256)
n = p*q
d = getPrime(130)
phi = (p-1)*(q-1)
e = invert(d, phi)
c = pow(m, e, n)
print(f'n = {n}')
print(f'c = {c}')
# print(f'e = {e}')def gen(bits):while True:p = 2while p.bit_length() < bits:p *= choice(sieve_base)if isPrime(p - 1):return p - 1p1 = gen(256)
q1 = gen(256)
n1 = p1 * q1
c1 = p1 + eprint(f'n1 = {n1}')
print(f'c1 = {c1}')'''n = 10037257627154486608196774801095855162090578704439233219876490744017222686494761706171113312036056644757212254824459536550416291797454693336043852190135363
c = 6723803125309437675713195914771839852631361554645954138639198200804046718848872479140347495288135138109762940384847808522874831433140182790750890982139835
n1 = 151767047787614712083974720416865469041528766980347881592164779139223941980832935534609228636599644744364450753148219193621511377088383418096756216139022880709
c1 = 6701513605196718137208327145211106525052740242222174201768345944717813148931922063338128366155730924516887607710111701686062781667128443135522927486682574
'''
analysis
采用了RSA对密文进行了加密,准确给出了n
,c
。接着再使用了RSA对e
进行了隐藏。
我们需要针对于第二次的RSA进行破解恢复e
,之后我们就拿到了第一次RSA的n,e,c
据此进行明文m
的恢复。虽然n
的位数不是很高,但是针对于直接分解,都是有很明确的攻击方向的。
第二次加密的算法利用浏览器搜索可以直接搜到p+1
光滑的攻击方法,此后第一个搜索的话应该可以搜索到winer
攻击。但是根据验证发现并不满足winer
攻击的条件,但是可以顺着winer
攻击的低解密指数的方向寻找,boneh and durfee攻击
找个模板代码进行数据替换即可。
exp
p+1光滑攻击
from itertools import count
from math import gcd, isqrt
from tqdm import trangedef mlucas(v, a, n):v1, v2 = v, (v ** 2 - 2) % nfor bit in bin(a)[3:]: v1, v2 = ((v1 ** 2 - 2) % n, (v1 * v2 - v) % n) if bit == "0" else ((v1 * v2 - v) % n, (v2 ** 2 - 2) % n)return v1def primegen():yield 2yield 3yield 5yield 7yield 11yield 13ps = primegen() # yay recursionp = ps.__next__() and ps.__next__()q, sieve, n = p ** 2, {}, 13while True:if n not in sieve:if n < q:yield nelse:next, step = q + 2 * p, 2 * pwhile next in sieve:next += stepsieve[next] = stepp = ps.__next__()q = p ** 2else:step = sieve.pop(n)next = n + stepwhile next in sieve:next += stepsieve[next] = stepn += 2def ilog(x, b): # greatest integer l such that b**l <= x.l = 0while x >= b:x /= bl += 1return ldef attack(n):for v in count(1):for p in primegen():e = ilog(isqrt(n), p)if e == 0:breakfor _ in trange(e):v = mlucas(v, p, n)g = gcd(v - 2, n)if 1 < g < n:return int(g), int(n // g) # g|nif g == n:break
n1 = 151767047787614712083974720416865469041528766980347881592164779139223941980832935534609228636599644744364450753148219193621511377088383418096756216139022880709
print(attack(n1))
# (647625598040937990477179775340017395831855498212348808173836982264933068647233, 234343806431846981391062476356400447729334179333927516463017977438646752515331973)
boneh and durfee攻击
from Crypto.Util.number import *
import gmpy2n = 10037257627154486608196774801095855162090578704439233219876490744017222686494761706171113312036056644757212254824459536550416291797454693336043852190135363
c = 6723803125309437675713195914771839852631361554645954138639198200804046718848872479140347495288135138109762940384847808522874831433140182790750890982139835
n1 = 151767047787614712083974720416865469041528766980347881592164779139223941980832935534609228636599644744364450753148219193621511377088383418096756216139022880709
c1 = 6701513605196718137208327145211106525052740242222174201768345944717813148931922063338128366155730924516887607710111701686062781667128443135522927486682574# print(n1.bit_length()) 526
################################################### p + 1光滑p1 = 647625598040937990477179775340017395831855498212348808173836982264933068647233
q1 = 234343806431846981391062476356400447729334179333927516463017977438646752515331973
assert p1 * q1 == n1 and isPrime(p1) and isPrime(q1)
e1 = c1 - p1
e2 = c1 - q1
print(f"e1 = {e1}")
print(f"e2 = {e2}")"""
e1 = 647625598040937990477179775340017395831855498212348808173836982264933068647233
e2 = 6701513605196718137208327145211106525052740242222174201768345944717813148697578256906281384764668448160487159980777522352135265204110465696876174971350601
"""
# sage
from Crypto.Util.number import *
# from __future__ import print_function
import time############################################
# Config
##########################################"""
Setting debug to true will display more informations
about the lattice, the bounds, the vectors...
"""
debug = True"""
Setting strict to true will stop the algorithm (and
return (-1, -1)) if we don't have a correct
upperbound on the determinant. Note that this
doesn't necesseraly mean that no solutions
will be found since the theoretical upperbound is
usualy far away from actual results. That is why
you should probably use `strict = False`
"""
strict = False"""
This is experimental, but has provided remarkable results
so far. It tries to reduce the lattice as much as it can
while keeping its efficiency. I see no reason not to use
this option, but if things don't work, you should try
disabling it
"""
helpful_only = True
dimension_min = 7 # stop removing if lattice reaches that dimension############################################
# Functions
########################################### display stats on helpful vectors
def helpful_vectors(BB, modulus):nothelpful = 0for ii in range(BB.dimensions()[0]):if BB[ii,ii] >= modulus:nothelpful += 1print(nothelpful, "/", BB.dimensions()[0], " vectors are not helpful")# display matrix picture with 0 and X
def matrix_overview(BB, bound):for ii in range(BB.dimensions()[0]):a = ('%02d ' % ii)for jj in range(BB.dimensions()[1]):a += '0' if BB[ii,jj] == 0 else 'X'if BB.dimensions()[0] < 60:a += ' 'if BB[ii, ii] >= bound:a += '~'print(a)# tries to remove unhelpful vectors
# we start at current = n-1 (last vector)
def remove_unhelpful(BB, monomials, bound, current):# end of our recursive functionif current == -1 or BB.dimensions()[0] <= dimension_min:return BB# we start by checking from the endfor ii in range(current, -1, -1):# if it is unhelpful:if BB[ii, ii] >= bound:affected_vectors = 0affected_vector_index = 0# let's check if it affects other vectorsfor jj in range(ii + 1, BB.dimensions()[0]):# if another vector is affected:# we increase the countif BB[jj, ii] != 0:affected_vectors += 1affected_vector_index = jj# level:0# if no other vectors end up affected# we remove itif affected_vectors == 0:print("* removing unhelpful vector", ii)BB = BB.delete_columns([ii])BB = BB.delete_rows([ii])monomials.pop(ii)BB = remove_unhelpful(BB, monomials, bound, ii-1)return BB# level:1# if just one was affected we check# if it is affecting someone elseelif affected_vectors == 1:affected_deeper = Truefor kk in range(affected_vector_index + 1, BB.dimensions()[0]):# if it is affecting even one vector# we give up on this oneif BB[kk, affected_vector_index] != 0:affected_deeper = False# remove both it if no other vector was affected and# this helpful vector is not helpful enough# compared to our unhelpful oneif affected_deeper and abs(bound - BB[affected_vector_index, affected_vector_index]) < abs(bound - BB[ii, ii]):print("* removing unhelpful vectors", ii, "and", affected_vector_index)BB = BB.delete_columns([affected_vector_index, ii])BB = BB.delete_rows([affected_vector_index, ii])monomials.pop(affected_vector_index)monomials.pop(ii)BB = remove_unhelpful(BB, monomials, bound, ii-1)return BB# nothing happenedreturn BB"""
Returns:
* 0,0 if it fails
* -1,-1 if `strict=true`, and determinant doesn't bound
* x0,y0 the solutions of `pol`
"""
def boneh_durfee(pol, modulus, mm, tt, XX, YY):"""Boneh and Durfee revisited by Herrmann and Mayfinds a solution if:* d < N^delta* |x| < e^delta* |y| < e^0.5whenever delta < 1 - sqrt(2)/2 ~ 0.292"""# substitution (Herrman and May)PR.<u, x, y> = PolynomialRing(ZZ)Q = PR.quotient(x*y + 1 - u) # u = xy + 1polZ = Q(pol).lift()UU = XX*YY + 1# x-shiftsgg = []for kk in range(mm + 1):for ii in range(mm - kk + 1):xshift = x^ii * modulus^(mm - kk) * polZ(u, x, y)^kkgg.append(xshift)gg.sort()# x-shifts list of monomialsmonomials = []for polynomial in gg:for monomial in polynomial.monomials():if monomial not in monomials:monomials.append(monomial)monomials.sort()# y-shifts (selected by Herrman and May)for jj in range(1, tt + 1):for kk in range(floor(mm/tt) * jj, mm + 1):yshift = y^jj * polZ(u, x, y)^kk * modulus^(mm - kk)yshift = Q(yshift).lift()gg.append(yshift) # substitution# y-shifts list of monomialsfor jj in range(1, tt + 1):for kk in range(floor(mm/tt) * jj, mm + 1):monomials.append(u^kk * y^jj)# construct lattice Bnn = len(monomials)BB = Matrix(ZZ, nn)for ii in range(nn):BB[ii, 0] = gg[ii](0, 0, 0)for jj in range(1, ii + 1):if monomials[jj] in gg[ii].monomials():BB[ii, jj] = gg[ii].monomial_coefficient(monomials[jj]) * monomials[jj](UU,XX,YY)# Prototype to reduce the latticeif helpful_only:# automatically removeBB = remove_unhelpful(BB, monomials, modulus^mm, nn-1)# reset dimensionnn = BB.dimensions()[0]if nn == 0:print("failure")return 0,0# check if vectors are helpfulif debug:helpful_vectors(BB, modulus^mm)# check if determinant is correctly boundeddet = BB.det()bound = modulus^(mm*nn)if det >= bound:print("We do not have det < bound. Solutions might not be found.")print("Try with highers m and t.")if debug:diff = (log(det) - log(bound)) / log(2)print("size det(L) - size e^(m*n) = ", floor(diff))if strict:return -1, -1else:print("det(L) < e^(m*n) (good! If a solution exists < N^delta, it will be found)")# display the lattice basisif debug:matrix_overview(BB, modulus^mm)# LLLif debug:print("optimizing basis of the lattice via LLL, this can take a long time")BB = BB.LLL()if debug:print("LLL is done!")# transform vector i & j -> polynomials 1 & 2if debug:print("looking for independent vectors in the lattice")found_polynomials = Falsefor pol1_idx in range(nn - 1):for pol2_idx in range(pol1_idx + 1, nn):# for i and j, create the two polynomialsPR.<w,z> = PolynomialRing(ZZ)pol1 = pol2 = 0for jj in range(nn):pol1 += monomials[jj](w*z+1,w,z) * BB[pol1_idx, jj] / monomials[jj](UU,XX,YY)pol2 += monomials[jj](w*z+1,w,z) * BB[pol2_idx, jj] / monomials[jj](UU,XX,YY)# resultantPR.<q> = PolynomialRing(ZZ)rr = pol1.resultant(pol2)# are these good polynomials?if rr.is_zero() or rr.monomials() == [1]:continueelse:print("found them, using vectors", pol1_idx, "and", pol2_idx)found_polynomials = Truebreakif found_polynomials:breakif not found_polynomials:print("no independant vectors could be found. This should very rarely happen...")return 0, 0rr = rr(q, q)# solutionssoly = rr.roots()if len(soly) == 0:print("Your prediction (delta) is too small")return 0, 0soly = soly[0][0]ss = pol1(q, soly)solx = ss.roots()[0][0]#return solx, solydef example():############################################# How To Use This Script############################################ The problem to solve (edit the following values)## the modulusN = 10037257627154486608196774801095855162090578704439233219876490744017222686494761706171113312036056644757212254824459536550416291797454693336043852190135363# the public exponent# e = 0x19441f679c9609f2484eb9b2658d7138252b847b2ed8ad182be7976ed57a3e441af14897ce041f3e07916445b88181c22f510150584eee4b0f776a5a487a4472a99f2ddc95efdd2b380ab4480533808b8c92e63ace57fb42bac8315fa487d03bec86d854314bc2ec4f99b192bb98710be151599d60f224114f6b33f47e357517e = 6701513605196718137208327145211106525052740242222174201768345944717813148931274437740087428165253744741547590314279846187850432858954606153257994418035341e2 = 6701513605196718137208327145211106525052740242222174201768345944717813148697578256906281384764668448160487159980777522352135265204110465696876174971350601# the hypothesis on the private exponent (the theoretical maximum is 0.292)delta = .26 # this means that d < N^delta## Lattice (tweak those values)## you should tweak this (after a first run), (e.g. increment it until a solution is found)m = 5 # size of the lattice (bigger the better/slower)# you need to be a lattice master to tweak theset = int((1-2*delta) * m) # optimization from Herrmann and MayX = 2*floor(N^delta) # this _might_ be too muchY = floor(N^(1/2)) # correct if p, q are ~ same size## Don't touch anything below## Problem put in equationP.<x,y> = PolynomialRing(ZZ)A = int((N+1)/2)pol = 1 + x * (A + y)## Find the solutions!## Checking boundsif debug:print("=== checking values ===")print("* delta:", delta)print("* delta < 0.292", delta < 0.292)print("* size of e:", int(log(e)/log(2)))print("* size of N:", int(log(N)/log(2)))print("* m:", m, ", t:", t)# boneh_durfeeif debug:print("=== running algorithm ===")start_time = time.time()solx, soly = boneh_durfee(pol, e, m, t, X, Y)# found a solution?if solx > 0:print("=== solution found ===")if False:print("x:", solx)print("y:", soly)d = int(pol(solx, soly) / e)print("private key found:", d)c = 6723803125309437675713195914771839852631361554645954138639198200804046718848872479140347495288135138109762940384847808522874831433140182790750890982139835m = pow(c,d,N)print(long_to_bytes(int(m)))else:print("=== no solution was found ===")if debug:print(("=== %s seconds ===" % (time.time() - start_time)))if __name__ == "__main__":example()
# flag{39693fd4a45b386c28c63100cc930238259891a2}
这道题不知道有没有人尝试硬分解。
告白2009-01-23
- 首先,我在比赛第一天就进行了附件的下载,由于我先进行了
"babyrsa"
的解题,针对于winer
攻击,看起来就有莫名的熟悉感。之后进行winer
攻击即可获得RSA加密的私钥d
.之后我以为这道出在新生赛的题应该就到这里了,然后直接进行了long_to_bytes
发现结果是乱码。
from Crypto.Util.number import *n= 106907120255411141276638612258492580223206670508697860345280705552076099016030935898699700187523599766269485047282325650117035914628760419926410817774570995043643433455055595591107437470658308764074450729921003648782408533657438504280874574703167028727399770901329675528708585142713643443248769642817712218371
e= 92066298664485065396027178362270794902621018857568310802765263839921592653297188141639082907410773099588833460614099675385786190965706296547920850855064908555902716021514756109564555466796584126969045436871844375174789134742417250605776973188216013735765092101366990049447374275811804264794446656219369440535
c= 72413193823586193683552385578931939035012872670413497855056244201691512354415666469936125548748032982020958495114951719066245650644060153838816623502095911253320142088319318206119073607336497914311058118174988818658610257295726356030260769061712429926392969618604615189351858925626182197332313954336604548074############################################################# winer
import gmpy2
import libnumdef continuedFra(x, y):"""计算连分数:param x: 分子:param y: 分母:return: 连分数列表"""cf = []while y:cf.append(x // y)x, y = y, x % yreturn cf
def gradualFra(cf):"""计算传入列表最后的渐进分数:param cf: 连分数列表:return: 该列表最后的渐近分数"""numerator = 0denominator = 1for x in cf[::-1]:# 这里的渐进分数分子分母要分开numerator, denominator = denominator, x * denominator + numeratorreturn numerator, denominator
def solve_pq(a, b, c):"""使用韦达定理解出pq,x^2−(p+q)∗x+pq=0:param a:x^2的系数:param b:x的系数:param c:pq:return:p,q"""par = gmpy2.isqrt(b * b - 4 * a * c)return (-b + par) // (2 * a), (-b - par) // (2 * a)def getGradualFra(cf):"""计算列表所有的渐近分数:param cf: 连分数列表:return: 该列表所有的渐近分数"""gf = []for i in range(1, len(cf) + 1):gf.append(gradualFra(cf[:i]))return gfdef wienerAttack(e, n):""":param e::param n::return: 私钥d"""cf = continuedFra(e, n)gf = getGradualFra(cf)for d, k in gf:if k == 0: continueif (e * d - 1) % k != 0:continuephi = (e * d - 1) // kp, q = solve_pq(1, n - phi + 1, n)if p * q == n:return d
d = wienerAttack(e, n)
-
但是根据
task.py
前面的注释,(起初以为是英文,尝试读了一下,后面发现看不懂,豆包翻译,才发现是德语,注意这里的德语,划重点)。 -
之后我就把我解出的乱码以及提示,翻译丢给了deepseek,发现他给出的解决方法,但是都没能提交成功。之后我去通过群主大大联系出题人大大,被告知题目名称就是hint.在此告一段落……
-
比赛重新开始的第一天,由于附件进行了更新,我在题主不会说一句废话的基础上一个字一个字地扣,首先,
“打不出来给你们挂孙吧”
,我就去搜孙吧,后面越看越发懵,这孙吧是啥东西呀……
然后翻译了一下今天的德语内容:
-
之后我就用过浏览器搜索2009-1-23日表白,在这里我找到了这道题的关键点:
摩斯电码里的爱情 - analyzer - 博客园 -
我醍醐灌顶,昨天
task.py
中的“除了摩斯别的都用了”,以及今天的看着对信中的数字困惑不解。我就明白了,利用winer
攻击获得的私钥d
进行解密m
不要转字节就是原文中经过摩斯之后的内容,之后9键解密内容为:OISTTSEEOWOI
,之后利用单表替换密码进行解密内容为IHLEELCCIBIH
。然后吧,我就想着继续跟着文章走,应该马上就出来了,但是发现根本不是有意义的明文,并且提交不对。 -
这个时候我很懊恼这个脑洞题,注意到我们还有一段提示没有使用,那就是栅栏,我将最后的密文和提示丢给deepseek,发现还是不行,不论是脚本还是想法,都不是正确答案,但是deepseek给我提醒了一下,其中提示中所说的碰倒了第9根和第12根板条,但是栅栏完好无损,deepseek说这两个位置可以是任意的英文字母,这个时候,我明白了,然后我就去爆破。发现还是没有找到有意义的明文。最后我就先不爆破了,我直接用
_
进行占位,看看能不能找到有意义的明文。(为什么是有意义的明文,因为原文章中的I love you too;我觉得出题人不会大费周章地利用随机数的)。 -
之后我还是没有发现什么端倪,之后,我想了想,我要不要直接给deepseek,让他适当地加几个空格获取,毕竟他不会累,但是还是没有收获,这个时候,我想到了,
!!!题目注释是德语写的
会不会,这道题的答案也是德语,所以再次求救deepseek。这次有了新发现"ICH_LIEBE_DICH"是“我爱你”
-
但是我比对这个答案跟我原来求解的字母个数(因为原文是栅栏最后一步),发现我求解的字符个数中多个
L
少个D
.这个时候我要去看看是不是真的通过栅栏解密的,因为原来不知道德语,也看不明吧,这次带着这个相似的答案进行找,就看到了。
-
这里我不知道是出题人故意少个
D
多个L
。最后拿着flag{xxxx}去提交,发现还是不对,这个时候我就去请教群主大大,之后我尝试了如下答案之后,得到最后的flag{ich liebe dich}
.
单表替换的代码:
def decrypt_substitution(ciphertext: str) -> str:"""使用QWE=ABC顺序的单表替换进行解密参数:ciphertext: 密文字符串返回:解密后的明文字符串"""# 构建解密映射表 (QWE对应ABC,依次类推)# 获取字母表alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'# 构建替换表 (Q对应A, W对应B, E对应C, ...)# 这里使用Q作为起始点,按字母表顺序映射key = 'QWERTYUIOPASDFGHJKLZXCVBNM'# 确保key包含所有26个字母且不重复if len(set(key)) != 26 or len(key) != 26:raise ValueError("替换密钥必须包含所有26个字母且不重复")# 构建解密字典 (密文字母 -> 明文字母)decrypt_dict = {k: v for k, v in zip(key, alphabet)}# 执行解密plaintext = []for char in ciphertext.upper():if char in decrypt_dict:plaintext.append(decrypt_dict[char])else:# 非字母字符保持不变plaintext.append(char)return ''.join(plaintext)# 示例使用
if __name__ == "__main__":ciphertext = "Q" # 对应明文"HELL"plaintext = decrypt_substitution(ciphertext)print(f"密文: {ciphertext}")print(f"明文: {plaintext}")
进行flag格式爆破的代码:
s1 = "ICH_LIEBE_LICH"
s2 = "ICH_LIEBE_DICH"
s1 = s1.lower()
s2 = s2.lower()
print("flag{" + s1 + "}")
print("flag{" + s2 + "}")
s1 = s1.replace("_", " ", 2)
s2 = s2.replace("_", " ", 2)
print("flag{" + s1 + "}")
print("flag{" + s2 + "}")
s1 = s1.replace(" ", "", 2)
s2 = s2.replace(" ", "", 2)
print("flag{" + s1 + "}")
print("flag{" + s2 + "}")
前面尝试了六种形式的大写,发现都不对。
注:这个时候有人问为啥要用_
占位,因为随波逐粒进行栅栏的时候有时候会忽略空格,为了少些脚本,我利用_
占位。
DIladila
analysis
给出的分组密码没有采用CBC等分组密码的工作模式,因此我们可以采用逐四字符的穷举攻击,这对于正常的密码学而言是最有效的方式.
至于这个是不是预期解,等出题人大大放出wp吧。
不过经过不断修改代码逻辑,可以根据二分算法的思想,缩小需要爆破的范围,最后可以很会地爆破出来flag。
exp
from Crypto.Util.number import *def ror(val, r_bits, max_bits=16):return (val >> r_bits) | ((val << (max_bits - r_bits)) & (2**max_bits - 1))def decrypt(xys,key):may_xys = []for xy in xys:x,y = xytmp_y = bin(x^y)[2:].zfill(16)may_y = int(tmp_y[-2:]+tmp_y[:14],2)tmp_x = x^keymay_xs = []for xh in range(2 ** 9):for xl in range(2 ** 7):may_x = int(bin(xh)[2:].zfill(9)+bin(xl)[2:].zfill(7),2)if (ror(may_x, 7) + may_y) & 0xFFFF == tmp_x:may_xys.append((may_x, may_y))return may_xysciphertext = [
(57912, 19067),
(38342, 34089),
(16842, 41652),
(30292, 50979),
(9137, 57458),
(29822, 64285),
(33379, 14140),
(16514, 4653)
]
flag=b''
for c in ciphertext:keys = [0x1234, 0x5678, 0x9abc, 0xdef0]keys = keys[::-1]xys=[c]for _ in range(4):xys=decrypt(xys, keys[_])for xy in xys:x,y=xyflag+=(long_to_bytes(x)[::-1]+long_to_bytes(y)[::-1])
print(flag)
# flag{You_DIladila_Crypto_Matser}
古典密码
anlysis
- 给了四种加密,但是说五种,应该另一种就是凯撒。因为凯撒是仿射的特殊情况。单表替换、维吉尼亚、仿射、凯撒的顺序会影响最后的结果。但是栅栏只改变顺序。所以放在第几次解密都可以。
- 其中单表替换的密码表比价容易看出来,同时维吉尼亚的密钥也很容易看出来。接着就是5、8以及4、5.由于仿射加密需要两个密钥,即a,b同时需要满足
gcd(a, 26) = 1
这就表示5、8是仿射加密的密钥,而4、5是栅栏加密的密钥,在经过尝试后发现先5后4进行栅栏解密可以得到flag{}的形式。 - 我只需要爆破4!=24种解密顺序,每次凯撒解密的时候都进行25次密钥的爆破,之后进行先5后4的栅栏。最终寻找有没有
flag
就可以了。
exp
import itertools
from string import ascii_lowercase, ascii_uppercasedef caesar_decrypt(ciphertext, shift):decrypted = []for char in ciphertext:if char.islower():decrypted.append(chr((ord(char) - ord('a') - shift) % 26 + ord('a')))elif char.isupper():decrypted.append(chr((ord(char) - ord('A') - shift) % 26 + ord('A')))else:decrypted.append(char)return ''.join(decrypted)def vigenere_decrypt(ciphertext, key):decrypted = []key_index = 0for char in ciphertext:if char.isalpha():key_char = key[key_index % len(key)]key_shift = ord(key_char.lower()) - ord('a')if char.islower():decrypted.append(chr((ord(char) - ord('a') - key_shift) % 26 + ord('a')))else:decrypted.append(chr((ord(char) - ord('A') - key_shift) % 26 + ord('A')))key_index += 1else:decrypted.append(char)return ''.join(decrypted)def affine_decrypt(ciphertext, a, b):a_inv = 0for i in range(26):if (a * i) % 26 == 1:a_inv = ibreakdecrypted = []for char in ciphertext:if char.islower():decrypted.append(chr((a_inv * (ord(char) - ord('a') - b)) % 26 + ord('a')))elif char.isupper():decrypted.append(chr((a_inv * (ord(char) - ord('A') - b)) % 26 + ord('A')))else:decrypted.append(char)return ''.join(decrypted)def qwe_decrypt(ciphertext):qwe_mapping = {'q': 'a', 'w': 'b', 'e': 'c', 'r': 'd', 't': 'e', 'y': 'f', 'u': 'g', 'i': 'h', 'o': 'i', 'p': 'j','a': 'k', 's': 'l', 'd': 'm', 'f': 'n', 'g': 'o', 'h': 'p', 'j': 'q', 'k': 'r', 'l': 's', 'z': 't','x': 'u', 'c': 'v', 'v': 'w', 'b': 'x', 'n': 'y', 'm': 'z'}decrypted = []for char in ciphertext:if char.isalpha():if char.islower():decrypted.append(qwe_mapping.get(char, char))else:decrypted.append(qwe_mapping.get(char.lower(), char).upper())else:decrypted.append(char)return ''.join(decrypted)def main():ciphertext = "njih{ddolYScoikOWrlctrcc}"vigenere_key = "nxtcctf"affine_a = 5affine_b = 8transformations = [('caesar', lambda x, s: caesar_decrypt(x, s)),('vigenere', lambda x, k: vigenere_decrypt(x, k)),('affine', lambda x, a, b: affine_decrypt(x, a, b)),('qwe', lambda x: qwe_decrypt(x))]# 尝试所有可能的变换顺序for order in itertools.permutations(transformations, 4):# 尝试所有可能的凯撒偏移for caesar_shift in range(26):# 应用变换顺序current_text = ciphertextfor name, func in order:if name == 'caesar':current_text = func(current_text, caesar_shift)elif name == 'vigenere':current_text = func(current_text, vigenere_key)elif name == 'affine':current_text = func(current_text, affine_a, affine_b)elif name == 'qwe':current_text = func(current_text)# 检查是否包含flag格式if 'flag{' in current_text:print(f"找到可能的flag!")print(f"变换顺序: {[t[0] for t in order]}")print(f"凯撒偏移: {caesar_shift}")print(f"明文: {current_text}")print()if __name__ == "__main__":main()
"""
找到可能的flag!
变换顺序: ['vigenere', 'affine', 'qwe', 'caesar']
凯撒偏移: 3
明文: flag{nxtcNBflagNBctfflag}
"""
相关文章:
2025第一届轩辕杯--Crypto--WriteUp
2025第一届轩辕杯–Crypto–WriteUp Crypto easyrsa task e 65537 n 1000000000000000000000000000156000000000000000000000000005643 c 418535905348643941073541505434424306523376401168593325605206exp from Crypto.Util.number import inverse, long_to_bytese …...
人工智能范式:技术革命下的认知重构
当生成式AI能够自主创作内容、设计解决方案甚至编写程序时,我们正在见证的不仅是工具革新,更是一场认知范式的根本转变。人工智能范式正在重塑人类理解世界、解决问题和创造价值的基本方式——这种转变将重新定义未来十年的职业逻辑与知识体系。 一、范…...
python训练营打卡第30天
模块和库的导入 知识点回顾: 导入官方库的三种手段导入自定义库/模块的方式导入库/模块的核心逻辑:找到根目录(python解释器的目录和终端的目录不一致) 一、导入官方库 1.标准导入:导入整个库 import mathprint(&quo…...
第29天-python实现mysql数据增删改查
想用Python和Tkinter实现一个MySQL数据库的增删改查应用。首先,我需要确定用户的需求是什么。他们可能想要一个图形界面,方便操作数据库,而不需要直接写SQL语句。用户可能对Python和Tkinter有一定了解,但对如何整合数据库操作可能不太熟悉。 首先,我应该考虑如何设计界面。…...
2025.05.21华为暑期实习机考真题解析第三题
📌 点击直达笔试专栏 👉《大厂笔试突围》 💻 春秋招笔试突围在线OJ 👉 笔试突围OJ 03. GPU资源租赁优化 问题描述 A先生是一家云计算服务商的资源调度负责人,负责管理公司的GPU资源租赁业务。公司拥有多个高性能GPU核心,并按时间段出租给不同客户使用。每个客户有…...
Datawhale 5月llm-universe 第4次笔记
第四章 构建RAG应用 envs 在 Conda 中,envs 目录是用来存放虚拟环境的地方。 也就是说,你在运行: onda create -n llm-universe python3.10 时,Conda 就会在这个路径下创建一个新的文件夹: makefile D:\Users\…...
滑窗问题实验LC2653(一次遍历维持窗口元素保持排序)
在只有一次遍历(即滑窗每向右移动一步只处理新增元素和删除旧元素)的前提下,要维持当前窗口内元素的全局“可排序”结构 问题背景:LeetCode 2653 - 滑动子数组的美丽值 题目要求在大小为 k 的滑动窗口中,找到第 x 小…...
PHP学习笔记(八)
返回值 值通过可选参数的返回语句返回 return的使用 函数不能返回多个值,但可以通过返回一个数组来得到类似的效果 函数返回一个引用,必须在函数声明和指派返回值给一个变量时都使用引用运算符&: 可变函数 PHP支持可变函数的概念。意味…...
【react18】在styled-components中引入图片报错
在styled-components项目中,遇到背景图片显示不出来的问题。图片的确是引入正确,但是webpack解析路径是有问题的 效果展示 以下这两种写法都不行,无法生效 export const HeaderNavLeft styled.h1width: 176px;height: 69px;background: ur…...
693SJBH基于.NET的题库管理系统
计算机与信息学院 本科毕业论文(设计)开题报告 论文中文题目 基于asp.net的题库管理系统设计与实现 论文英文题目 Asp.net based database management system design and Implementation 学生姓名 专业班级 XXXXXX专业08 班 ⒈选题的背景和意…...
centos系统redis-dump安装
1. Ruby 环境 Redis-dump 是一个 Ruby 工具,需先安装 Ruby 和 RubyGems。 安装依赖: sudo yum install -y curl gpg2 gcc-c patch readline readline-devel zlib zlib-devel libyaml-devel libffi-devel openssl-devel make bzip2 autoconf aut…...
如何利用 Conda 安装 Pytorch 教程 ?
如何利用 Conda 安装 Pytorch 教程 ? 总共分为六步走: (1)第一步:验证conda 环境是否安装好? 1) conda -V2) conda --version(2)第二步:查看现有环境 conda env list…...
FPGA降低功耗研究
FPGA降低功耗研究 首先要明白一点:我们的核心目标是在维持性能的前提下,通过工艺、架构、设计方法学和系统级策略的协同优化,降低动态功耗、静态功耗和短路功耗。 本篇文章则是聚焦于 FPGA 设计阶段 的功耗优化,主要从 RTL 代码设…...
软考 系统架构设计师系列知识点之杂项集萃(67)
接前一篇文章:软考 系统架构设计师系列知识点之杂项集萃(66) 第108题 RISC(精简指令系统计算机)的特点不包括()。 A. 指令长度固定,指令种类尽量少 B. 寻址方式尽量丰富ÿ…...
第十节第三部分:常见API:传统时间:Date日期类、SimpleDateFormat
Date日期类常用方法 时间格式常用符号 Date日期类总结 为什么用SimpleDateFormat SimpleDateFormat常见方法 SimpleDateFormat解析字符串时间成为日期对象 SimpleDateFormat总结 代码: 代码一:Date日期类 package com.itheima.Time;import java.util.D…...
Python学习Day1:安装
Python的安装 1.安装python 打开python的官网,我们下载3.6.8版本Python Release Python 3.6.8 | Python.org 根据自己的电脑来下载对应的python版本 如图所示: 我已经下载完成,就是这样, 安装页面的第一排是自动安装…...
Java安全-Servlet内存马
内存马简介 内存马是指将恶意代码注入到内存中,达到无文件落地的效果,使得被攻击方难以察觉。由于是无文件的形式,可以绕过部分基于文件检测的杀软。而 Servlet 内存马是基于 Java Servlet 技术,动态将恶意代码注入到 Tomcat 内存…...
Mariadb cpu 93% 问题
最近项目遇到cpu平均使用率93% 一、登录数据库服务查看具体情况 可以看到服务器负载很高,,mariadb CPU使用已达到接近380.4% (因为是8核,所以会有超过100%的情况)。如下图: 二、排查是否有耗时较长sql 在…...
LeetCode222_完全二叉树的结点个数
LeetCode222_完全二叉树的结点个数 标签:#位运算 #树 #二分查找 #二叉树Ⅰ. 题目Ⅱ. 示例 0. 个人方法 标签:#位运算 #树 #二分查找 #二叉树 Ⅰ. 题目 给你一棵 完全二叉树 的根节点 root ,求出该树的节点个数。 完全二叉树 的定义如下&…...
linux 查看java的安装路径
一、验证Java安装状态 java -version正常安装会显示版本信息: openjdk version "1.8.0_65" OpenJDK Runtime Environment (build 1.8.0_65-b17) OpenJDK 64-Bit Server VM (build 25.65-b01, mixed mode)二、检查环境变量配置 若已配置JAVA_HOME&#…...
day32 python解释性库PDPbox
目录 一、初识PDPbox官方文档 二、准备鸢尾花数据集和训练模型 三、使用PDPbox进行解释性分析 1. 导入类并实例化对象 2. 调用plot方法绘制图形 3. 探索返回值的意义 四、总结与感悟 作为一名正在学习机器学习的学生,今天要学习一个非常有趣的库——PDPbox&am…...
LVLM-AFAH论文精读
Basic Information 标题:Your Large Vision-Language Model Only Needs A Few Attention Heads For Visual Grounding作者:Seil Kang, Jinyeong Kim, Junhyeok Kim, Seong Jae Hwang机构:Yonsei Universit…...
蓝桥杯3503 更小的数
问题描述 小蓝有一个长度均为 n 且仅由数字字符 0∼9 组成的字符串,下标从 0 到 n−1,你可以将其视作是一个具有 n 位的十进制数字 num,小蓝可以从 num 中选出一段连续的子串并将子串进行反转,最多反转一次。 小蓝想要将选出的子…...
5.21本日总结
一、英语 复习list4list26 二、数学 学完14讲,1000题13讲写完 三、408 学习计网5.3剩余内容 四、总结 高数本月结束知识点学习,15讲知识点与14讲的题目同步进行。408剩余两本书要加快学习进度。 五、明日计划 英语:复习lsit5list25 …...
【25软考网工】第七章(3) UOS Linux防火墙配置和Web应用服务配置
博客主页:christine-rr-CSDN博客 专栏主页:软考中级网络工程师笔记 大家好,我是christine-rr !目前《软考中级网络工程师》专栏已经更新三十多篇文章了,每篇笔记都包含详细的知识点,希望能帮助到你&am…...
Python数据分析基础
Python数据分析入门 介绍 在这个教程中,我们将学习如何使用Python来进行基本的数据分析。 安装必要的库 为了开始,你需要安装以下Python库: NumPyPandasMatplotlib 示例代码 import numpy as np import pandas as pd import matplotli…...
spring cloud config更新配置
在开发微服务时,往往需要有开发环境、测试环境和生产环境,手动修改配置环境是一件很麻烦的事情,因此,这里使用spring cloud config管理配置环境。要使用spring cloud config,需要先在GitHub搭建一个仓库。 一、仓库搭…...
小米汽车二期工厂下月将竣工,产能提升助力市场拓展
在新能源汽车市场竞争日益激烈的当下,小米汽车传来重要进展消息。据多方信息显示,小米汽车二期工厂下月即将竣工,这一关键节点的到来,有望为小米汽车的产能提升与市场布局带来重大突破。 小米汽车二期工厂位于北京亦庄ÿ…...
【单片机】如何产生负电压?
以下是对知乎文章《单片机中常用的负电压是这样产生的!》的解析与总结,结合电路原理、应用场景及讨论要点展开: 一、负电压产生的核心原理 负电压本质是相对于参考地(GND)的电势差为负值,需通过电源或储能…...
Mcu_Bsdiff_Upgrade
系统架构 概述 MCU BSDiff 升级系统通过使用二进制差分技术,提供了一种在资源受限的微控制器上进行高效固件更新的机制。系统不传输和存储完整的固件映像,而是只处理固件版本之间的差异,从而显著缩小更新包并降低带宽要求。 该架构遵循一个…...
阿里云ecs 8核 16G 内存 装有redis6 分配了3G内存,和2个tomcat 每个tomcat 4G 服务器反应迟钝,如何确认不是redis的问题
我们经常用redis 但遇到tomcat timeout的时候,如何确认不是redis的问题。 不能因为这个再去搞个redis 压力测试。有没有更省劲的方法来确认不是redis的问题 以下是针对 阿里云 ECS(8核16G,Redis 6分配3G内存,2个Tomcat各分配4G&a…...
机器学习-KNN算法
1.机器学习概述(全部流程) 获取数据:from sklearn.datasets import load_wine 数据处理(数据集划分、标准化):from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler 特征工程:from s…...
【数据结构 · 初阶】- 快速排序
目录 一. Hoare 版本 1. 单趟 2. 整体 3. 时间复杂度 4. 优化(抢救一下) 4.1 随机选 key 4.2 三数取中 二. 挖坑法 格式优化 三. 前后指针(最好) 四. 小区间优化 五. 改非递归 快速排序是 Hoare 提出的一种基于二叉树…...
HTTPS和HTTP区别
客户端向服务器发送HTTPS请求。服务器响应并发送其数字证书。客户端通过证书的公钥验证数字签名的有效性,如果有效,客户端生成一个随机的对称密钥。客户端使用公钥对这个对称密钥进行加密,然后将加密后的密钥发送给服务器。服务器使用自己的私…...
自动驾驶中的预测控制算法:用 Python 让无人车更智能
自动驾驶中的预测控制算法:用 Python 让无人车更智能 自动驾驶技术近年来取得了令人惊叹的进步,AI 与边缘计算的结合让车辆能够实时感知环境、规划路径并执行驾驶决策。其中,预测控制(Model Predictive Control,MPC) 作为一种先进的控制算法,凭借其对未来驾驶行为的优化…...
分享一些实用的网址
第一个:视频转化为动图 网址:https://www.freeconvert.com/zh/convert/mp4-to-gif 当然还有很多其他的功能,可以按需选择使用。 第二个:ASCII字符串到16进制在线转换工具 网址 :https://coding.tools/cn/ascii-to-…...
打开小程序提示请求失败(小程序页面空白)
1、小程序代码是商城后台下载的还是自己编译的 (1)要是商城后台下载的,检查设置里面的域名是不是https的 (2)要是自己编译的,检查app.js里面的接口域名是不是https的,填了以后有没有保存 注&a…...
Taro 安全区域
目录 一、问题描述 二、问题解决 1、顶部刘海区 2、底部小黑条 一、问题描述 安全区域主要是为了避免刘海屏或底部栏遮挡,而造成的不良显示效果。 本次将针对以下两点进行考量: 1、顶部刘海屏区 2、苹果X底部小黑条 二、问题解决 通过Taro.getS…...
DL00988-稀疏增强数据transformer船舶AIS轨迹预测含完整数据集
提升科研效率,精准预测船舶AIS轨迹!文末有完整获取方式 作为研究生和科研人员,是否在进行船舶轨迹预测时遇到数据稀疏、轨迹复杂等问题?现在,我们为你提供一款基于稀疏增强数据Transformer的船舶AIS轨迹预测工具&#…...
重写B站(网页、后端、小程序)
1. 网页端 1.1 框架 Vue ElementUI axios 1.2 框架搭建步骤 搭建Vue 1.3 配置文件 main.js import {createApp} from vue import ElementUi from element-plus import element-plus/dist/index.css; import axios from "axios"; import router from…...
SOPHGO算能科技BM1688内存使用与编解码开发指南
1. BM1688内存分配接口详解 1.1 设备内存分配接口区别 BM1688提供了三个主要的设备内存分配接口,它们的主要区别如下: // 基本设备内存分配接口 void* bm_malloc_device_byte(bm_handle_t handle, unsigned int size);// 指定heap区域的设备内存分配 void*</...
如何使用Antv X6使用拖拽布局?
拖拽效果图 拖拽后 布局预览 官方: X6 图编辑引擎 | AntV 安装依赖 # npm npm install antv/x6 --save npm install antv/x6-plugin-dnd --save npm install antv/x6-plugin-export --save需要引入的代码 import { Graph, Shape } from antv/x6; import { Dnd } …...
STM32项目分享:智能家居(机智云)升级版
目录 一、前言 二、项目简介 1.功能详解 2.主要器件 三、原理图设计 四、PCB硬件设计 PCB图 五、程序设计 六、实验效果 七、资料内容 项目分享 一、前言 项目成品图片: 哔哩哔哩视频链接: STM32智能家居(机智云)升级版 (资料…...
Flask-SQLAlchemy核心概念:模型类与数据库表、类属性与表字段、外键与关系映射
前置阅读,关于Flask-SQLAlchemy支持哪些数据库及基本配置,链接:Flask-SQLAlchemy_数据库配置 摘要 本文以一段典型的 SQLAlchemy 代码示例为引入,阐述以下核心概念: 模型类(Model Class) ↔ 数…...
算法优选系列(9.BFS 解决拓扑排序)
目录 拓扑排序简介: 编辑 课程表(medium): 课程表II(medium): 火星词典(hard): 拓扑排序简介: 有向无环图(DAG图) 如上图每条边…...
DevExpress Blazor中文教程 - 如何用AI聊天组件构建大型语言模型聊天APP?
DevExpress Blazor UI组件使用了C#为Blazor Server和Blazor WebAssembly创建高影响力的用户体验,这个UI自建库提供了一套全面的原生Blazor UI组件(包括Pivot Grid、调度程序、图表、数据编辑器和报表等)。 AI服务提供各种量身定制的模型来满…...
PHP:经典编程语言在当代Web开发中的新活力
在当今的Web开发领域,新技术和新语言层出不穷,但PHP(超文本预处理器)作为一门经典且功能强大的编程语言,依然在Web开发中占据着重要地位,并不断展现出新的活力。 PHP的历史与现状 PHP诞生于1994年&#x…...
ToDesk云电脑、并行智算云与顺网云AI支持能力深度实测报告
随着AI技术的迅猛发展,云计算平台已成为突破本地硬件限制、实现高效AI计算的关键基础设施。本报告对ToDesk云电脑、并行智算云和顺网云三大主流云平台进行了全面测评,从硬件配置、AI软件兼容性、性能表现、用户体验及成本效益等维度深入分析其AI支持能力…...
javaweb的拦截功能,自动跳转登录页面
我们开发系统时候,肯定希望用户登录后才能进入主页面去访问其他服务,但要是没有拦截功能的话,他就可以直接通过url访问或者post注入攻击了。 因此我们可以通过在后端添加拦截过滤功能把没登录的用户给拦截下来,让他去先登录&#…...
精益数据分析(75/126):用户反馈的科学解读与试验驱动迭代——Rally的双向验证方法论
精益数据分析(75/126):用户反馈的科学解读与试验驱动迭代——Rally的双向验证方法论 在创业的黏性阶段,用户反馈是优化产品的重要依据,但如何避免被表面反馈误导?如何将反馈转化为可落地的迭代策略&#x…...