N1CTF 2018 Writeup

この大会は2018/3/10 10:00(JST)~2018/3/12 10:00(JST)に開催されました。
今回もチームで参戦。結果は2054点で517チーム中27位でした。
自分で解けた問題をWriteupとして書いておきます。

baby_N1ES (Crypto)

カスタマイズしたDESのような処理。keyはわかっているので、encrypt関数を参考にdecrypt関数を作って、復号すればよい。
次のようにN1ES.pyにコードを追加する。

      :
class N1ES:
      :
    def encrypt(self, plaintext):
        if (len(plaintext) % 16 != 0 or isinstance(plaintext, bytes) == False):
            raise Exception("plaintext must be a multiple of 16 in length")
        res = ''
        for i in range(len(plaintext) / 16):
            block = plaintext[i * 16:(i + 1) * 16]
            L = block[:8]
            R = block[8:]
            for round_cnt in range(32):
                L, R = R, (round_add(L, self.Kn[round_cnt]))
            L, R = R, L
            res += L + R
        return res

## 以下、追加 ##
    def decrypt(self, ciphertext):
        if (len(ciphertext) % 16 != 0 or isinstance(ciphertext, bytes) == False):
            raise Exception("ciphertext must be a multiple of 16 in length")
        res = ''
        for i in range(len(ciphertext) / 16):
            block = ciphertext[i * 16:(i + 1) * 16]
            L = block[:8]
            R = block[8:]
            for round_cnt in range(32):
                L, R = (round_add(R, self.Kn[round_cnt])), L
            L, R = R, L
            res += L + R
        return res

あとは以下のコードで復号する。

from N1ES import N1ES
import base64

key = 'wxy191iss00000000000cute'
n1es = N1ES(key)
cipher = base64.b64decode('HRlgC2ReHW1/WRk2DikfNBo1dl1XZBJrRR9qECMNOjNHDktBJSxcI1hZIz07YjVx')
flag = n1es.decrypt(cipher)
print flag
N1CTF{F3istel_n3tw0rk_c4n_b3_ea5i1y_s0lv3d_/--/}

easy_fs (Crypto)

$ nc 47.52.195.203 2333
==============================
   Welcome to Nu1L Easy FS    
==============================
   1. File list               
   2. Read encrypted file     
   3. Custom encryption       
   4. Quit                    
==============================
   Input your choice:         
==============================
1
total 8
-rwxr-xr-x 1 root root  16 Mar 10 17:28 a_small_file
-rwxr-xr-x 1 root root 216 Mar 10 17:28 flag
==============================
   Welcome to Nu1L Easy FS    
==============================
   1. File list               
   2. Read encrypted file     
   3. Custom encryption       
   4. Quit                    
==============================
   Input your choice:         
==============================
2
Input filename:
flag
Now generating N......
N = 0xb93c631540d83109f523e342985fd282748b01b8c2a4d324f2cd7d507addd33b3844d2972a4258de132d277291cde1bb89862efec2ad040fb15fc68f92ca313a833f889851bca2eaed462813dc4cc9a02598872b7538e231a3f0598150a58e9106a3dba1efe87eff119c2185f0b0c20eed78aaab5c8c4e5fff5087e264391b45938d2a315212afa028b49b2a7c898da4d4cac558b20e8b43093b4ff0d1b531298ae4e346633d2751b703063de6b0af346023a01441055500c5d7d417e77bc9864fb20c6280e8d141752d8d37a2602fc0b275dd85bae7e403eb4631f60c8f3d8b574d52d41bb4080bc582e59fff701bd151d7f8777c8b0ce7d1df988f89d2769
Please input your E: (Hexadecimal, without '0x/0X', 1 - 16 digits)
3
e = 0x3
Success!
C = 0x9d0dccdfe8ff3778b578f4f872e4ad10c33fae97b292d19435b6f577426dc09187593e00a5d81d415a4e849e3721040a10381a7111ac36eadbbddb1983938f8f49d6055153badb5caa2587391510a1118591abd7ccd6b493edf6f413ed7411811e4a7fa48b161b9cda96f8b64b853912d509502203462b
You can just read once :)
$ nc 47.52.195.203 2333
==============================
   Welcome to Nu1L Easy FS    
