西湖论剑misc部分wp

[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师傅的马题),希望明年西湖论剑的时候能进入线下赛吧,还需努力。

发表评论