[toc]
菜鸡第一次参加西湖论剑的比赛,只会一些misc,大多数都还是队友做出来的,卑微划水。又一次成为想学web但web0解的弟弟。
Yusapapa
Yusa,永远滴女神!西湖论剑不就是1分钟解签到,然后看Yusa一整天的比赛嘛~

先查看网页源码,可以看到有一段比较特殊的注释:

google搜索找到一份Biometrict替换表用以解码
https://en.wikipedia.org/wiki/PGP_word_list
将表格数据复制下来用以解码,再将注释里的文件也复制下来。
s = ''
with open(r'C:\Users\34603\Desktop\垃圾场\VScode\测试\table') as f://表格数据
with open(r'C:\Users\34603\Desktop\垃圾场\VScode\测试\word') as ff://注释数据
table = f.readlines()
word = ff.readlines()
#print(word)
for wo in word:
for tab in table:
if wo[:-1] in tab:
s += chr(int(tab[:2],16))
print(s)
结果如下:
You can see my collection puzzles in /hint.rar and another /encode.png.
By the way,the picture shoud be used
"Yusa" is very important in this challenge!!
解码后可以看到有一个hint.rar和encode.png,在题目网址上将这两题下载下来。
encode.png是一个跟网站上Yusa姐姐看起来一样的webp图片。
webp图片用stegpy解码可得:
the_password_is:Yus@_1s_YYddddsstegpy encode.webp the_key_is:Yus@_yydsstegpy!!
解开压缩包后得到hint.jpg。给了hint是一种较为古老的工具,根据hint:Invisible找到了这个工具:InvisibleSecret
选择unhide files,选择hint.jpg,密码填Yusa,选择Blowfish解密算法

可以得到一个encode.py脚本:
import os,random
from PIL import Image,ImageDraw
p=Image.open('flag.png').convert('L')
flag = []
a,b = p.size
for x in range(a):
for y in range(b):
if p.getpixel((x,y)) == 255:
flag.append(0)
else:
flag.append(1)
key1stream = []
for _ in range(len(flag)):
key1stream.append(random.randint(0,1))
random.seed(os.urandom(8))
key2stream = []
for _ in range(len(flag)):
key2stream.append(random.randint(0,1))
enc = []
for i in range(len(flag)):
enc.append(flag[i]^key1stream[i]^key2stream[i])
hide=Image.open('source.png').convert('RGB')
R=[]
G=[]
B=[]
a,b = hide.size
for x in range(a):
for y in range(b):
R.append(bin(hide.getpixel((x,y))[0]).replace('0b','').zfill(8))
G.append(bin(hide.getpixel((x, y))[1]).replace('0b','').zfill(8))
B.append(bin(hide.getpixel((x, y))[2]).replace('0b','').zfill(8))
R1=[]
G1=[]
B1=[]
for i in range(len(key1stream)):
if key1stream[i] == 1:
R1.append(R[i][:7]+'1')
else:
R1.append(R[i][:7]+'0')
for i in range(len(key2stream)):
if key2stream[i] == 1:
G1.append(G[i][:7]+'1')
else:
G1.append(G[i][:7]+'0')
for i in range(len(enc)):
if enc[i] == 1:
B1.append(B[i][:7]+'1')
else:
B1.append(B[i][:7]+'0')
for r in range(len(R)):
R[r] = int(R1[r],2)
for g in range(len(G)):
G[g] = int(G1[g],2)
for b in range(len(B)):
B[b] = int(B1[b],2)
a,b = hide.size
en_p = Image.new('RGB',(a,b),(255,255,255))
for x in range(a):
for y in range(b):
en_p.putpixel((x,y),(R[y+x*b],G[y+x*b],B[y+x*b]))
en_p.save('encode.png')
写一个解密脚本解密即可得到flag:
from PIL import Image,ImageDraw
p = Image.open('encode1.png').convert('RGB')
a,b = p.size
R=[]
G=[]
B=[]
for x in range(a):
for y in range(b):
R.append(bin(p.getpixel((x,y))[0]).replace('0b','').zfill(8))
G.append(bin(p.getpixel((x, y))[1]).replace('0b', '').zfill(8))
B.append(bin(p.getpixel((x, y))[2]).replace('0b', '').zfill(8))
R1=[]
G1=[]
B1=[]
flag=[]
for i in range(len(R)):
R1.append(int(R[i][-1:]))
G1.append(int(G[i][-1:]))
B1.append(int(B[i][-1:]))
print(R1[100:150])
print(G1[100:150])
print(B1[100:150])
for i in range(len(R1)):
flag.append(B1[i]^G1[i]^R1[i])
print(flag[100:150])
de_p = Image.new('L',(a,b),255)
c=0
for x in range(a):
for y in range(b):
if flag[c] == 1:
de_p.putpixel((x,y),0)
else:
de_p.putpixel((x, y), 255)
c=c+1
de_p.save('flag.png')
最后就可以提取出flag图片