==============================
   1. File list               
   2. Read encrypted file     
   3. Custom encryption       
   4. Quit                    
==============================
   Input your choice:         
==============================
2
Input filename:
flag
Now generating N......
N = 0x6bfd9bbd41dc934f85f0ae0df7ff303999fd13a4071010d3b6ad2677da0bea82906b9fe8c2966278c6f21c8531aa5ace28982ef0cfdaa2326c5a5a77e52e26798b2d9d143320dfafeb754de59c96e472e3583e49774c37b7d620d95f1f2a741097c17b17863432a49ae7c785d4b5514378cbc4298e1d79fbd0c8d8ce787df4442088b28ea2f875197b822b1573b97a31ec76eb2fbc8adc04c7417e9ba6504f2847d8eaf7d617d6b2db6fdf7efb32c3cc45100fb94d96629abaaf1fbf0bc234988d259d18e2a301d6572a52ed6bb1913594b89f5eb98bc447d9cb7f75e66d70838527b793af01bb19f46fffc9885486e06a7e9410aea6fb52caaa18a083ea142d
Please input your E: (Hexadecimal, without '0x/0X', 1 - 16 digits)
3
e = 0x3
Invalid E, use 0x10001
Success!
C = 0x850ae708ae14dba31e70876be5cdb33456c1235c9b5b019b98678a2b3e5fbc4149e9110606287e5b70fcfbbde13d70d767caf1a1504066e9da0e1b9cf459d561f1fbd9cd56e4407800735de9bd67b28d4b6a884a1de6b6e486715cad0828414bde3cf41010c604d9c0f81ceec98f6da1c61503eac8788e05e786d6f1f0fa776c53cad4b6221afb587366fa9e5ab0403545df67eb71fed057dd5e6e8382a0b15d431c9c29c3b5bed5dbce659f58d94983d73b35ade95f8858de98408c40f135cd688ff78e2e8fe20d14eb7d916d67405906f033438a2203c678fdf97cb51f15c36ce9b8a5969d98b2f979fbcec2579624a93042f425487eb0839dbd440200d49

flagファイルの内容をRSA暗号したデータを入手できる。nはランダムで、eは指定できるが同じ値でも受け付けてもらえないことがある。
Custom encryptionで有効なeを確認しながら、有効なeに対応する暗号化データをe種類の情報が得られるまで収集する。
eが3の場合は、3個収集できれば、Hastad's broadcast attackで復号できる。

[1回目]
==============================
   Welcome to Nu1L Easy FS    
==============================
   1. File list               
   2. Read encrypted file     
   3. Custom encryption       
   4. Quit                    
==============================
   Input your choice:         
==============================
2
Input filename:
flag
N = 0x6ec96a53b50bb87fb6ebccc68640d18ba0c5b7798319ba551971d2444867f85b819524cf686271d07c08b0c6daff51485fed221dada9abf50b76c1b21578d9114eb6e8b04439f08567d848d103657f817b3cc9f0f56d6aaa865fdec5f3364d4e973a615d3c678c46d24ef6a5092467af5282d759ac85ab6ddc86f7940538013a8ac2a75b8f55d218d676b22e0233e75626c794cd5df64b3ca8e4d761f596d717641060a070ec85451321f862d8b6e264798656159acebf68ef653455550ed9d73e8dd6e322610ff6fe554945adde3df0e934e5d59c365226238c54b96dea088cac8ef748634160ae6807a6672628926c81a8975450e27337306f88e4e9ff8c85
Please input your E: (Hexadecimal, without '0x/0X', 1 - 16 digits)
3
e = 0x3
Success!
C = 0x3a7bdc206e63785573ef49ca4e4bf812237f2cdc0e0204978ab2d6d4e6faa3bbbaea937608bbf000f3f4fbdd1a6a9d690015072c75e1daba85cc3bb8e1ff1428161e67213b104e7ac9435bfcb504a733fcb89df0af34c7c73c4df2bf1c97e3e56bee4f3f1dd52fcda4f40a5e33c5486135afaaa1f5032ea59bd71517b8b692e512322cdd2ba03df5b41f107939d5f361862b58fd2973a296b0c4c9bf095e181746b69d0da6ec625a00e740fe6e3dcdd83e76f0a9511a868832aa536aa0866debba92369a491d240939956266239a3e242f3d25a3415ddcccbd47c4ccc1eb376062b19e4018a29991f870786e7195eba1bd0d1cf8790832deff6f9fa5eaa49489

