2019-De1CTF-xorz

題目給了一個異或腳本:

from itertools import *
from data import flag,plain

key=flag.strip("de1ctf{").strip("}")
assert(len(key<38))
salt="WeAreDe1taTeam"
ki=cycle(key)
si=cycle(salt)
cipher = ''.join([hex(ord(p) ^ ord(next(ki)) ^ ord(next(si)))[2:].zfill(2) for p in plain])
print cipher
cipher = ''.join([hex(ord(p) ^ ord(next(ki)) ^ ord(next(si)))[2:].zfill(2) for p in plain])

三者進行異或,未知部分有兩個,flag和plain,最後輸出的結果是16進制的密文
salt和key都是循環使用
salt是已知的因此先把salt層異或去掉

from itertools import *
salt="WeAreDe1taTeam"
si=cycle(salt)
c = '49380d773440222d1b421b3060380c3f403c3844791b202651306721135b6229294a3c3222357e766b2f15561b35305e3c3b670e49382c295c6c170553577d3a2b791470406318315d753f03637f2b614a4f2e1c4f21027e227a4122757b446037786a7b0e37635024246d60136f7802543e4d36265c3e035a725c6322700d626b345d1d6464283a016f35714d434124281b607d315f66212d671428026a4f4f79657e34153f3467097e4e135f187a21767f02125b375563517a3742597b6c394e78742c4a725069606576777c314429264f6e330d7530453f22537f5e3034560d22146831456b1b72725f30676d0d5c71617d48753e26667e2f7a334c731c22630a242c7140457a42324629064441036c7e646208630e745531436b7c51743a36674c4f352a5575407b767a5c747176016c0676386e403a2b42356a727a04662b4446375f36265f3f124b724c6e346544706277641025063420016629225b43432428036f29341a2338627c47650b264c477c653a67043e6766152a485c7f33617264780656537e5468143f305f4537722352303c3d4379043d69797e6f3922527b24536e310d653d4c33696c635474637d0326516f745e610d773340306621105a7361654e3e392970687c2e335f3015677d4b3a724a4659767c2f5b7c16055a126820306c14315d6b59224a27311f747f336f4d5974321a22507b22705a226c6d446a37375761423a2b5c29247163046d7e47032244377508300751727126326f117f7a38670c2b23203d4f27046a5c5e1532601126292f577776606f0c6d0126474b2a73737a41316362146e581d7c1228717664091c'.decode('hex')
no_salt = ''.join([chr(ord(p) ^ ord(next(si))) for p in c])
print no_salt.encode('hex')
no_salt =1e5d4c055104471c6f234f5501555b5a014e5d001c2a54470555064c443e235b4c0e590356542a130a4242335a47551a590a136f1d5d4d440b0956773613180b5f184015210e4f541c075a47064e5f001e2a4f711844430c473e2413011a100556153d1e4f45061441151901470a196f035b0c4443185b322e130806431d5a072a46385901555c5b550a541c1a2600564d5f054c453e32444c0a434d43182a0b1c540a55415a550a5e1b0f613a5c1f10021e56773a5a0206100852063c4a18581a1d15411d17111b052113460850104c472239564c0755015a13271e0a55553b5a47551a54010e2a06130b5506005a393013180c100f52072a4a1b5e1b165d50064e411d0521111f235f114c47362447094f10035c066f19025402191915110b4206182a544702100109133e394505175509671b6f0b01484e06505b061b50034a2911521e44431b5a233f13180b5508131523050154403740415503484f0c2602564d470a18407b775d031110004a54290319544e06505b060b424f092e1a770443101952333213030d554d551b2006064206555d50141c454f0c3d1b5e4d43061e453e39544c17580856581802001102105443101d111a043c03521455074c473f3213000a5b085d113c194f5e08555415180f5f433e270d131d420c1957773f560d11440d40543c060e470b55545b114e470e193c155f4d47110947343f13180c100f565a000403484e184c15050250081f2a54470545104c5536251325435302461a3b4a02484e12545c1b4265070b3b5440055543185b36231301025b084054220f4f42071b1554020f430b196f19564d4002055d79

去掉salt層,就剩下plain和key了,key就是我們要求的flag,這裏注意到key位數小於38,所以是用key來循環異或加密的,對於利用重複密鑰異或的情況有現成的腳本,原理爲漢明距離hamming_distance

hamming_distance:在信息論中表示兩個等長字符串在對應位置上不同字符的數目 以d(x, y)表示字符串x和y之間的漢明距離 簡單來說 漢明距離度量了通過替換字符的方式將字符串x變成y所需要的最小的替換次數

# coding:utf-8

# python3

