首页 > 技术文章 > 西湖论剑2020中国杭州网络安全技能大赛部分题目WriteUp

pleiades 2020-10-12 14:57 原文

作为一个刚接触CTF不到3个月的小白,抱着来学习的想法,参加了西湖论剑·2020中国杭州网络安全技能大赛,只做出来2道题目,发一下WriteUp。

一、[CRYPTO]BrokenSystems

介绍大意是,有人黑进了加密系统并进行了篡改,剩下的就交给你了。

提供了3个文件

1.加密算法脚本

from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
from secret import flag
import os
rsa = RSA.generate(2048)
public_key = rsa.publickey().exportKey()
f=open("public.key","w")
f.write(public_key.decode())
f.close()

rsakey=RSA.importKey(open("public.key","r").read())
rsa = PKCS1_OAEP.new(rsakey)
msg=rsa.encrypt(flag.encode())
f=open("message","wb")
f.write(msg)
f.close()

2.公钥public.key

-----BEGIN PUBLIC KEY-----
MIICITANBgkqhkiG9w0BAQEFAAOCAg4AMIICCQKCAQEAwgdFIj/1uUss2EEhZvco
iiHyGH4aQhRTkYyrA8gCU0USM+sb3CNjdEIoqoaUqMLLyDP4Dd9AgxpokBsjC4Pz
8P7Uty0LlCteld7ayNzABHoq+5DIHtctOtSbcvyL0NMWfd2qarUWfAWN82rxkLIW
CFu9nWIfm9I6CT5OPZzDh7YnTywznIjhstkIrLM/TiDmR6vuBxSjzORkbolilLeB
A9zJpNt+1oEWTG5sx/0zR24XSmxwcDeyUEkTZfnw63auq6B9svZi2IBIr5jIjHbG
cQ25ZY1J/KDK8fXNmdwH8YhDK0j4VXEWitEPyCS3toK61sql0S/28EySeGtzirGb
twKCAQAdLS8fFz+BzzaP7AbUDUf9kvhhXBLuwUFo8ohCeVK4z1pTj3C6M0G2oXOu
gDdalrDThNlyKxkUn3iUc3Xgoz315pPtq9Xk1Ez/qeUl6gFPP6SZtfeymyGdkLiN
pVquOghjczjXvtBW467Fdb5Wu95TSzVaLndX23rsqW541n8hUwt8PsJKxh+bR0qy
gyIN2VRRNdBlpyTOL49E4y5GDu9fmVgAnFivWVGT135ywl8MsBUFuZPBNTKLEbUA
3KvJVckXf4Od0ENYbiWjEzXn1UN9yebNbU6+yyk34WAmwnkuF0X0Tu1UEb6qtV7Q
kF25GYy9QxERvodGL0Y2njHRpGE/
-----END PUBLIC KEY-----

3.密文message

由于是二进制,这里转为BASE64贴出来,需要的自己转回去即可。

J6a4HIY3ilKlD3lwR7GRG5WCPANVLC4NV9UKv/jHw23jj6I6LhgFhuY3P3PAtMtoEA7Wu1r7vNH/xSaQGidEryCJ8SXdqH29AqfMfmEszf2KtZib2sl2fatU1gHsLCNhZIE5pSck7N+cjOBnDDDfVTsOI8nJhF8yEPkO7pyK02srFdDooySSzkhEumWWkblciuwDwb4A6uD0K7X45rsJQ0TN3/cZFTOnIGgf9RNWoDlwrFp8C/xkVVWbask14UzaqrfWYp3fGrORgH3DLn5RHUq7O+exk7AJgqr3qIlTDcQeEEnm860nt9FKQl+TFE8wJgIP0AH3wg1TeE7hr3kY4g==

解题思路:

 首先从加密脚本确定是一个正常的RSA公钥加密,需要破解得到私钥才能解密。由于提供了公钥,我们能获得e和n的值,从而判断属于什么漏洞。

第一步,获取e和n:

from Crypto.PublicKey import RSA
from rsa import PublicKey,transform, core
from Crypto.Cipher import PKCS1_OAEP