[2回目]
==============================
   Welcome to Nu1L Easy FS    
==============================
   1. File list               
   2. Read encrypted file     
   3. Custom encryption       
   4. Quit                    
==============================
   Input your choice:         
==============================
2
Input filename:
flag
N = 0x34becd5b321d34414e7b13d9499f29a00bfeda5dcaaeadaf27de31a8afb45c303ef0e6734575d106973189de4ffdd5646eed5a90814c5f5b40bbf504e4ee4dfa50e6c99a80cb166fe7612756951a3edefe5d61c461938001c65bbf89adae4f16d4ac7ec4890b7899cea9ed284add7500af5a41b436e7314a90c2ce763264f3ada70a747663bb5a7b0648672103b1bf1fb93cf9f66bc3d262f2cd53b5be6f7cb2b1d44f9768faf53c7a9cfedaad46958a71278ede34c7dfb4a0c4b51a2e2526e333d4a2da11d6f0bb97ec41982046684204ec8b4903bbc92f7d9736d82b4d388b8624d9b9ba1569ecdd818c0ccbaa868d892a012f65b332ab4ed7d002ec505fcd
Please input your E: (Hexadecimal, without '0x/0X', 1 - 16 digits)
3
e = 0x3
Success!
C = 0xe1cb3e348dcf16e66ed1ec2fc9c91289aaf532187807608983a53211cadf1080e3d7ed54fe5fa8e2dacdd6c101c0d02bcba1a69c0f14d1c80fd880e7480c2695087570e16e07bdf6216578354701a7b729c1cba93f06e0dfe3e4df60db9dcd688290ea90abca0864595fe69c7c3454781db4e150fe411374a9f9d57f1bca655148bc3fac531ffb49b5008f7465242ce8eaec4b0e9641f0455ba9e634f8ffb6a12be2099c926d8b7e47d496ddbd5c34ede8c0cf116575cc5e646bc68762da3d1b3e01db463fe1f5dbcf66e250b949b5e46aeb4d14214dfeefc0f47b9f8124fb7f600003f267e6ffd09d1c5c59f82540e4acbd25e756e6999c16c897675775076

[3回目]
==============================
   Welcome to Nu1L Easy FS    
==============================
   1. File list               
   2. Read encrypted file     
   3. Custom encryption       
   4. Quit                    
==============================
   Input your choice:         
==============================
2
Input filename:
flag
N = 0x1506b0cb9d5eed45f8a1ebd9721f43214d857fdb21980b21e6c031e48a1194d520c9fd6b48adc1a1d12a1708b81ff7d4ee77d06e21567cc3bf0bc52036ef65fa96ae3efb871d32db854d0db286b7fdd7013cb7d71873a0a8e379fa1d38a385179cc0eaa55b617236d993c39ea4b4995eb7b5fcfa06c8d50ed3c245d7a72d57b8817f2cef8ad57b5c71482c14c210b3ace47539f109d117015f6c8bb82bd8dcbfe4985d5fd0bc7e1cf58275dc628538b3d64ad5f30404072858fe6298380795814e95ec4abb83ba4ece47f2f3a35bbe66b5eb76208540cac03f7c71d17ab37fa466f0cfc5514c06bc98e5982a19d8a6d57ad5107a9e1dc24b119f9ca852744569
Please input your E: (Hexadecimal, without '0x/0X', 1 - 16 digits)
3
e = 0x3
Success!
C = 0x132e7f056cdd5e4f980ca06a29ba16697672a78ead81040ca452d071189e8917757cc33ff40738d4bddcbfea638c4e1ea61c6606ed5430085257a0aa2c17fa7d5ffc4987868374b5c427457ae235e7c692c27629a7039a750781619ac3534a3d4cfcf76b6cec65c9a05668382a8fa998db0796bd735bd3fc9bee82b1e31d23f4e415cbb75fcf3dc957684e619e38467b28b1d33881c17da9fe94577dccdf7aae4a7d05de3888adc4fefff07bb3fd06e317ecee61261919a46019a129c1b22dc21c600931e84efd59acdb45393cdcdb9649d2d5c64bd3878b9a0a3afabec1e4237fcf91f495b44effeeeb0453bae965943e5882863044581cf65576ff3150d850