no_salt =bytes.fromhex('1e5d4c055104471c6f234f5501555b5a014e5d001c2a54470555064c443e235b4c0e590356542a130a4242335a47551a590a136f1d5d4d440b0956773613180b5f184015210e4f541c075a47064e5f001e2a4f711844430c473e2413011a100556153d1e4f45061441151901470a196f035b0c4443185b322e130806431d5a072a46385901555c5b550a541c1a2600564d5f054c453e32444c0a434d43182a0b1c540a55415a550a5e1b0f613a5c1f10021e56773a5a0206100852063c4a18581a1d15411d17111b052113460850104c472239564c0755015a13271e0a55553b5a47551a54010e2a06130b5506005a393013180c100f52072a4a1b5e1b165d50064e411d0521111f235f114c47362447094f10035c066f19025402191915110b4206182a544702100109133e394505175509671b6f0b01484e06505b061b50034a2911521e44431b5a233f13180b5508131523050154403740415503484f0c2602564d470a18407b775d031110004a54290319544e06505b060b424f092e1a770443101952333213030d554d551b2006064206555d50141c454f0c3d1b5e4d43061e453e39544c17580856581802001102105443101d111a043c03521455074c473f3213000a5b085d113c194f5e08555415180f5f433e270d131d420c1957773f560d11440d40543c060e470b55545b114e470e193c155f4d47110947343f13180c100f565a000403484e184c15050250081f2a54470545104c5536251325435302461a3b4a02484e12545c1b4265070b3b5440055543185b36231301025b084054220f4f42071b1554020f430b196f19564d4002055d79')
import base64
import string

def bxor(a, b):# xor two byte strings of different lengths
   if len(a) > len(b):
        return bytes([x ^ y for x, y in zip(a[:len(b)], b)])
   else:
      return bytes([x ^ y for x, y in zip(a, b[:len(a)])])
def hamming_distance(b1, b2):
    differing_bits =0

    for byte in bxor(b1, b2):
        differing_bits += bin(byte).count("1")
    return differing_bits

def score(s):
   freq = {}
   freq[' '] = 700000000
   freq['e'] = 390395169
   freq['t'] = 282039486
   freq['a'] = 248362256
   freq['o'] = 235661502
   freq['i'] = 214822972
   freq['n'] = 214319386
   freq['s'] = 196844692
   freq['h'] = 193607737
   freq['r'] = 184990759
   freq['d'] = 134044565
   freq['l'] = 125951672
   freq['u'] = 88219598
   freq['c'] = 79962026
   freq['m'] = 79502870
   freq['f'] = 72967175
   freq['w'] = 69069021
   freq['g'] = 61549736
   freq['y'] = 59010696
   freq['p'] = 55746578
   freq['b'] = 47673928
   freq['v'] = 30476191
   freq['k'] = 22969448
   freq['x'] = 5574077
   freq['j'] = 4507165
   freq['q'] = 3649838
   freq['z'] = 2456495

   score = 0
   string=bytes.decode(s)
   for c in string.lower():
       if c in freq:
            score += freq[c]
   return score

def break_single_key_xor(b1):
    max_score = 0
    english_plaintext = 0
    key = 0

    for i in range(0,256):
        b2 = [i]* len(b1)
        try:
            plaintext = bxor(b1, b2)
            pscore = score(plaintext)
        except Exception:
            continue
        if pscore > max_score or not max_score:
           max_score = pscore
           english_plaintext = plaintext
           key = chr(i)
    return key

b = no_salt

normalized_distances = []

for KEYSIZE in range(2, 40):

   # 我們取其中前6段計算平局漢明距離

   b1 = b[: KEYSIZE]
   b2 = b[KEYSIZE: KEYSIZE * 2]
   b3 = b[KEYSIZE * 2: KEYSIZE * 3]
   b4 = b[KEYSIZE * 3: KEYSIZE * 4]
   b5 = b[KEYSIZE * 4: KEYSIZE * 5]
   b6 = b[KEYSIZE * 5: KEYSIZE * 6]
   b7 = b[KEYSIZE * 6: KEYSIZE * 7]

   normalized_distance = float(
      hamming_distance(b1, b2) +
      hamming_distance(b2, b3) +
      hamming_distance(b3, b4) +
      hamming_distance(b4, b5) +
      hamming_distance(b5, b6) 
   ) / (KEYSIZE * 5)
   normalized_distances.append(
      (KEYSIZE, normalized_distance)
   )

normalized_distances = sorted(normalized_distances, key=lambda x: x[1])


for KEYSIZE, _ in normalized_distances[:5]:
   block_bytes = [[] for _ in range(KEYSIZE)]
   for i, byte in enumerate(b):
        block_bytes[i % KEYSIZE].append(byte)
   keys = ''

   for bbytes in block_bytes:
        keys += break_single_key_xor(bbytes)
   key = bytearray(keys * len(b), "utf-8")
   plaintext = bxor(b, key)
   print("keysize:", KEYSIZE)
   print("key is:", keys, "n")
   s = bytes.decode(plaintext)
   print(s)

最後發現plain就是莎士比亞的十四行詩。。。


flag{W3lc0m3tOjo1nu55un1ojOt3m0cl3W}

參考:
小記一類ctf密碼題解題思路
https://xz.aliyun.com/t/3256#toc-22
https://cypher.codes/writing/cryptopals-challenge-set-1
https://cryptopals.com/sets/1/challenges/6
https://mp.weixin.qq.com/s?__biz=MzU3ODc2NTg1OA==&mid=2247484089&idx=1&sn=3f41f1d65595f47dc72918b22cffc128&chksm=fd7117f4ca069ee292d6bbabe37d5aa9445747d0c13dd8b2d2f1d274f7afa32f910fe28ff07e&mpshare=1&scene=23&srcid=&sharer_sharetime=1565101436270&sharer_shareid=0a12e40935fbeb4e04dfab7ff42aca93#rd

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章