pubkey=open("public.key","r").read()
rsakey = PublicKey.load_pkcs1_openssl_pem(pubkey)

n=rsakey.n
e=rsakey.e

print(n)
print(e)

观察打印出来的n和e:

24493816160588971749455534346389861269947121809901305744877671102517333076424951483888863597563544011725032585417200878377314372325231470164799594965293350352923195632229495874587039720317200655351788887974047948082357232348155828924230567816817425104960545706688263839042183224681231800805037117758927837949941052360649778743187012198508745207332696876463490071925421229447425456903529626946628855874075846839745388326224970202749994059533831664092151570836853681204646481502222112116971464211748086292930029540995987019610460396057955900244074999111267618452967579699626655472948383601391620012180211885979095636919
3683191938452247871641914583009119792552938079110383367782698429399084083048335018186915282465581498846777124014232879019914546010406868697694661244001972931366227108140590201194336470785929194895915077935083045957890179080332615291089360169761324533970721460473221959270664692795701362942487885620152952927112838769014944652059440137350285198702402612151501564899791870051001152984815689187374906618917967106000628810361686645504356294175173529719443860140795170776862320812544438211122891112138748710073230404456268507750721647637959502454394140328030018450883598342764577147457231373121223878829298942493059211583

发现e很大,我们知道一般e取值为65537,当加密指数e取得过大时,会导致解密指数d较小,存在低解密指数攻击的可能性。我们套用破解脚本:

def rational_to_contfrac (x, y):
    ''' 
    Converts a rational x/y fraction into
    a list of partial quotients [a0, ..., an] 
    '''
    a = x//y
    if a * y == x:
        return [a]
    else:
        pquotients = rational_to_contfrac(y, x - a * y)
        pquotients.insert(0, a)
        return pquotients
def convergents_from_contfrac(frac):    
    '''
    computes the list of convergents
    using the list of partial quotients 
    '''
    convs = [];
    for i in range(len(frac)):
        convs.append(contfrac_to_rational(frac[0:i]))
    return convs

def contfrac_to_rational (frac):
    '''Converts a finite continued fraction [a0, ..., an]
     to an x/y rational.
     '''
    if len(frac) == 0:
        return (0,1)
    elif len(frac) == 1:
        return (frac[0], 1)
    else:
        remainder = frac[1:len(frac)]
        (num, denom) = contfrac_to_rational(remainder)
        # fraction is now frac[0] + 1/(num/denom), which is 
        # frac[0] + denom/num.
        return (frac[0] * num + denom, num)

def egcd(a,b):
    '''
    Extended Euclidean Algorithm
    returns x, y, gcd(a,b) such that ax + by = gcd(a,b)
    '''
    u, u1 = 1, 0
    v, v1 = 0, 1
    while b:
        q = a // b
        u, u1 = u1, u - q * u1
        v, v1 = v1, v - q * v1
        a, b = b, a - q * b
    return u, v, a

def gcd(a,b):
    '''
    2.8 times faster than egcd(a,b)[2]
    '''
    a,b=(b,a) if a<b else (a,b)
    while b:
        a,b=b,a%b
    return a

def modInverse(e,n):
    '''
    d such that de = 1 (mod n)
    e must be coprime to n
    this is assumed to be true
    '''
    return egcd(e,n)[0]%n

def totient(p,q):
    '''
    Calculates the totient of pq
    '''
    return (p-1)*(q-1)

def bitlength(x):
    '''
    Calculates the bitlength of x
    '''
    assert x >= 0
    n = 0
    while x > 0:
        n = n+1
        x = x>>1
    return n