以上の情報から復号する。

N1 = 0x6ec96a53b50bb87fb6ebccc68640d18ba0c5b7798319ba551971d2444867f85b819524cf686271d07c08b0c6daff51485fed221dada9abf50b76c1b21578d9114eb6e8b04439f08567d848d103657f817b3cc9f0f56d6aaa865fdec5f3364d4e973a615d3c678c46d24ef6a5092467af5282d759ac85ab6ddc86f7940538013a8ac2a75b8f55d218d676b22e0233e75626c794cd5df64b3ca8e4d761f596d717641060a070ec85451321f862d8b6e264798656159acebf68ef653455550ed9d73e8dd6e322610ff6fe554945adde3df0e934e5d59c365226238c54b96dea088cac8ef748634160ae6807a6672628926c81a8975450e27337306f88e4e9ff8c85
C1 = 0x3a7bdc206e63785573ef49ca4e4bf812237f2cdc0e0204978ab2d6d4e6faa3bbbaea937608bbf000f3f4fbdd1a6a9d690015072c75e1daba85cc3bb8e1ff1428161e67213b104e7ac9435bfcb504a733fcb89df0af34c7c73c4df2bf1c97e3e56bee4f3f1dd52fcda4f40a5e33c5486135afaaa1f5032ea59bd71517b8b692e512322cdd2ba03df5b41f107939d5f361862b58fd2973a296b0c4c9bf095e181746b69d0da6ec625a00e740fe6e3dcdd83e76f0a9511a868832aa536aa0866debba92369a491d240939956266239a3e242f3d25a3415ddcccbd47c4ccc1eb376062b19e4018a29991f870786e7195eba1bd0d1cf8790832deff6f9fa5eaa49489

N2 = 0x34becd5b321d34414e7b13d9499f29a00bfeda5dcaaeadaf27de31a8afb45c303ef0e6734575d106973189de4ffdd5646eed5a90814c5f5b40bbf504e4ee4dfa50e6c99a80cb166fe7612756951a3edefe5d61c461938001c65bbf89adae4f16d4ac7ec4890b7899cea9ed284add7500af5a41b436e7314a90c2ce763264f3ada70a747663bb5a7b0648672103b1bf1fb93cf9f66bc3d262f2cd53b5be6f7cb2b1d44f9768faf53c7a9cfedaad46958a71278ede34c7dfb4a0c4b51a2e2526e333d4a2da11d6f0bb97ec41982046684204ec8b4903bbc92f7d9736d82b4d388b8624d9b9ba1569ecdd818c0ccbaa868d892a012f65b332ab4ed7d002ec505fcd
C2 = 0xe1cb3e348dcf16e66ed1ec2fc9c91289aaf532187807608983a53211cadf1080e3d7ed54fe5fa8e2dacdd6c101c0d02bcba1a69c0f14d1c80fd880e7480c2695087570e16e07bdf6216578354701a7b729c1cba93f06e0dfe3e4df60db9dcd688290ea90abca0864595fe69c7c3454781db4e150fe411374a9f9d57f1bca655148bc3fac531ffb49b5008f7465242ce8eaec4b0e9641f0455ba9e634f8ffb6a12be2099c926d8b7e47d496ddbd5c34ede8c0cf116575cc5e646bc68762da3d1b3e01db463fe1f5dbcf66e250b949b5e46aeb4d14214dfeefc0f47b9f8124fb7f600003f267e6ffd09d1c5c59f82540e4acbd25e756e6999c16c897675775076