这题前面的biometric word list刚开始不知道是什么,以为是snow隐写,一直在写各种词频统计脚本想找key。但是一直有问题(毕竟错误解法,当然有问题)。之后经过重重试错,队友找到了biometric word list,然后解出了网页注释的意思。最终我解了一个LSB隐写得到了flag。
指鹿为马
JC师傅出的题,但是比赛结束后经JC师傅口述的解题过程,好像没有人是用JC师傅希望的解法解的。
这里就写写我的解法。
nc连接后,

有四个选项,进入第一个选项,得到源代码:
import numpy as np
from PIL import Image
import math
import operator
import os
import time
import base64
import random
def load_horse():
data = []
p = Image.open('./horse.png').convert('L')
p = np.array(p).reshape(-1)
p = np.append(p,0)
data.append(p)
return np.array(data)
def load_deer():
data = []
p = Image.open('./deer.png').convert('L')
p = np.array(p).reshape(-1)
p = np.append(p,1)
data.append(p)
return np.array(data)
def load_test(pic):
data = []
p = Image.open(pic).convert('L')
p = np.array(p).reshape(-1)
p = np.append(p,1)
data.append(p)
return np.array(data)
def euclideanDistance(instance1, instance2, length):
distance = 0
for x in range(length):
distance += pow((instance1[x] - instance2[x]), 2)
return math.sqrt(distance)
def getNeighbors(trainingSet, testInstance, k):
distances = []
length = len(testInstance) - 1
for x in range(len(trainingSet)):
dist = euclideanDistance(testInstance, trainingSet[x], length)
distances.append((trainingSet[x], dist))
distances.sort(key=operator.itemgetter(1))
neighbors = []
for x in range(k):
neighbors.append(distances[x][0])
return neighbors
def getResponse(neighbors):
classVotes = {}
for x in range(len(neighbors)):
response = neighbors[x][-1]
if response in classVotes:
classVotes[response] += 1
else:
classVotes[response] = 1
sortedVotes = sorted(classVotes.items(), key=operator.itemgetter(1), reverse=True)
return sortedVotes[0][0]
def getAccuracy(testSet, predictions):
correct = 0
for x in range(len(testSet)):
if testSet[x][-1] == predictions[x]:
correct += 1
return (correct / float(len(testSet))) * 100.0
def check(pic):
source_p = Image.open('deer.png')
try:
c_p = Image.open(pic)
except:
print("Please upload right picture.")
exit()
diff_pixel = 0
a, b = source_p.size
if c_p.size[0] != a and c_p.size[1] != b:
print("Please upload right picture size("+str(a)+','+str(b)+')')
exit()
for y in range(b):
for x in range(a):
diff_pixel += abs(source_p.getpixel((x, y)) - c_p.getpixel((x, y)))
return diff_pixel
def main():
while 1:
print('-' * 134)
print(''' ____ __ _ _ _ _ _ _ _
| __ \ / _| | | | | | | | | | | | | | |
| |__) |___| |_ ___ _ __ | |_ ___ | |_| |__ ___ __| | ___ ___ _ __ __ _ ___ | |_| |__ ___ | |__ ___ _ __ ___ ___
| _ // _ \ _/ _ \ '__| | __/ _ \ | __| '_ \ / _ \ / _` |/ _ \/ _ \ '__| / _` / __| | __| '_ \ / _ \ | '_ \ / _ \| '__/ __|/ _ \\
| | \ \ __/ || __/ | | || (_) | | |_| | | | __/ | (_| | __/ __/ | | (_| \__ \ | |_| | | | __/ | | | | (_) | | \__ \ __/
|_| \_\___|_| \___|_| \__\___/ \__|_| |_|\___| \__,_|\___|\___|_| \__,_|___/ \__|_| |_|\___| |_| |_|\___/|_| |___/\___|
''')
print('-'*134)
print('\t1.show source code')
print('\t2.give me the source pictures')
print('\t3.upload picture')
print('\t4.exit')
choose = input('>')
if choose == '1':
w = open('run.py','r')
print(w.read())
continue
elif choose == '2':
print('this is horse`s picture:')
h = base64.b64encode(open('horse.png','rb').read())
print(h.decode())
print('-'*134)
print('this is deer`s picture:')
d = base64.b64encode(open('deer.png', 'rb').read())
print(d.decode())
continue
elif choose == '4':
break
elif choose == '3':
print('Please input your deer picture`s base64(Preferably in png format)')
pic = input('>')
try:
pic = base64.b64decode(pic)
except:
exit()
if b"<?php" in pic or b'eval' in pic:
print("Hacker!!This is not WEB,It`s Just a misc!!!")
exit()
salt = str(random.getrandbits(15))
pic_name = 'tmp_'+salt+'.png'
tmp_pic = open(pic_name,'wb')
tmp_pic.write(pic)
tmp_pic.close()
if check(pic_name)>=100000:
print('Don`t give me the horse source picture!!!')
os.remove(pic_name)
break
ma = load_horse()
lu = load_deer()
k = 1
trainingSet = np.append(ma, lu).reshape(2, 5185)
testSet = load_test(pic_name)
neighbors = getNeighbors(trainingSet, testSet[0], k)
result = getResponse(neighbors)
if repr(result) == '0':
os.system('clear')
print('Yes,I want this horse like deer,here is your flag encoded by base64')
flag = base64.b64encode(open('flag','rb').read())
print(flag.decode())
os.remove(pic_name)
break
else:
print('I want horse but not deer!!!')
os.remove(pic_name)
break
else:
print('wrong choose!!!')
break
exit()
if __name__=='__main__':
main()
进入2选项,得到一个马图和一个鹿图的base64编码:
this is horse`s picture:
iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAAAAABwhuybAAAFeElEQVR4nO2Xy28TVxTGvzN2ZpLYIY5dkgImIU4I2DFqQqiaqGpL2gXqv8AC2lUX/BFl032lSpWqblqpGyQ2LQhERQhUDSqk4ZnEtHnxCATn4TjP8fOeLmZszzPEUpeclefeM7/z3e/OfRh4G/9n+Dtk907v7jnKh42vc6690u5BTe/u1FsFyEPVgGoaqkAbw+pR5+m5689dq+5QxApSWjuO/DhbGoRgU6dcv7E7UH1472EUw2fuFDVS8e+UKblmX3JXoHBfiEKAOJjSClPOOpTOp2k3kCG19aN3IADA08habGyZk7nxRN2bFTWc8AuEWiWADtG8AIAF1ZLNrZ77SwynqIAa19cpHpMZgLddmcsTCpmwBACFZLGcFQ69TKoaivPragVKxl/9Z/YI/WlxRiVtoKC1qyoAtJzyAgAx66+LrWdPNu2KGL2n/el6vaFZntogzUBS8wCALXUPA2CQXl0KBPbdXtFfN5gdPxt68M210lNTNFj6mSxooJdWX5ibB3w20JEv9vLMqyeFUlJDtEX7pb4kSZIInFi3rTZuOWIdWuTLfUJiVNxjpUuZFwA8xwVAUzNYvTvgs03ZoYRqAsW+CgnAh3rDWL3t8tMCULMfAD0H8CxzbH+NQQ4A9jeYQF3fHR1LE/fXdHkMNaWD8kyGwAAKaQBIpkJBWRsf1YeCEgOSvmtqrW3fnxTp+2kiYov21alNAkDL1zLWQdW2xQMMcX1eKwoA9edPFjnQG2AWVg+CsSAA8LSNg8w/N1cq7ksA8P7nAuBAT4CJyDwz3HC0BcCLaRsHwModtZztBYC4XwDgpt6RJY/gBo+JVNslL6/N+f12Tl59PRczgXQsN937SRY43StM+d5ION9nx5DILSeWyqvQCwDjmz4GQBt/LQIYOqqYX5FqXTaPgz3p5flSEgDcveohgDyXRwFgctK2NbNzCA50HJYNoO2vL2xJ0uYv51UAyA5btyH3YBz4WPtEdXt8J7p5fGxbe1DO9QmXFx0i98NdA8gcvecUp2bnkB59m4XLSTsxUcUpye0H4AbKVeES2Bd2BWGyGklSozuoKknat+hSuSpJtAMod8NdkvPtxq1w4pFbD684XtuMZ78n3Fx+u7hWdKxMuZnNQFuNvcMI6vqk8h1yIzuBqHB58XgKDiTDAChSKyrheMJT4cpvOULqWX4nRZKX4Walzslf/bVAABw0WW9sBX222OFKTdlLV3Sj7SQLSEoOaQtfdMet0yalLo6UzmE7yaZoQ99BVItJxE8uJgCQv0ySjSm7u/kTide3bq0DQEenftmZHTvVZCC5ghjQ55S5sD1//552UwsNKAwAtPBHMnk2UCG5gho8QPbWGoGLanpxKau1Kv26iumRNYzCQLKCvB5NudzvFdL4BctqoN4IA6Dio9EMYCJZpka0HNNaYt0C6rB1VUXeAwBSR25rJ/joz6ul784AKqYBeD/o8QCQB+sgjScsnOCAAkBKDz0snYujQ6U+o6KHrwio6WsuCbppEaQMBBmgV9dmy84o7WScFz3SQwuE4sMlQD5ZB2li0syhnggDmP7d8D8i1i2gzllBSA0tiNGxAhCNOzjU3gOQeHBjzaBxsA7SZAKwzlrqRsu/RUAerBM2QcEBhUkdfVw0tEVjAupw1g7CygrgLEjpDzHSf84Zl4U8WCekSa2g0wfpKKgzwsgMvzC1RbvLghz3bEeH/B5QbtVaECVBTiD507KDO0c0LpC5mXUFxQyCDWG7XmqCJvQnu0dGBw2RXWPaNO1R0bhAplzQDup2FpSYZYhtW8GSIDtIcRaErBUdMwmyexSLOQqyhdkhh//9n/kEOQiyRfwYpKyhoA00PgU8frMg5C8JWp94c97bqMR/qKdMxhtIpukAAAAASUVORK5CYII=
--------------------------------------------------------------------------------------------------------------------------------------
this is deer`s picture:
iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAAAAABwhuybAAAEXUlEQVR4nO2XS2xbRRSG/5l7b/xSb2wnDqbBSdU0FcQUmsRZVCAkorZJumkrFWEkWLJgyZINC5bsQQKEVAkhNRKJRKWqjUiFEAgUkYRQtYQ+ZCc4jzbk4dgkaWzfOSwSP+7D9r3Jtmc198ycz/+ZmTMzBp6Zc2N959wAXGdjDEDTxY6DggJfXT8FoOv7L/wArox/xG2FmUcpHsUNwKO4FYB3QNABQaCKyGi39pc9kKzT8p/Yb3pdfkhNSvTtpvu/2eLoQBfPfjkNAGh7+bTqlYIfs8aGha9XHYOkUyd7pgFAes/L8iIHldIzo4/scSpB2mPRpWYAwLtwK1EAINZW8jY5utQmzr8Uv5YB+MKnf9uNtwTdGbt8uWtW8tG4c44OVPiWBqJRiELSOUcHQvbqw4FjDR4qHBbU/mane0MJy+e2Z7VDgFj3W01ZhZFGL4bGx2wvVzG63IzF/TMjZwYYh0Dh1k2HpHKtxeIqJZcfaqRpRPLgkGI3cM9KqcXiKjHCfoWSPIiamviQd3KuspyL4FhcJcALLyuRampiJ4Y+OFMxL0VFvXGVAOpTTkg2NQktcGm5YsNxAGCxd/wAQIH+SElu3XkSgV6jotbXsptySNKfafXnCWGpvN1kANB+JzraLxnH1SVJFZMkA0BWcvlebTAfqSQPSjef1tJkAG0pA17F6mgm+XxoLCUseqxB9AveCO5lxhViABVjifd13vlzYctUepVZASiVCGsMKQAg2j8JCrDtmaelgYzl0umcEcQiR4jf/ax8TuzvI0qn9xrJxCsaoC3Pl9MjOdRiToUI5A8vlL6NJbP73bYQgrfplpCE2Qig1vejVUH46Z4EUIvfzrVIR989WRW0el0AcEdscADRfEmtBsKNJQ6g1WvrphYdxTIxgx7c5gCpYVuSwHtc1UDaaIYBUti4UayNwoFqIExMcQCyTZBbrQrKjhYA8cRWYQCSUhWEHxKySM7ZU1SqDdmiK/XNlfUnebsgVAfRyPNHWJljyJFZ/4IVCKnp/nK0K6iP3NmwDxK3OyNFkgi+rp/H3amUVYz123dlZL3UwbjevLE2+yDcu5ood+lTI48lyTI1ALPLvd3HJQBM7EqGsuOntUVTQI1FDn8YIIAt/2xYNmrpyUvB4pf2+d2aigAUCADY6o/GA7vVxRtEs8Fp74+G3hjn+X/WioDQwUEAWG5+bW9W+IXYYUBguflFAgBqjEftgaosB9u6NskAgPzHgZqTvR9Q5VnKVh4tsT4C9t9m9RSxjV+tSSKhZYcny3LrKGLp4T+sO9YXgcwwSk+kOorYxJTZ6eMAJXcAbN7IFDXVS23H7JLaGNjW3p2+W8r7AMvf/BwBqU2D9wCgdheQSxrvT+cgXwRgj1eMbuegVpUgEqY94RgkHeNg60smv2NQcwuB5syL6RjU7gLbmjP7nYJ8EQCpjLmjVokwTtxY+y80guUTpBtTD7Q95iF23+icBnb+1Y95UEPMM6uw/wEb7IPCRqC/gwAAAABJRU5ErkJggg==
将两个base64解码后就能得到一张马图和一张鹿图。