def isqrt(n):
    '''
    Calculates the integer square root
    for arbitrary large nonnegative integers
    '''
    if n < 0:
        raise ValueError('square root not defined for negative numbers')

    if n == 0:
        return 0
    a, b = divmod(bitlength(n), 2)
    x = 2**(a+b)
    while True:
        y = (x + n//x)//2
        if y >= x:
            return x
        x = y


def is_perfect_square(n):
    '''
    If n is a perfect square it returns sqrt(n),

    otherwise returns -1
    '''
    h = n & 0xF; #last hexadecimal "digit"

    if h > 9:
        return -1 # return immediately in 6 cases out of 16.

    # Take advantage of Boolean short-circuit evaluation
    if ( h != 2 and h != 3 and h != 5 and h != 6 and h != 7 and h != 8 ):
        # take square root if you must
        t = isqrt(n)
        if t*t == n:
            return t
        else:
            return -1

    return -1

def hack_RSA(e,n):
    frac = rational_to_contfrac(e, n)
    convergents = convergents_from_contfrac(frac)

    for (k,d) in convergents:
        #check if d is actually the key
        if k!=0 and (e*d-1)%k == 0:
            phi = (e*d-1)//k
            s = n - phi + 1
            # check if the equation x^2 - s*x + n = 0
            # has integer roots
            discr = s*s - 4*n
            if(discr>=0):
                t = is_perfect_square(discr)
                if t!=-1 and (s+t)%2==0:
                    print("\nHacked!")
                    return d

def main():
    n = 24493816160588971749455534346389861269947121809901305744877671102517333076424951483888863597563544011725032585417200878377314372325231470164799594965293350352923195632229495874587039720317200655351788887974047948082357232348155828924230567816817425104960545706688263839042183224681231800805037117758927837949941052360649778743187012198508745207332696876463490071925421229447425456903529626946628855874075846839745388326224970202749994059533831664092151570836853681204646481502222112116971464211748086292930029540995987019610460396057955900244074999111267618452967579699626655472948383601391620012180211885979095636919
e = 3683191938452247871641914583009119792552938079110383367782698429399084083048335018186915282465581498846777124014232879019914546010406868697694661244001972931366227108140590201194336470785929194895915077935083045957890179080332615291089360169761324533970721460473221959270664692795701362942487885620152952927112838769014944652059440137350285198702402612151501564899791870051001152984815689187374906618917967106000628810361686645504356294175173529719443860140795170776862320812544438211122891112138748710073230404456268507750721647637959502454394140328030018450883598342764577147457231373121223878829298942493059211583
    d=hack_RSA(e,n)
    print ("d=")
    print (d)

if __name__ == '__main__':
    main()
View Code

输出结果为:

Hacked!
d=
1779217788383673416690068487595062922771414230914791138743960472798057054853883175313487137767631446949382388070798609545617543049566741624609996040273727

这样就得到了d。如果直接用pow(c,d,n)尝试计算原文,会发现输出是乱码,琢磨了半天发现,我们应当构造RSA私钥进行解密:

from Crypto.PublicKey import RSA
from rsa import PublicKey,transform, core
from Crypto.Cipher import PKCS1_OAEP

private_key = RSA.construct((n, e, d))
rsa = PKCS1_OAEP.new(private_key)
m=rsa.decrypt(long_to_bytes(c))
print(m)

这样就解出了原文:

DASCTF{ce02347b86167f2d3519251b9a8a5ba8}

二、[Misc]指鹿为马

  题目给了一个地址和端口,没有附件。用nc连上去一看,长这样:

--------------------------------------------------------------------------------------------------------------------------------------
      ____       __            _          _   _                _                              _   _            _
     |  __ \     / _|          | |        | | | |              | |                            | | | |          | |
     | |__) |___| |_ ___ _ __  | |_ ___   | |_| |__   ___    __| | ___  ___ _ __    __ _ ___  | |_| |__   ___  | |__   ___  _ __ ___  ___
     |  _  // _ \  _/ _ \ '__| | __/ _ \  | __| '_ \ / _ \  / _` |/ _ \/ _ \ '__|  / _` / __| | __| '_ \ / _ \ | '_ \ / _ \| '__/ __|/ _ \
     | | \ \  __/ ||  __/ |    | || (_) | | |_| | | |  __/ | (_| |  __/  __/ |    | (_| \__ \ | |_| | | |  __/ | | | | (_) | |  \__ \  __/
     |_|  \_\___|_| \___|_|     \__\___/   \__|_| |_|\___|  \__,_|\___|\___|_|     \__,_|___/  \__|_| |_|\___| |_| |_|\___/|_|  |___/\___|

--------------------------------------------------------------------------------------------------------------------------------------
        1.show source code
        2.give me the source pictures
        3.upload picture
        4.exit

Refer to the deer as the horse,好一个指鹿为马。

输入1,获得源文件代码:

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):#k===1
    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))
    print('Eud: ', distances)
    neighbors = []
    for x in range(k):#(0,1)
        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)))
    print(diff_pixel)
    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('>')
        print(choose)
        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()
            #跟deer要相似
            if check(pic_name)>=100000:
                print('Don`t give me the horse source picture!!!')
                os.remove(pic_name)
                break
            ma = load_horse()
            print('ma:')
            print(ma)
            lu = load_deer()
            print('lu:')
            print(lu)
            k = 1
            trainingSet = np.append(ma, lu).reshape(2, 5185)
            print('train:', trainingSet)
            testSet = load_test(pic_name)
            print('test:', testSet, len(testSet[0]))
            neighbors = getNeighbors(trainingSet, testSet[0], k)
            print('neighbors:', neighbors)
            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()