N3 = 0x1506b0cb9d5eed45f8a1ebd9721f43214d857fdb21980b21e6c031e48a1194d520c9fd6b48adc1a1d12a1708b81ff7d4ee77d06e21567cc3bf0bc52036ef65fa96ae3efb871d32db854d0db286b7fdd7013cb7d71873a0a8e379fa1d38a385179cc0eaa55b617236d993c39ea4b4995eb7b5fcfa06c8d50ed3c245d7a72d57b8817f2cef8ad57b5c71482c14c210b3ace47539f109d117015f6c8bb82bd8dcbfe4985d5fd0bc7e1cf58275dc628538b3d64ad5f30404072858fe6298380795814e95ec4abb83ba4ece47f2f3a35bbe66b5eb76208540cac03f7c71d17ab37fa466f0cfc5514c06bc98e5982a19d8a6d57ad5107a9e1dc24b119f9ca852744569
C3 = 0x132e7f056cdd5e4f980ca06a29ba16697672a78ead81040ca452d071189e8917757cc33ff40738d4bddcbfea638c4e1ea61c6606ed5430085257a0aa2c17fa7d5ffc4987868374b5c427457ae235e7c692c27629a7039a750781619ac3534a3d4cfcf76b6cec65c9a05668382a8fa998db0796bd735bd3fc9bee82b1e31d23f4e415cbb75fcf3dc957684e619e38467b28b1d33881c17da9fe94577dccdf7aae4a7d05de3888adc4fefff07bb3fd06e317ecee61261919a46019a129c1b22dc21c600931e84efd59acdb45393cdcdb9649d2d5c64bd3878b9a0a3afabec1e4237fcf91f495b44effeeeb0453bae965943e5882863044581cf65576ff3150d850

import functools
import itertools

def chinese_remainder(n, a):
    sum = 0
    prod = functools.reduce(lambda a, b: a*b, n)
    for n_i, a_i in zip(n, a):
        p = prod // n_i
        sum += a_i * mul_inv(p, n_i) * p
    return sum % prod
 
def mul_inv(a, b):
    b0 = b
    x0, x1 = 0, 1
    if b == 1: return 1
    while a > 1:
        q = a // b
        a, b = b, a%b
        x0, x1 = x1 - q * x0, x0
    if x1 < 0: x1 += b0
    return x1

def inv_pow(c, e):
    low = -1
    high = c+1
    while low + 1 < high:
        m = (low + high) // 2
        p = pow(m, e)
        if p < c:
            low = m
        else:
            high = m
    m = high
    assert pow(m, e) == c
    return m
 
N = [N1, N2, N3]
C = [C1, C2, C3]
e = len(N)
a = chinese_remainder(N, C)
for n, c in zip(N, C):
    assert a % n == c
m = inv_pow(a, e)
flag = ('%x' % m).decode('hex')
print flag

実行結果は以下の通り。

Particular applications of the Coppersmith method for attacking RSA include cases when the public exponent e is small or when partial knowledge of the secret key is available. N1CTF{A_sm4ll_l34k_l3ad5_t0_l4rge_br34k}
N1CTF{A_sm4ll_l34k_l3ad5_t0_l4rge_br34k}

rsa_padding (Crypto)

まず以下のコードで暗号化のコードを得る。

import socket
import re
import itertools
import string
import hashlib

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('47.75.39.249', 23333))

data = s.recv(256)
print data

pattern = 'sha256\(\"(.+)\"\+str\)\.hexdigest\(\)\.startswith\(\"(.+)\"\)'
m = re.search(pattern, data)
pre_str = m.group(1)
pre_hash = m.group(2)

for c in itertools.product(string.digits + string.letters, repeat=4):
    tail_str = ''.join(c)
    if hashlib.sha256(pre_str + tail_str).hexdigest().startswith(pre_hash):
        break

print tail_str
s.sendall(tail_str + '\n')