通过源代码可知,需要在第三选项中传输一张马图,但是直接将2选项的马图传入后会发生以下问题:

根据图片中的显示,和题目名字,可知需要传入一个马图,但要让程序以为是鹿的图,这里我现在还是有点迷,附上host大佬的解题脚本:
# -*- coding: utf-8 -*-
import numpy as np
from PIL import Image
import math
import operator
import random
import os
import time
import base64
import random
def load_horse():
data = []
p = Image.open('./horse.png').convert('L')
p = np.array(p).reshape(-1)
p = np.append(p, 0)
data.append(p)
return np.array(data)
def load_deer():
data = []
p = Image.open('./deer.png').convert('L')
p = np.array(p).reshape(-1)
p = np.append(p, 1)
data.append(p)
return np.array(data)
def load_test(pic):
data = []
p = Image.open(pic).convert('L')
p = np.array(p).reshape(-1)
p = np.append(p, 1)
data.append(p)
return np.array(data)
def euclideanDistance(instance1, instance2, length):
distance = 0
for x in range(5184):
distance += pow((instance1[x] - instance2[x]), 2)
return math.sqrt(distance)
def getNeighbors(trainingSet, testInstance, k):
distances = []
length = 5184
for x in range(2):
dist = euclideanDistance(testInstance, trainingSet[x], 5184)
# print(trainingSet[x], type(trainingSet[x]))
distances.append((trainingSet[x], dist))
# print(len(distances), len(distances[x][0]))
print(distances)
distances.sort(key=operator.itemgetter(1))
print(distances[0])
neighbors = []
neighbors.append(distances[0][0])
return neighbors
def getResponse(neighbors):
classVotes = {}
response = neighbors[0][-1]
# print(response, 'response')
if response in classVotes:
classVotes[response] += 1
else:
classVotes[response] = 1
sortedVotes = sorted(classVotes.items(), key=operator.itemgetter(1), reverse=True)
return sortedVotes[0][0]
def check(pic):
source_p = Image.open('deer.png')
try:
c_p = Image.open(pic)
except:
print("Please upload right picture.")
exit()
diff_pixel = 0
a, b = source_p.size
if c_p.size[0] != a and c_p.size[1] != b:
print("Please upload right picture size(" + str(a) + ',' + str(b) + ')')
exit()
for y in range(b):
for x in range(a):
diff_pixel += abs(source_p.getpixel((x, y)) - c_p.getpixel((x, y)))
return diff_pixel
pic_name = 'tmp_deer.png'
ma = load_horse()
lu = load_deer()
deer = Image.open('deer.png').convert('L')
horse = Image.open('horse.png').convert('L')
deer2 = deer.copy()
for i in range(72):
for j in range(72):
deer2.putpixel((i, j), int(horse.getpixel((i, j))*random.uniform(0.45,0.54)+random.uniform(0.4,0.5)*deer.getpixel((i, j))))
deer2.save('tmp_deer.png')
print(check(pic_name))
trainingSet = np.append(ma, lu).reshape(2, 5185)
testSet = load_test(pic_name)
neighbors = getNeighbors(trainingSet, testSet[0], 1)
result = getResponse(neighbors)
print(result)
解完后得到一个既不像鹿又不像马的图片:

将这个图片base64编码后 即可获得一大串http传输的数据,其中有一段很长的base64编码,这里就不写了,将base64解码后即可获得flag图片。
Barbar
一道小套娃的题目,下载附件得到一个二维码,将图片用winhex打开,可以在最后看到有隐藏。先将隐藏分离出来。

用QR Research扫码后有一长串奇怪的问号,然后尝试用手机扫码,发现没有问号,只有“密码在哪啊”五个字。当时就感觉可能会有零宽字节隐写的问题。

然后利用网页扫码网站扫码,可以在源代码中看到零宽字节:

将这有着127个空格的句子用零宽字节解码后获得key。
https://yuanfux.github.io/zero-width-web/

将压缩包解开后获得一个Aztec码和一个被处理过的二维码。

Aztec码扫码后获得一段文本
di`f{e1c64e14db14c6bb8faabab5bd7be1dc}
将被处理过的二维码,定位标志修复:

这里我用PS手动修复了好久,可恶的是扫码后发现只是一段气人的话,当时直接心态爆炸:

这里我之后我就解不出来了,卡在这直到比赛结束。比赛结束后群里有大佬说了最后一步是npiet,尝试了好久的在本地搭建npiet环境,但是一直有问题。最后就先尝试利用网页在线工具解码,以后再搭本地环境:
https://www.bertnase.de/npiet/npiet-execute.php

选择Upload and ask about input,输入Aztec解码后得到的文本“di`f{e1c64e14db14c6bb8faabab5bd7be1dc}”
解码后得到flag:

由于比赛已经结束,没法提交flag了,所以也不确定是否是正确的。
经过这次比赛,我又明白了自己其实还有特别特别多不懂的地方,尤其是web题,一道题都看不懂。但是这些misc题的复现让我收获颇多(尤其是JC师傅的马题),希望明年西湖论剑的时候能进入线下赛吧,还需努力。