View Code

输入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==
--------------------------------------------------------------------------------------------------------------------------------------
View Code

输入3,提示输入图片:Please input your deer picture`s base64(Preferably in png format)

解题思路:

阅读代码,发现需要用户上传的图片符合一定要求,才会给出flag。这个要求就是,看起来是鹿,却又更像马-_-||

具体来说,就是以check函数计算出的距离来看,与鹿的图片差距值小于一定阈值(100000),而用euclideanDistance(欧氏距离)作为依据又更接近马的图片。

乍一看,这有可能吗?我们来分析一下。

我们把问题简化,假如图片只有2个像素,IMG鹿=(x1, y1), IMG马=(x2, y2),用户上传图片IMG_USER=(xt, yt),则要求:

abs(x1-xt) + abs(y1-yt) < T

且:

d1 = sqrt((x1 - xt)^2 + (y1 - yt)^2)

d2 = sqrt((x2 - xt)^2 + (y2 - yt)^2)

min(d1, d2) == d2

在坐标系中表示出来,就是这样:

我们需要找到一个点User(xt, yt),落在图示中的位置,这就是答案。

而题目中的向量是图片的灰度矩阵拉平后的值,有5184维。我们无法求解出这个目标区域的解析解,只需要找到一个符合的向量就可以了。

我是通过在鹿的图片中替换一部分像素为马的像素来求解,脚本如下:

from PIL import Image
import numpy as np
from PIL import Image
import math
import os
import time
import base64
import random


def euclideanDistance(instance1, instance2, length):
    distance = 0
    for x in range(length):
        distance += pow((instance1[x] - instance2[x]), 2)
    return math.sqrt(distance)


def check(p1, p2):
    diff_pixel=0
    for x in range(len(p1)):
        diff_pixel += abs(p1[x] - p2[x])
    #print(diff_pixel)
    return diff_pixel
    


p1 = Image.open('./horse.png').convert('L')
p1 = np.array(p1).reshape(-1)
print('horse', p1)

p2 = Image.open('./deer.png').convert('L')
p2 = np.array(p2).reshape(-1)
print('deer', p2)

test = np.array(p2).reshape(-1)

for i in range(3000):
    test[i] = p1[i]

eud1 = euclideanDistance(p1, test, 5184)
print('Eu1: ', eud1)

eud2 = euclideanDistance(p2, test, 5184)
print('Eu2: ', eud2)

dif = check(test, p2)
print('Diff:', dif)

if(eud1 < eud2):
    print('Eud Good')
if dif < 100000:
    print('Diff Good')

img = test.reshape(72, 72)
#Image.save(img, 'test.png')
img = np.uint8(img)
im = Image.fromarray(img)
im.save("out.png", 'png')


 
with open("out.png", 'rb') as f:
    base64_data = base64.b64encode(f.read())
    s = base64_data.decode()
    print(s)
View Code

得到的图片BASE64值输入到服务器后,返回了另一段BASE64,转为PNG图片后,得到flag:

由于本人水平有限,只做出来这两道题目,与道友们共同学习多多交流,如有纰漏欢迎指正。

 

推荐阅读