data = s.recv(1024)
data += s.recv(1024)
data += s.recv(1024)
print data
print '1'
s.sendall('1\n')
data = s.recv(4096)
print data

以下のコードでサーバが動作していることがわかる。

#!/usr/bin/env python3
# -*- coding=utf-8 -*-

from Crypto.Util.number import getPrime, GCD, bytes_to_long
from hashlib import sha256
import random
import signal
import sys, os

signal.alarm(20)

m = b"xxxxxxxxxxxxxx"
n = 21727106551797231400330796721401157037131178503238742210927927256416073956351568958100038047053002307191569558524956627892618119799679572039939819410371609015002302388267502253326720505214690802942662248282638776986759094777991439524946955458393011802700815763494042802326575866088840712980094975335414387283865492939790773300256234946983831571957038601270911425008907130353723909371646714722730577923843205527739734035515152341673364211058969041089741946974118237091455770042750971424415176552479618605177552145594339271192853653120859740022742221562438237923294609436512995857399568803043924319953346241964071252941
e = 3

def welcom():
    batch = """
 _   _      __ _         _____ _______ ______
| \ | |    /_ | |       / ____|__   __|  ____|
|  \| |_   _| | |      | |       | |  | |__
| . ` | | | | | |      | |       | |  |  __|
| |\  | |_| | | |____  | |____   | |  | |
|_| \_|\__,_|_|______|  \_____|  |_|  |_|

_|_|_|      _|_|_|    _|_|          _|_|_|    _|_|    _|      _|  _|_|_|_|
_|    _|  _|        _|    _|      _|        _|    _|  _|_|  _|_|  _|
_|_|_|      _|_|    _|_|_|_|      _|  _|_|  _|_|_|_|  _|  _|  _|  _|_|_|
_|    _|        _|  _|    _|      _|    _|  _|    _|  _|      _|  _|
_|    _|  _|_|_|    _|    _|        _|_|_|  _|    _|  _|      _|  _|_|_|_|      
"""
    print(batch)

def proof():
    strings = "abcdefghijklmnopqrstuvwxyzWOERFJASKL"
    prefix = "".join(random.sample(strings, 6))
    starwith = str(random.randint(10000, 99999))
    pf = """
sha256("%s"+str).hexdigest().startswith("%s") == True
Please give me str
"""%(prefix, starwith)
    print(pf)
    s = input().strip()
    if sha256((prefix+s).encode()).hexdigest().startswith(starwith):
        return True
    else:
        return False

def cmd():
    help = """
1. get code
2. get flag
Please tell me, what you want?
"""
    while True:
        print(help)
        c = input().strip()
        if c == "1":
            return True
        elif c == "2":
            return False
        else:
            print("Enter Error!")

def main():
    if not proof():
        print("Check Failed!")
        return
    welcom()
    if cmd():
        f = open("file.py")
        print(f.read())
        return
    mm = bytes_to_long(m)
    assert pow(mm, e) != pow(mm, e, n)
    sys.stdout.write("Please give me a padding: ")
    padding = input().strip()
    padding = int(sha256(padding.encode()).hexdigest(),16)
    c = pow(mm+padding, e, n)
    print("Your Ciphertext is: %s"%c)

if __name__ == '__main__':
    main()

このコードを見ると、入力した文字列をsha256したものをプラスして暗号化している。
2パターンのプラスした値とその暗号のペアを得ることができれば、Franklin-Reiter Related Message Attackで復号できる。
まず2パターンのデータを得る。

import socket
import re
import itertools
import string
import hashlib

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('47.75.39.249', 23333))

data = s.recv(256)
print data

pattern = 'sha256\(\"(.+)\"\+str\)\.hexdigest\(\)\.startswith\(\"(.+)\"\)'
m = re.search(pattern, data)
pre_str = m.group(1)
pre_hash = m.group(2)

for c in itertools.product(string.digits + string.letters, repeat=4):
    tail_str = ''.join(c)
    if hashlib.sha256(pre_str + tail_str).hexdigest().startswith(pre_hash):
        break

print tail_str
s.sendall(tail_str + '\n')

data = s.recv(1024)
data += s.recv(1024)
data += s.recv(1024)
print data
print '2'
s.sendall('2\n')
data = s.recv(1024)
print data

pad = 'a'
print pad
s.sendall(pad + '\n')
data = s.recv(1024)
print data

上記はaを入力したパターンで、以下の結果が得られた。

Your Ciphertext is: 14550589053226237723784378782911157204367764723816660214902465302717810822172021499672797688101690836227536656522669877557513678726451567934772958540881870505027812672770632131417158771747207631406190598426624499318100261365041342574487198815092211749558434576684546969401968650344265478567760472921329074234371969691478468223496149493810842447332952014841777151128845522350571280750495062964803524353826424837916218613026269361845916011417547716655151014908270156235727462738771351489212657321400055033617868608092797200321917477463676573100374585542816545631299684842142462655847321565714756762304545470196198340385

同様にbの場合は以下の結果が得られた。

Your Ciphertext is: 14550589053226237723784378782911157204367764723812418468791010699229875830463647355158844707705886546430710672657478975121784614197819889919141286816518375155221497149366869190177475417434888910395438707022979956329590154012896676989382415396402741552931485301047383627371895227749949128440632032518188813954527856952846604591394762443469965587269478029933983601737740826745184166883892469527983987217466171262168882709572986062075929388484286172431551877578026734690125212518284473430174129218378709797148961525512754353241835286245103580265565835556405824029469560545138200760717522255129021253354200673658556765641

この情報から復号する。

# related_message_attack.sage
from hashlib import sha256

def related_message_attack(c1, c2, diff, e, n):
    PRx.<x> = PolynomialRing(Zmod(n))
    g1 = x^e - c1
    g2 = (x+diff)^e - c2

    def gcd(g1, g2):
        while g2:
            g1, g2 = g2, g1 % g2
        return g1.monic()

    return -gcd(g1, g2)[0]

n = 21727106551797231400330796721401157037131178503238742210927927256416073956351568958100038047053002307191569558524956627892618119799679572039939819410371609015002302388267502253326720505214690802942662248282638776986759094777991439524946955458393011802700815763494042802326575866088840712980094975335414387283865492939790773300256234946983831571957038601270911425008907130353723909371646714722730577923843205527739734035515152341673364211058969041089741946974118237091455770042750971424415176552479618605177552145594339271192853653120859740022742221562438237923294609436512995857399568803043924319953346241964071252941
c1 = 14550589053226237723784378782911157204367764723816660214902465302717810822172021499672797688101690836227536656522669877557513678726451567934772958540881870505027812672770632131417158771747207631406190598426624499318100261365041342574487198815092211749558434576684546969401968650344265478567760472921329074234371969691478468223496149493810842447332952014841777151128845522350571280750495062964803524353826424837916218613026269361845916011417547716655151014908270156235727462738771351489212657321400055033617868608092797200321917477463676573100374585542816545631299684842142462655847321565714756762304545470196198340385
c2 = 14550589053226237723784378782911157204367764723812418468791010699229875830463647355158844707705886546430710672657478975121784614197819889919141286816518375155221497149366869190177475417434888910395438707022979956329590154012896676989382415396402741552931485301047383627371895227749949128440632032518188813954527856952846604591394762443469965587269478029933983601737740826745184166883892469527983987217466171262168882709572986062075929388484286172431551877578026734690125212518284473430174129218378709797148961525512754353241835286245103580265565835556405824029469560545138200760717522255129021253354200673658556765641
e = 3

h1 = int(sha256('a').hexdigest(), 16)
h2 = int(sha256('b').hexdigest(), 16)
diff = h2 - h1
m = related_message_attack(c1, c2, diff, e, n) - h1
flag = ('%x' % m).decode('hex')
print flag

実行結果は以下の通り。

Welcom to Nu1L CTF, Congratulations, You get flag, and flag is N1CTF{f7efbf4e5f5ef78ca1fb9c8f5eb02635}
N1CTF{f7efbf4e5f5ef78ca1fb9c8f5eb02635}