<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>ADP</title><description>My Blog</description><link>https://dphuoc.github.io/</link><language>en</language><item><title>Hack The Box: Apocalypse 2024</title><link>https://dphuoc.github.io/posts/htb-apocalypse2024/</link><guid isPermaLink="true">https://dphuoc.github.io/posts/htb-apocalypse2024/</guid><pubDate>Sat, 06 Apr 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;1. Dynasty (very easy)&lt;/h1&gt;
&lt;h2&gt;Attachments:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;chall.py&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;from random import randint
from secret import FLAG

def to_identity_map(a):
    return ord(a) - 0x41


def from_identity_map(a):
    return chr(a % 26 + 0x41)


def encrypt(m):
    c = &apos;&apos;
    for i in range(len(m)):
        ch = m[i]
        if not ch.isalpha():
            ech = ch
        else:
            chi = to_identity_map(ch)
            ech = from_identity_map(chi + i)
        c += ech
    return c


with open(&apos;output.txt&apos;, &apos;w&apos;) as f:
    f.write(&apos;Make sure you wrap the decrypted text with the HTB flag format :-]\n&apos;)
    f.write(encrypt(FLAG))
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;output.txt&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;Make sure you wrap the decrypted text with the HTB flag format :-]
DJF_CTA_SWYH_NPDKK_MBZ_QPHTIGPMZY_KRZSQE?!_ZL_CN_PGLIMCU_YU_KJODME_RYGZXL
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Solution:&lt;/h2&gt;
&lt;p&gt;Ta thấy FLAG được encrypt bằng cách:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Nếu ký tự trong flag không thuộc bảng chữ cái thì $enc_i = FLAG_i$&lt;/li&gt;
&lt;li&gt;Nếu thuộc bảng chữ cái thì $enc_i = chr(ord(FLAG_i - 0x41 + i) \ % \ 26 + 0x41)$&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;$\rightarrow FLAG_i = chr(ord(enc_i - 0x41 - i) \ % \ 26 + 0x41)$&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;enc = &quot;DJF_CTA_SWYH_NPDKK_MBZ_QPHTIGPMZY_KRZSQE?!_ZL_CN_PGLIMCU_YU_KJODME_RYGZXL&quot;

FLAG = &quot;&quot;

for i in range(len(enc)):
    if enc[i].isalpha():
        FLAG += chr((ord(enc[i]) - 0x41 - i) % 26 + 0x41)
    else:
        FLAG += enc[i]

print(f&quot;HTB{{{FLAG}}}&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;FLAG&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;HTB{DID_YOU_KNOW_ABOUT_THE_TRITHEMIUS_CIPHER?!_IT_IS_SIMILAR_TO_CAESAR_CIPHER}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;2. Make shift (very easy)&lt;/h1&gt;
&lt;h2&gt;Attachments:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;chall.py&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;from secret import FLAG

flag = FLAG[::-1]
new_flag = &apos;&apos;

for i in range(0, len(flag), 3):
    new_flag += flag[i+1]
    new_flag += flag[i+2]
    new_flag += flag[i]

print(new_flag)
# &quot;!?}De!e3d_5n_nipaOw_3eTR3bt4{_THB&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Solution:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Nếu các bạn tinh ý nhận ra thì code để encrypt cũng chính là code để descrypt&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;enc = &quot;!?}De!e3d_5n_nipaOw_3eTR3bt4{_THB&quot;

enc = enc[::-1]

flag = &apos;&apos;

for i in range(0, len(enc), 3):
    flag += enc[i + 1]
    flag += enc[i + 2]
    flag += enc[i]

print(flag)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;FLAG&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;HTB{4_b3tTeR_w3apOn_i5_n3edeD!?!}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;3. Primary Knowledge (very easy)&lt;/h1&gt;
&lt;h2&gt;Attachments:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;chall.py&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;import math
from Crypto.Util.number import getPrime, bytes_to_long
from secret import FLAG

m = bytes_to_long(FLAG)

n = math.prod([getPrime(1024) for _ in range(2 ** 0)])
e = 0x10001
c = pow(m, e, n)

with open(&apos;output.txt&apos;, &apos;w&apos;) as f:
    f.write(f&apos;{n = }\n&apos;)
    f.write(f&apos;{e = }\n&apos;)
    f.write(f&apos;{c = }\n&apos;)
# n = 144595784022187052238125262458232959109987136704231245881870735843030914418780422519197073054193003090872912033596512666042758783502695953159051463566278382720140120749528617388336646147072604310690631290350467553484062369903150007357049541933018919332888376075574412714397536728967816658337874664379646535347
# e = 65537
# c = 15114190905253542247495696649766224943647565245575793033722173362381895081574269185793855569028304967185492350704248662115269163914175084627211079781200695659317523835901228170250632843476020488370822347715086086989906717932813405479321939826364601353394090531331666739056025477042690259429336665430591623215
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Solution:&lt;/h2&gt;
&lt;p&gt;Khi đọc kỹ code thì ta thấy được rằng $n$ là một số nguyên tố
Và ta biết rằng phi hàm Euler của $n$ chính là $n - 1$
Dựa vào đó ta có thể&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from Crypto.Util.number import long_to_bytes

n = 144595784022187052238125262458232959109987136704231245881870735843030914418780422519197073054193003090872912033596512666042758783502695953159051463566278382720140120749528617388336646147072604310690631290350467553484062369903150007357049541933018919332888376075574412714397536728967816658337874664379646535347
e = 65537
c = 15114190905253542247495696649766224943647565245575793033722173362381895081574269185793855569028304967185492350704248662115269163914175084627211079781200695659317523835901228170250632843476020488370822347715086086989906717932813405479321939826364601353394090531331666739056025477042690259429336665430591623215

phi = n - 1

d = inverse(e, phi)
m = pow(c, d, n)
print(long_to_bytes(m))
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;FLAG&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;HTB{0h_d4mn_4ny7h1ng_r41s3d_t0_0_1s_1!!!}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;4. Blunt (easy)&lt;/h1&gt;
&lt;h2&gt;Attachments:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;chall.py&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from Crypto.Util.number import getPrime, long_to_bytes
from hashlib import sha256

from secret import FLAG

import random

p = getPrime(32)
print(f&apos;p = 0x{p:x}&apos;)

g = random.randint(1, p - 1)
print(f&apos;g = 0x{g:x}&apos;)

a = random.randint(1, p - 1)
b = random.randint(1, p - 1)

A, B = pow(g, a, p), pow(g, b, p)

print(f&apos;A = 0x{A:x}&apos;)
print(f&apos;B = 0x{B:x}&apos;)

C = pow(A, b, p)
assert C == pow(B, a, p)

# now use it as shared secret
hash = sha256()
hash.update(long_to_bytes(C))

key = hash.digest()[:16]
iv = b&apos;\xc1V2\xe7\xed\xc7@8\xf9\\\xef\x80\xd7\x80L*&apos;
cipher = AES.new(key, AES.MODE_CBC, iv)

encrypted = cipher.encrypt(pad(FLAG, 16))
print(f&apos;ciphertext = {encrypted}&apos;)
# p = 0xdd6cc28d
# g = 0x83e21c05
# A = 0xcfabb6dd
# B = 0xc4a21ba9
# ciphertext = b&apos;\x94\x99\x01\xd1\xad\x95\xe0\x13\xb3\xacZj{\x97|z\x1a(&amp;amp;\xe8\x01\xe4Y\x08\xc4\xbeN\xcd\xb2*\xe6{&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Solution&lt;/h2&gt;
&lt;p&gt;Vì các số đều là số nguyên tố nhỏ nên có thể dùng &lt;a href=&quot;https://www.alpertron.com.ar/DILOG.HTM&quot;&gt;tool&lt;/a&gt; hoặc sử dụng hàm &lt;code&gt;discrete_log&lt;/code&gt; trong &lt;code&gt;sage&lt;/code&gt; để tính được $a$
Từ đó ta sẽ có key của AES&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from Crypto.Util.number import *
from hashlib import sha256
from tqdm import tqdm

p = 0xdd6cc28d
g = 0x83e21c05
A = 0xcfabb6dd
B = 0xc4a21ba9
ciphertext = b&apos;\x94\x99\x01\xd1\xad\x95\xe0\x13\xb3\xacZj{\x97|z\x1a(&amp;amp;\xe8\x01\xe4Y\x08\xc4\xbeN\xcd\xb2*\xe6{&apos;
iv = b&apos;\xc1V2\xe7\xed\xc7@8\xf9\\\xef\x80\xd7\x80L*&apos;

a = 2766777741

C = pow(B, a, p)

hash = sha256()
hash.update(long_to_bytes(C))

key = hash
key = key.digest()[:16]
cipher = AES.new(key, AES.MODE_CBC, iv)

decrypted = cipher.decrypt(ciphertext)
print(f&apos;decrypted = {decrypted}&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;FLAG&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;HTB{y0u_n3ed_a_b1gGeR_w3ap0n!!}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;5. Iced Tea (easy)&lt;/h1&gt;
&lt;h2&gt;Attachments&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;chall.py&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;import os
from Crypto.Util.Padding import pad
from Crypto.Util.number import bytes_to_long as b2l, long_to_bytes as l2b
from enum import Enum

from secret import FLAG

class Mode(Enum):
    ECB = 0x01
    CBC = 0x02


class Cipher:
    def __init__(self, key, iv=None):
        self.BLOCK_SIZE = 64
        self.KEY = [b2l(key[i:i + self.BLOCK_SIZE // 16]) for i in range(0, len(key), self.BLOCK_SIZE // 16)]
        self.DELTA = 0x9e3779b9
        self.IV = iv
        if self.IV:
            self.mode = Mode.CBC
        else:
            self.mode = Mode.ECB

    def _xor(self, a, b):
        return b&apos;&apos;.join(bytes([_a ^ _b]) for _a, _b in zip(a, b))

    def encrypt(self, msg):
        msg = pad(msg, self.BLOCK_SIZE // 8)
        blocks = [msg[i:i + self.BLOCK_SIZE // 8] for i in range(0, len(msg), self.BLOCK_SIZE // 8)]

        ct = b&apos;&apos;
        print(self.mode)
        if self.mode == Mode.ECB:
            for pt in blocks:
                ct += self.encrypt_block(pt)
        elif self.mode == Mode.CBC:
            X = self.IV
            for pt in blocks:
                enc_block = self.encrypt_block(self._xor(X, pt))
                ct += enc_block
                X = enc_block
        return ct

    def encrypt_block(self, msg):
        m0 = b2l(msg[:4])
        m1 = b2l(msg[4:])
        K = self.KEY
        msk = (1 &amp;lt;&amp;lt; (self.BLOCK_SIZE // 2)) - 1

        s = 0
        for i in range(32):
            s += self.DELTA
            m0 += ((m1 &amp;lt;&amp;lt; 4) + K[0]) ^ (m1 + s) ^ ((m1 &amp;gt;&amp;gt; 5) + K[1])
            m0 &amp;amp;= msk
            m1 += ((m0 &amp;lt;&amp;lt; 4) + K[2]) ^ (m0 + s) ^ ((m0 &amp;gt;&amp;gt; 5) + K[3])
            m1 &amp;amp;= msk

        m = ((m0 &amp;lt;&amp;lt; (self.BLOCK_SIZE // 2)) + m1) &amp;amp; ((1 &amp;lt;&amp;lt; self.BLOCK_SIZE) - 1)  # m = m0 || m1

        return l2b(m)


if __name__ == &apos;__main__&apos;:
    KEY = os.urandom(16)
    cipher = Cipher(KEY)
    ct = cipher.encrypt(FLAG)
    with open(&apos;output.txt&apos;, &apos;w&apos;) as f:
        f.write(f&apos;Key : {KEY.hex()}\nCiphertext : {ct.hex()}&apos;)
        
# Key : 42ed720514532a272f972569a56f8ff0
# Ciphertext : 8850a0af3c9c09610e044b6df550e4e2bb65162e479b4ccfea201fd3533cd8
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Solution:&lt;/h2&gt;
&lt;p&gt;Sau khi đọc code một hồi thì mình nhận thấy rằng Block Cipher này có một nhược điểm là không có &lt;code&gt;confusion&lt;/code&gt; và &lt;code&gt;diffusion&lt;/code&gt;
Vì vậy mình hoàn toàn có thể reverse lại code để từ đó viết hàm &lt;code&gt;descrypt_block&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from Crypto.Cipher import AES
import os
from Crypto.Util.Padding import pad
from Crypto.Util.number import bytes_to_long as b2l, long_to_bytes as l2b
from enum import Enum

Key = &quot;850c1413787c389e0b34437a6828a1b2&quot;
Ciphertext = &quot;b36c62d96d9daaa90634242e1e6c76556d020de35f7a3b248ed71351cc3f3da97d4d8fd0ebc5c06a655eb57f2b250dcb2b39c8b2000297f635ce4a44110ec66596c50624d6ab582b2fd92228a21ad9eece4729e589aba644393f57736a0b870308ff00d778214f238056b8cf5721a843&quot;


class Mode(Enum):
    ECB = 0x01
    CBC = 0x02


class Cipher:
    def __init__(self, key, iv=None):
        self.BLOCK_SIZE = 64
        self.KEY = [b2l(key[i:i + self.BLOCK_SIZE // 16]) for i in range(0, len(key), self.BLOCK_SIZE // 16)]
        self.DELTA = 0x9e3779b9
        self.IV = iv
        if self.IV:
            self.mode = Mode.CBC
        else:
            self.mode = Mode.ECB

    def _xor(self, a, b):
        return b&apos;&apos;.join(bytes([_a ^ _b]) for _a, _b in zip(a, b))

    def encrypt(self, msg):
        msg = pad(msg, self.BLOCK_SIZE // 8)
        blocks = [msg[i:i + self.BLOCK_SIZE // 8] for i in range(0, len(msg), self.BLOCK_SIZE // 8)]

        ct = b&apos;&apos;
        print(self.mode)
        if self.mode == Mode.ECB:
            for pt in blocks:
                ct += self.encrypt_block(pt)
        elif self.mode == Mode.CBC:
            X = self.IV
            for pt in blocks:
                enc_block = self.encrypt_block(self._xor(X, pt))
                ct += enc_block
                X = enc_block
        return ct

    def descrypt(self, msg):
        msg = pad(msg, self.BLOCK_SIZE // 8)
        blocks = [msg[i:i + self.BLOCK_SIZE // 8] for i in range(0, len(msg), self.BLOCK_SIZE // 8)]

        ct = b&apos;&apos;
        print(self.mode)
        if self.mode == Mode.ECB:
            for pt in blocks:
                ct += self.descrypt_block(pt)
        elif self.mode == Mode.CBC:
            X = self.IV
            for pt in blocks:
                enc_block = self.descrypt_block(self._xor(X, pt))
                ct += enc_block
                X = enc_block
        return ct

    def encrypt_block(self, msg):
        m0 = b2l(msg[:4])
        m1 = b2l(msg[4:])
        K = self.KEY
        msk = (1 &amp;lt;&amp;lt; (self.BLOCK_SIZE // 2)) - 1

        s = 0
        for i in range(32):
            s += self.DELTA
            m0 += ((m1 &amp;lt;&amp;lt; 4) + K[0]) ^ (m1 + s) ^ ((m1 &amp;gt;&amp;gt; 5) + K[1])
            m0 &amp;amp;= msk
            m1 += ((m0 &amp;lt;&amp;lt; 4) + K[2]) ^ (m0 + s) ^ ((m0 &amp;gt;&amp;gt; 5) + K[3])
            m1 &amp;amp;= msk

        m = ((m0 &amp;lt;&amp;lt; (self.BLOCK_SIZE // 2)) + m1) &amp;amp; ((1 &amp;lt;&amp;lt; self.BLOCK_SIZE) - 1)  # m = m0 || m1

        return l2b(m)

    def descrypt_block(self, msg):
        m0 = b2l(msg[:4])
        m1 = b2l(msg[4:])
        K = self.KEY
        msk = (1 &amp;lt;&amp;lt; (self.BLOCK_SIZE // 2)) - 1

        s = self.DELTA &amp;lt;&amp;lt; 5
        for i in range(32):
            m1 -= ((m0 &amp;lt;&amp;lt; 4) + K[2]) ^ (m0 + s) ^ ((m0 &amp;gt;&amp;gt; 5) + K[3])
            m1 &amp;amp;= msk
            m0 -= ((m1 &amp;lt;&amp;lt; 4) + K[0]) ^ (m1 + s) ^ ((m1 &amp;gt;&amp;gt; 5) + K[1])
            m0 &amp;amp;= msk
            s -= self.DELTA
        m = ((m0 &amp;lt;&amp;lt; (self.BLOCK_SIZE // 2)) + m1) &amp;amp; ((1 &amp;lt;&amp;lt; self.BLOCK_SIZE) - 1)

        return l2b(m)


if __name__ == &apos;__main__&apos;:
    KEY = bytes.fromhex(Key)
    cipher = Cipher(KEY)
    ct = bytes.fromhex(Ciphertext)
    print(cipher.descrypt(ct))
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;FLAG&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;HTB{th1s_1s_th3_t1ny_3ncryp710n_4lg0r1thm_____y0u_m1ght_h4v3_4lr34dy_s7umbl3d_up0n_1t_1f_y0u_d0_r3v3rs1ng}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;6. Partial Tenacity (Medium)&lt;/h1&gt;
&lt;h2&gt;Attachments:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;chall.py&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP

from secret import FLAG

class RSACipher:
    def __init__(self, bits):
        self.key = RSA.generate(bits)
        self.cipher = PKCS1_OAEP.new(self.key)

    def encrypt(self, m):
        return self.cipher.encrypt(m)

    def decrypt(self, c):
        return self.cipher.decrypt(c)


cipher = RSACipher(1024)

enc_flag = cipher.encrypt(FLAG)

with open(&apos;output.txt&apos;, &apos;w&apos;) as f:
    f.write(f&apos;n = {cipher.key.n}\n&apos;)
    f.write(f&apos;ct = {enc_flag.hex()}\n&apos;)
    print(cipher.key.p)
    print(str(cipher.key.p)[::2])
    print(cipher.key.q)
    print(str(cipher.key.q)[1::2])
    f.write(f&apos;p = {str(cipher.key.p)[::2]}\n&apos;)
    f.write(f&apos;q = {str(cipher.key.q)[1::2]}&apos;)
    
# n = 118641897764566817417551054135914458085151243893181692085585606712347004549784923154978949512746946759125187896834583143236980760760749398862405478042140850200893707709475167551056980474794729592748211827841494511437980466936302569013868048998752111754493558258605042130232239629213049847684412075111663446003
# ct = &quot;7f33a035c6390508cee1d0277f4712bf01a01a46677233f16387fae072d07bdee4f535b0bd66efa4f2475dc8515696cbc4bc2280c20c93726212695d770b0a8295e2bacbd6b59487b329cc36a5516567b948fed368bf02c50a39e6549312dc6badfef84d4e30494e9ef0a47bd97305639c875b16306fcd91146d3d126c1ea476&quot;
# p = 151441473357136152985216980397525591305875094288738820699069271674022167902643
# q = 15624342005774166525024608067426557093567392652723175301615422384508274269305
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Solution:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Sau khi đọc code ta thấy t sẽ nhận được một nửa của $p$ và $q$&lt;/li&gt;
&lt;li&gt;Ở bài này ta có thể xài &lt;code&gt;BFS&lt;/code&gt; để tìm 2 số nguyên tố $p$ và $q$&lt;/li&gt;
&lt;li&gt;Để tăng tốc độ và độ chính xác ta sẽ thêm 2 điều kiện
&lt;ul&gt;
&lt;li&gt;Nếu $p_i$ * $q_i$ &amp;gt; $n$ thì sẽ skip state đó&lt;/li&gt;
&lt;li&gt;Chúng ta sẽ kiểm tra xem những chữ số cuối của $p_i$ * $q_i$ có giống với những chữ số cuối của $n$ không. Nếu không ta cũng sẽ skip state đó&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;from Crypto.Util.number import long_to_bytes, inverse

from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP


n = 118641897764566817417551054135914458085151243893181692085585606712347004549784923154978949512746946759125187896834583143236980760760749398862405478042140850200893707709475167551056980474794729592748211827841494511437980466936302569013868048998752111754493558258605042130232239629213049847684412075111663446003
ct = &quot;7f33a035c6390508cee1d0277f4712bf01a01a46677233f16387fae072d07bdee4f535b0bd66efa4f2475dc8515696cbc4bc2280c20c93726212695d770b0a8295e2bacbd6b59487b329cc36a5516567b948fed368bf02c50a39e6549312dc6badfef84d4e30494e9ef0a47bd97305639c875b16306fcd91146d3d126c1ea476&quot;
p = 151441473357136152985216980397525591305875094288738820699069271674022167902643
q = 15624342005774166525024608067426557093567392652723175301615422384508274269305

pstr = str(p)
qstr = str(q)
cnt = 1
nums = [(pstr[-1], &quot;1&quot;, 1)]
while True:
    nums2 = []
    for p, q, i in nums:
        pi = int(p)
        qi = int(q)
        cnt = cnt + 1
        if pi * qi == n:
            print(&quot;found&quot;)
            assert pi * qi == n

            key = RSA.construct((n, 65537, inverse(65537, (pi - 1) * (qi - 1)), pi, qi))
            cipher = PKCS1_OAEP.new(key)

            print(cipher.decrypt(bytes.fromhex(ct)))
            exit()

        if pi * qi &amp;gt; n:
            continue

        if (n - pi * qi) % (10 ** i) != 0:
            continue

        for j in range(10):
            if i % 2 == 1:
                nums2.append((str(j) + p, qstr[-(i // 2 + 1)] + q, i + 1))
            else:
                nums2.append((pstr[-(i // 2 + 1)] + p, str(j) + q, i + 1))

    nums = nums2
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;FLAG&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;HTB{v3r1fy1ng_pr1m3s_m0dul0_p0w3rs_0f_10!}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;7. Arranged (Medium)&lt;/h1&gt;
&lt;h2&gt;Attachments&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;chall.sage&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from Crypto.Util.number import long_to_bytes
from hashlib import sha256

from secret import FLAG, p, b, priv_a, priv_b

F = GF(p)
E = EllipticCurve(F, [726, b])
G = E(926644437000604217447316655857202297402572559368538978912888106419470011487878351667380679323664062362524967242819810112524880301882054682462685841995367, 4856802955780604241403155772782614224057462426619061437325274365157616489963087648882578621484232159439344263863246191729458550632500259702851115715803253)

A = G * priv_a
B = G * priv_b

print(A)
print(B)

C = priv_a * B

assert C == priv_b * A

# now use it as shared secret
secret = C[0]

hash = sha256()
hash.update(long_to_bytes(secret))

key = hash.digest()[16:32]
iv = b&apos;u\x8fo\x9aK\xc5\x17\xa7&amp;gt;[\x18\xa3\xc5\x11\x9en&apos;
cipher = AES.new(key, AES.MODE_CBC, iv)

encrypted = cipher.encrypt(pad(FLAG, 16))
print(encrypted)
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;output.txt&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;(6174416269259286934151093673164493189253884617479643341333149124572806980379124586263533252636111274525178176274923169261099721987218035121599399265706997 : 2456156841357590320251214761807569562271603953403894230401577941817844043774935363309919542532110972731996540328492565967313383895865130190496346350907696 : 1)
(4226762176873291628054959228555764767094892520498623417484902164747532571129516149589498324130156426781285021938363575037142149243496535991590582169062734 : 425803237362195796450773819823046131597391930883675502922975433050925120921590881749610863732987162129269250945941632435026800264517318677407220354869865 : 1)
b&apos;V\x1b\xc6&amp;amp;\x04Z\xb0c\xec\x1a\tn\xd9\xa6(\xc1\xe1\xc5I\xf5\x1c\xd3\xa7\xdd\xa0\x84j\x9bob\x9d&quot;\xd8\xf7\x98?^\x9dA{\xde\x08\x8f\x84i\xbf\x1f\xab&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Solution&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Điều đầu tiên ta cần làm trong bài này là tìm là $p$ và $b$ của ECC&lt;/li&gt;
&lt;li&gt;Challenge cho ta 3 điểm và ta biết được rằng
$\begin{cases}
y_G ^ 2 \equiv x_G ^ 3 + 726&lt;em&gt;x_G + b \ (mod \ p) \
y_A ^ 2 \equiv x_A ^ 3 + 726&lt;/em&gt;x_A + b \ (mod \ p) \
y_B ^ 2 \equiv x_B ^ 3 + 726*x_B + b \ (mod \ p) \
\end{cases}$&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;$\rightarrow \begin{cases}
y_G ^ 2 - y_A ^ 2 - x_G ^ 3 + x_A ^ 3 - 726&lt;em&gt;x_G + 726&lt;/em&gt;x_A  \equiv 0 \ (mod \ p) \
y_A ^ 2 - y_B ^ 2 - x_A ^ 3 + x_B ^ 3 - 726&lt;em&gt;x_A + 726&lt;/em&gt;x_B  \equiv 0 \ (mod \ p) \
y_B ^ 2 - y_G ^ 2 - x_B ^ 3 + x_G ^ 3 - 726&lt;em&gt;x_B + 726&lt;/em&gt;x_G  \equiv 0 \ (mod \ p) \
\end{cases}$&lt;/p&gt;
&lt;p&gt;Đặt lần lượt từng biểu thức trên là $x$,$y$,$z$&lt;/p&gt;
&lt;p&gt;$\rightarrow p = gcd(x, y, z)$&lt;/p&gt;
&lt;p&gt;$\rightarrow b = (y_A^2 - x_a^3 - 726 * x_a) \ % \ p$&lt;/p&gt;
&lt;p&gt;Tiếp đến khi check ta sẽ thấy được rằng &lt;code&gt;G.order() = 11&lt;/code&gt;. Vì thế ta có thể bruteforce từ 1 đến 11 để tìm &lt;code&gt;secret&lt;/code&gt; đúng&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from Crypto.Util.number import long_to_bytes
from hashlib import sha256
import math

A = (6174416269259286934151093673164493189253884617479643341333149124572806980379124586263533252636111274525178176274923169261099721987218035121599399265706997, 2456156841357590320251214761807569562271603953403894230401577941817844043774935363309919542532110972731996540328492565967313383895865130190496346350907696)
B = (4226762176873291628054959228555764767094892520498623417484902164747532571129516149589498324130156426781285021938363575037142149243496535991590582169062734, 425803237362195796450773819823046131597391930883675502922975433050925120921590881749610863732987162129269250945941632435026800264517318677407220354869865)
G = (926644437000604217447316655857202297402572559368538978912888106419470011487878351667380679323664062362524967242819810112524880301882054682462685841995367, 4856802955780604241403155772782614224057462426619061437325274365157616489963087648882578621484232159439344263863246191729458550632500259702851115715803253)

enc = b&apos;V\x1b\xc6&amp;amp;\x04Z\xb0c\xec\x1a\tn\xd9\xa6(\xc1\xe1\xc5I\xf5\x1c\xd3\xa7\xdd\xa0\x84j\x9bob\x9d&quot;\xd8\xf7\x98?^\x9dA{\xde\x08\x8f\x84i\xbf\x1f\xab&apos;

a = 726

tmp1 = (A[1] ^ 2 - B[1] ^ 2 - A[0] ^ 3 + B[0] ^ 3 - a * A[0] + a * B[0])
tmp2 = (G[1] ^ 2 - A[1] ^ 2 - G[0] ^ 3 + A[0] ^ 3 - a * G[0] + a * A[0])
tmp3 = (G[1] ^ 2 - B[1] ^ 2 - G[0] ^ 3 + B[0] ^ 3 - a * G[0] + a * B[0])

p = math.gcd(tmp1, tmp2, tmp3)
b = (A[1] ^ 2 - A[0] ^ 3 - a * A[0]) % p

F = GF(p)
E = EllipticCurve(F, [a, b])

G = E(G)
A = E(A)
B = E(B)

iv = b&apos;u\x8fo\x9aK\xc5\x17\xa7&amp;gt;[\x18\xa3\xc5\x11\x9en&apos;

for i in range(12):
    C = A * int(i)
    secret = C[0]

    hash = sha256()
    hash.update(long_to_bytes(int(secret)))

    key = hash.digest()[16:32]
    cipher = AES.new(key, AES.MODE_CBC, iv)

    decrypted = cipher.decrypt(enc)
    if b&apos;HTB{&apos; in decrypted:
        print(decrypted.decode())
        break
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;FLAG&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;HTB{0rD3r_mUsT_b3_prEs3RveD_!!@!}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;8. Tsayaky (Hard)&lt;/h1&gt;
&lt;h2&gt;Attachments&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;chall.py&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;from tea import Cipher as TEA
from secret import IV, FLAG
import os

ROUNDS = 10

def show_menu():
    print(&quot;&quot;&quot;
============================================================================================
|| I made this decryption oracle in which I let users choose their own decryption keys.   ||
|| I think that it&apos;s secure as the tea cipher doesn&apos;t produce collisions (?) ... Right?   ||
|| If you manage to prove me wrong 10 times, you get a special gift.                      ||
============================================================================================
&quot;&quot;&quot;)

def run():
    show_menu()

    server_message = os.urandom(20)
    print(f&apos;Here is my special message: {server_message.hex()}&apos;)
    
    used_keys = []
    ciphertexts = []
    for i in range(ROUNDS):
        print(f&apos;Round {i+1}/10&apos;)
        try:
            ct = bytes.fromhex(input(&apos;Enter your target ciphertext (in hex) : &apos;))
            assert ct not in ciphertexts

            for j in range(4):
                key = bytes.fromhex(input(f&apos;[{i+1}/{j+1}] Enter your encryption key (in hex) : &apos;))
                assert len(key) == 16 and key not in used_keys
                used_keys.append(key)
                cipher = TEA(key, IV)
                enc = cipher.encrypt(server_message)
                if enc != ct:
                    print(f&apos;Hmm ... close enough, but {enc.hex()} does not look like {ct.hex()} at all! Bye...&apos;)
                    exit()
        except:
            print(&apos;Nope.&apos;)
            exit()
            
        ciphertexts.append(ct)

    print(f&apos;Wait, really? {FLAG}&apos;)


if __name__ == &apos;__main__&apos;:
    run()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Solution&lt;/h2&gt;
&lt;p&gt;Đầu tiên ta cần phải hiểu flow của chương trình. Khi netcat vào, server sẽ chả về cho chúng ta một &lt;code&gt;message&lt;/code&gt; ở dạng hex.
Nhiệm vụ của ta là phải hoàn thành 10 rounds với công việc là tìm đúng &lt;code&gt;ciphertext&lt;/code&gt; sau khi encrypt &lt;code&gt;message&lt;/code&gt; với 4 &lt;code&gt;key&lt;/code&gt; khác nhau.&lt;/p&gt;
&lt;p&gt;Ta thấy được chương trình này dựa trên TeaCipher theo mode CBC (Một loại cipher mà chúng ta đã làm việc ở bài ICED TEA)&lt;/p&gt;
&lt;p&gt;Trong code của bài trên thì ta thấy có 2 MODE là ECB và CBC&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://hackmd.io/_uploads/B1kAxcR1C.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://hackmd.io/_uploads/H135ZcR10.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Ta thấy rằng khi ta nhập sai thì server sẽ trả lại ta chuỗi encrypt của message&lt;/p&gt;
&lt;p&gt;Vậy nên nếu chúng ta lấy chuỗi và decrypt theo ECB thì ta sẽ có được message chưa xor với IV
Vậy ta chỉ cần xor với message ban đầu sẽ có &lt;code&gt;IV = b&apos;\r\xdd\xd2w&amp;lt;\xf4\xb9\x08\&apos;&lt;/code&gt;
IV chỉ có 8 bytes thôi nhé, lúc đầu mình cũng nghĩ mãi sao chỉ có 8 mà không phải 16 bytes =)))&lt;/p&gt;
&lt;p&gt;Giờ thì công việc của chúng ta chỉ còn làm thế nào để generate ra 4 key mà không làm thay đổi giá trị sau khi encrypt thôi&lt;/p&gt;
&lt;p&gt;Sau một chút thời gian osint thì ta tìm thấy được với 1 key sẽ có thể tạo ra 3 equivalent keys dưới đây&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://hackmd.io/_uploads/SkKuH9RJ0.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Giờ đến lúc ta bắt tay vào code nào:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from Crypto.Util.Padding import pad
from Crypto.Util.number import bytes_to_long as b2l, long_to_bytes as l2b
from enum import Enum
from pwn import *


class Mode(Enum):
    ECB = 0x01
    CBC = 0x02


class Cipher:
    def __init__(self, key, iv=None):
        self.BLOCK_SIZE = 64
        self.KEY = [b2l(key[i:i + self.BLOCK_SIZE // 16]) for i in range(0, len(key), self.BLOCK_SIZE // 16)]
        self.DELTA = 0x9e3779b9
        self.IV = iv
        if self.IV:
            self.mode = Mode.CBC
        else:
            self.mode = Mode.ECB

    def _xor(self, a, b):
        return b&apos;&apos;.join(bytes([_a ^ _b]) for _a, _b in zip(a, b))

    def encrypt(self, msg):
        msg = pad(msg, self.BLOCK_SIZE // 8)
        blocks = [msg[i:i + self.BLOCK_SIZE // 8] for i in range(0, len(msg), self.BLOCK_SIZE // 8)]

        ct = b&apos;&apos;
        print(self.mode)
        if self.mode == Mode.ECB:
            for pt in blocks:
                ct += self.encrypt_block(pt)
        elif self.mode == Mode.CBC:
            X = self.IV
            for pt in blocks:
                enc_block = self.encrypt_block(self._xor(X, pt))
                ct += enc_block
                X = enc_block
        return ct

    def descrypt(self, msg):
        msg = pad(msg, self.BLOCK_SIZE // 8)
        blocks = [msg[i:i + self.BLOCK_SIZE // 8] for i in range(0, len(msg), self.BLOCK_SIZE // 8)]

        ct = b&apos;&apos;
        print(self.mode)
        if self.mode == Mode.ECB:
            for pt in blocks:
                ct += self.descrypt_block(pt)
        elif self.mode == Mode.CBC:
            X = self.IV
            for pt in blocks:
                enc_block = self.descrypt_block(self._xor(X, pt))
                ct += enc_block
                X = enc_block
        return ct

    def encrypt_block(self, msg):
        m0 = b2l(msg[:4])
        m1 = b2l(msg[4:])
        K = self.KEY
        msk = (1 &amp;lt;&amp;lt; (self.BLOCK_SIZE // 2)) - 1

        s = 0
        for i in range(32):
            s += self.DELTA
            m0 += ((m1 &amp;lt;&amp;lt; 4) + K[0]) ^ (m1 + s) ^ ((m1 &amp;gt;&amp;gt; 5) + K[1])
            m0 &amp;amp;= msk
            m1 += ((m0 &amp;lt;&amp;lt; 4) + K[2]) ^ (m0 + s) ^ ((m0 &amp;gt;&amp;gt; 5) + K[3])
            m1 &amp;amp;= msk

        m = ((m0 &amp;lt;&amp;lt; (self.BLOCK_SIZE // 2)) + m1) &amp;amp; ((1 &amp;lt;&amp;lt; self.BLOCK_SIZE) - 1)  # m = m0 || m1

        return l2b(m)

    def descrypt_block(self, msg):
        m0 = b2l(msg[:4])
        m1 = b2l(msg[4:])
        K = self.KEY
        msk = (1 &amp;lt;&amp;lt; (self.BLOCK_SIZE // 2)) - 1

        s = self.DELTA &amp;lt;&amp;lt; 5
        for i in range(32):
            m1 -= ((m0 &amp;lt;&amp;lt; 4) + K[2]) ^ (m0 + s) ^ ((m0 &amp;gt;&amp;gt; 5) + K[3])
            m1 &amp;amp;= msk
            m0 -= ((m1 &amp;lt;&amp;lt; 4) + K[0]) ^ (m1 + s) ^ ((m1 &amp;gt;&amp;gt; 5) + K[1])
            m0 &amp;amp;= msk
            s -= self.DELTA
        m = ((m0 &amp;lt;&amp;lt; (self.BLOCK_SIZE // 2)) + m1) &amp;amp; ((1 &amp;lt;&amp;lt; self.BLOCK_SIZE) - 1)

        return l2b(m)

def keys(key):
    h = 0x80000000
    h = long_to_bytes(h)

    k = [key[i:i + 4] for i in range(0, 16, 4)]
    K0 = k[0] + k[1] + k[2] + k[3]
    K1 = k[0] + k[1] + xor(k[2], h) + xor(k[3], h)
    K2 = xor(k[0], h) + xor(k[1], h) + k[2] + k[3]
    K3 = xor(k[0], h) + xor(k[1], h) + xor(k[2], h) + xor(k[3], h)
    return [K0, K1, K2, K3]   

r = remote(&quot;83.136.252.62&quot;, 44483)

ROUNDS = 10
IV = b&apos;\r\xdd\xd2w&amp;lt;\xf4\xb9\x08&apos;

r.recvuntil(b&apos;: &apos;)
msg = r.recvuntil(b&apos;\n&apos;).split(b&apos;\n&apos;)[0].decode()

for round in range(ROUNDS):
    key = os.urandom(16)
    ct = Cipher(key=key, iv=IV).encrypt(bytes.fromhex(msg))
    r.sendlineafter(b&apos;x) : &apos;, bytes.hex(ct).encode())
    temp = keys(key)
    for k in temp: 
        r.sendlineafter(b&apos;x) : &apos;, bytes.hex(k).encode())
        
r.interactive()

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tài liệu tham khảo: https://www.tayloredge.com/reference/Mathematics/VRAndem.pdf&lt;/p&gt;
&lt;h2&gt;FLAG&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;HTB{th1s_4tt4ck_m4k3s_T34_1n4ppr0pr14t3_f0r_h4sh1ng!}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;9. Permuted (Hard)&lt;/h1&gt;
&lt;h2&gt;Attachments&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;chall.py&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from Crypto.Util.number import long_to_bytes

from hashlib import sha256
from random import shuffle

from secret import a, b, FLAG

class Permutation:
    def __init__(self, mapping):
        self.length = len(mapping)

        assert set(mapping) == set(range(self.length))     # ensure it contains all numbers from 0 to length-1, with no repetitions
        self.mapping = list(mapping)

    def __call__(self, *args, **kwargs):
        idx, *_ = args
        assert idx in range(self.length)
        return self.mapping[idx]

    def __mul__(self, other):
        ans = []

        for i in range(self.length):
            ans.append(self(other(i)))

        return Permutation(ans)

    def __pow__(self, power, modulo=None):
        ans = Permutation.identity(self.length)
        ctr = self

        while power &amp;gt; 0:
            if power % 2 == 1:
                ans *= ctr
            ctr *= ctr
            power //= 2

        return ans

    def __str__(self):
        return str(self.mapping)

    def identity(length):
        return Permutation(range(length))


x = list(range(50_000))
shuffle(x)

g = Permutation(x)
print(&apos;g =&apos;, g)

A = g**a
print(&apos;A =&apos;, A)
B = g**b
print(&apos;B =&apos;, B)

C = A**b
assert C.mapping == (B**a).mapping

sec = tuple(C.mapping)
sec = hash(sec)
sec = long_to_bytes(sec)

hash = sha256()
hash.update(sec)

key = hash.digest()[16:32]
iv = b&quot;mg&apos;g\xce\x08\xdbYN2\x89\xad\xedlY\xb9&quot;

cipher = AES.new(key, AES.MODE_CBC, iv)

encrypted = cipher.encrypt(pad(FLAG, 16))
print(&apos;c =&apos;, encrypted)
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://drive.usercontent.google.com/u/0/uc?id=1gaB-UsN6GQohcy32CwZxakS_LGM1XqRh&amp;amp;export=download&quot;&gt;output.txt&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Solution&lt;/h2&gt;
&lt;p&gt;Sau một hồi thử thì ta rút ra một nhận xét rằng với mỗi vị trí trong Permution sẽ có 1 chu trình (Chu trình của từng vị trí có thể giống nhau hoặc khác)
Ta cũng nhận xét được rằng số chu trình nhiều nhất cũng chỉ là n chu trình
Và đến sau khi hoàn thành chu trình nó sẽ trở về trạng thái ban đầu
Vậy nếu chỉ cần biết độ dài chu trình của từng vị trí ta có thể sử dụng CRT để tìm ra được số bước cần thực hiện.&lt;/p&gt;
&lt;p&gt;Về cơ bản nó sẽ trông như thế này:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    G = [xG0, xG1, xG2, ... , xGn]

--&amp;gt; A = [xA0, xA1, xA2, ..., xAn]

Thì ta cần tìm 1 số sao cho
    x % (độ dài chu trình của xG0) = xA0
    x % (độ dài chu trình của xG1) = xA1
    x % (độ dài chu trình của xG2) = xA2
    ...
    x % (độ dài chu trình của xGn) = xAn
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vì trong giải mình không biết sử dụng sage nên mình đã xài C++ để tìm chu trình (Quá s4d)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;bits/stdc++.h&amp;gt;
using namespace std;

const long long MAXN = 1e5 + 10;
const long long MOD = 1e9 + 7;

long long n, g[MAXN], a[MAXN], root[MAXN], heso[MAXN], mod[MAXN], check = 0, cnt = 0;

int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);

    freopen(&quot;solve.txt&quot;, &quot;r&quot;, stdin);
    freopen(&quot;test.txt&quot;, &quot;w&quot;, stdout);

    cin &amp;gt;&amp;gt; n;
    for (int i = 0; i &amp;lt; n; i++) {
        cin &amp;gt;&amp;gt; g[i];
        root[i] = g[i];
    }
    cin &amp;gt;&amp;gt; n;
    for (int i = 0; i &amp;lt; n; i++) {
        cin &amp;gt;&amp;gt; a[i];
    }

    while (check &amp;lt; 2 * n) {
        cnt++;
        for (int i = 0; i &amp;lt; n; i++) {
            g[i] = root[g[i]];
            if (g[i] == a[i] &amp;amp;&amp;amp; heso[i] == 0) {
                heso[i] = cnt;
                check++;
            }
            if (g[i] == root[i] &amp;amp;&amp;amp; mod[i] == 0) {
                mod[i] = cnt;
                check++;
            }
        }
    }

    for (int i = 0; i &amp;lt; n; i++) {
        cout &amp;lt;&amp;lt; heso[i] &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; mod[i] &amp;lt;&amp;lt; endl;
    }

}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sau khi tìm chu trình xong mình CRT nó và tìm FLAG thôi&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from Crypto.Util.number import long_to_bytes

from hashlib import sha256
from random import shuffle
import output

class Permutation:
    def __init__(self, mapping):
        self.length = len(mapping)

        assert set(mapping) == set(range(self.length))     # ensure it contains all numbers from 0 to length-1, with no repetitions
        self.mapping = list(mapping)

    def __call__(self, *args, **kwargs):
        idx, *_ = args
        assert idx in range(self.length)
        return self.mapping[idx]

    def __mul__(self, other):
        ans = []

        for i in range(self.length):
            ans.append(self(other(i)))

        return Permutation(ans)

    def __pow__(self, power, modulo=None):
        ans = Permutation.identity(self.length)
        ctr = self

        while power &amp;gt; 0:
            if power % 2 == 1:
                ans *= ctr
            ctr *= ctr
            power //= 2

        return ans

    def __str__(self):
        return str(self.mapping)

    def identity(length):
        return Permutation(range(length))

g = Permutation(output.g)
# print(&apos;g =&apos;, g)

VALUECRT = []
MODCRT = []

with open(&quot;test.txt&quot;, &quot;r&quot;) as f:
    for line in f:
        a, b = map(int, line.split())
        VALUECRT.append(a)
        MODCRT.append(b)

a = CRT_list(VALUECRT, MODCRT) + 1
A = Permutation(output.A)
B = Permutation(output.B)

C = B ** a

sec = tuple(C.mapping)
sec = hash(sec)
sec = long_to_bytes(sec)

hash = sha256()
hash.update(sec)

key = hash.digest()[16:32]
iv = b&quot;mg&apos;g\xce\x08\xdbYN2\x89\xad\xedlY\xb9&quot;

cipher = AES.new(key, AES.MODE_CBC, iv)

print(cipher.decrypt(output.c).decode())
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;FLAG&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;HTB{w3lL_n0T_aLl_gRoUpS_aRe_eQUaL_!!}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;10. ROT 128 (Insane) (Sau giải)&lt;/h1&gt;
&lt;h2&gt;Attachments&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;chall.py&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;import random, os, signal
from Crypto.Util.number import long_to_bytes as l2b, bytes_to_long as b2l
from secret import FLAG

ROUNDS = 3
USED_STATES = []
_ROL_ = lambda x, i : ((x &amp;lt;&amp;lt; i) | (x &amp;gt;&amp;gt; (N-i))) &amp;amp; (2**N - 1)
N = 128

def handler(signum, frame):
    print(&quot;\n\nToo slow, don&apos;t try to do sneaky things.&quot;)
    exit()

def validate_state(state):
    if not all(0 &amp;lt; s &amp;lt; 2**N-1 for s in user_state[-2:]) or not all(0 &amp;lt;= s &amp;lt; N for s in user_state[:4]):
        print(&apos;Please, make sure your input satisfies the upper and lower bounds.&apos;)
        return False
    
    if sorted(state[:4]) in USED_STATES:
        print(&apos;You cannot reuse the same state&apos;)
        return False
    
    if sum(user_state[:4]) &amp;lt; 2:
        print(&apos;We have to deal with some edge cases...&apos;)
        return False

    return True

class HashRoll:
    def __init__(self):
        self.reset_state()

    def hash_step(self, i):
        r1, r2 = self.state[2*i], self.state[2*i+1]
        return _ROL_(self.state[-2], r1) ^ _ROL_(self.state[-1], r2)

    def update_state(self, state=None):
        if not state:
            self.state = [0] * 6
            self.state[:4] = [random.randint(0, N) for _ in range(4)]
            self.state[-2:] = [random.randint(0, 2**N) for _ in range(2)]
        else:
            self.state = state
    
    def reset_state(self):
        self.update_state()

    def digest(self, buffer):
        buffer = int.from_bytes(buffer, byteorder=&apos;big&apos;)
        m1 = buffer &amp;gt;&amp;gt; N
        m2 = buffer &amp;amp; (2**N - 1)
        self.h = b&apos;&apos;
        for i in range(2):
            self.h += int.to_bytes(self.hash_step(i) ^ (m1 if not i else m2), length=N//8, byteorder=&apos;big&apos;)
        return self.h


print(&apos;Can you test my hash function for second preimage resistance? You get to select the state and I get to choose the message ... Good luck!&apos;)

hashfunc = HashRoll()

for _ in range(ROUNDS):
    print(f&apos;ROUND {_+1}/{ROUNDS}!&apos;)

    server_msg = os.urandom(32)
    hashfunc.reset_state()
    server_hash = hashfunc.digest(server_msg)
    print(f&apos;You know H({server_msg.hex()}) = {server_hash.hex()}&apos;)

    signal.signal(signal.SIGALRM, handler)
    signal.alarm(2)

    user_state = input(&apos;Send your hash function state (format: a,b,c,d,e,f) :: &apos;).split(&apos;,&apos;)

    try:
        user_state = list(map(int, user_state))

        if not validate_state(user_state):
            print(&quot;The state is not valid! Try again.&quot;)
            exit()

        hashfunc.update_state(user_state)

        if hashfunc.digest(server_msg) == server_hash:
            print(f&apos;Moving on to the next round!&apos;)
            USED_STATES.append(sorted(user_state[:4]))
        else:
            print(&apos;Not today.&apos;)
            exit()
    except:
        print(&quot;The hash function&apos;s state must be all integers.&quot;)
        exit()
    finally:
       signal.alarm(0)

print(f&apos;Uhm... how did you do that? I thought I had cryptanalyzed it enough ... {FLAG}&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Solution&lt;/h2&gt;
&lt;p&gt;Về bài này thì server sẽ random 32 bytes &lt;code&gt;msg&lt;/code&gt; sau đó với các random state $(a, b, c, d, e, f)$
Nhiệm vụ của chúng ta là tìm các $(a, b, c, d, e, f)$ sao cho xảy ra hiện tượng hash collision với $0 \leqslant a, b, c, d &amp;lt; N$, $0 &amp;lt; e, f &amp;lt; 2^N - 1$ và tổng của $a, b, c, d &amp;gt; 2$ (Để tránh TH đặt biệt)&lt;/p&gt;
&lt;p&gt;Ta thấy rằng có thể ta sẽ lấy lại được kết quả của hàm &lt;code&gt;hash_step&lt;/code&gt; bằng cách sau:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Break the plaintext (pt) into 16 byte blocks
plt0 = pt &amp;gt;&amp;gt; N
plt1 = pt &amp;amp; (2**N - 1)

# Recover the hash_step results
H0 = plt0 ^ ht0
H1 = plt1 ^ ht1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Đến đây rồi thì ta có thể sử dụng &lt;code&gt;Z3&lt;/code&gt; để bruteforce các state sao cho phù hợp =))))&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from z3 import *
from pwn import *
import re

def findHex(text):
    pattern = r&apos;\b[a-fA-F0-9]{64}\b&apos;
    hex_strings = re.findall(pattern, text)
    return hex_strings

N = 128
_ROL_ = lambda x, i: ((x &amp;lt;&amp;lt; i) | (x &amp;gt;&amp;gt; (N - i))) &amp;amp; (2 ** N - 1)

HOST = &quot;...&quot;
PORT = &quot;...&quot;


while True:
    try:
        r = remote(HOST, PORT)
        # r = process([&quot;python3&quot;, &quot;server.py&quot;])
        context.log_level = &apos;debug&apos;
        cntTrue = 0

        for i in range(3):
            data = r.recvuntil(b&quot;f) ::&quot;).decode()
            data = findHex(data)
            print(data)
            plt = int(data[0], 16)
            hashStr = data[1]

            plt0 = plt &amp;gt;&amp;gt; N
            plt1 = plt &amp;amp; (2 ** N - 1)

            H0 = int(hashStr[:32], 16) ^ plt0
            H1 = int(hashStr[32:], 16) ^ plt1

            solver = Solver()

            e, f = BitVecs(&apos;e f&apos;, N)
            a, b, c, d = BitVecs(&apos;a b c d&apos;, 7)
            a, b, c, d = ZeroExt(128 - 7, a), ZeroExt(128 - 7, b), ZeroExt(128 - 7, c), ZeroExt(128 - 7, d)

            solver.add(_ROL_(e, a) ^ _ROL_(f, b) == H0)
            solver.add(_ROL_(e, c) ^ _ROL_(f, d) == H1)

            if solver.check() == sat:
                res = solver.model()
                res = {d.name(): res[d] for d in res.decls()}
                payload = f&quot;{res[&apos;a&apos;]},{res[&apos;b&apos;]},{res[&apos;c&apos;]},{res[&apos;d&apos;]},{res[&apos;e&apos;]},{res[&apos;f&apos;]}&quot;
                r.sendline(payload.encode())
                cntTrue += 1
        
        if cntTrue == 3:
            r.interactive()
            exit(0)

    except:
        pass

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Hãy cùng cầu mong code chạy được nào hihi&lt;/p&gt;
&lt;h2&gt;FLAG&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;HTB{k33p_r0t4t1ng_4nd_r0t4t1ng_4nd_x0r1ng_4nd_r0t4t1ng!}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>picoCTF - Cryptography</title><link>https://dphuoc.github.io/posts/picoctf/</link><guid isPermaLink="true">https://dphuoc.github.io/posts/picoctf/</guid><pubDate>Wed, 20 Mar 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Flag - printer&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://artifacts.picoctf.net/c_titan/19/encoded.txt&quot;&gt;encoded.txt&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://artifacts.picoctf.net/c_titan/19/flag_printer.py&quot;&gt;flag_printer.py&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;import galois
import numpy as np
MOD = 7514777789

points = []

for line in open(&apos;encoded.txt&apos;, &apos;r&apos;).read().strip().split(&apos;\n&apos;):
    x, y = line.split(&apos; &apos;)
    points.append((int(x), int(y)))

GF = galois.GF(MOD)

matrix = []
solution = []
for point in points:
    x, y = point
    solution.append(GF(y % MOD))

    row = []
    for i in range(len(points)):
        row.append(GF((x ** i) % MOD))
    
    matrix.append(GF(row))

open(&apos;output.bmp&apos;, &apos;wb&apos;).write(bytearray(np.linalg.solve(GF(matrix), GF(solution)).tolist()[:-1]))
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Solution&lt;/h2&gt;
&lt;p&gt;Ta thấy rằng file &lt;code&gt;encoded.txt&lt;/code&gt; bao gồm tập hợp các điểm với $x$ nằm từ &lt;code&gt;0&lt;/code&gt; --&amp;gt; &lt;code&gt;1769610&lt;/code&gt; và giá trị y random.
Và code mẫu đề bài cho là thực hiện giải ma trận dưới đây, tuy nhiên nó chạy rất chậm và độ phức tạp là $O(n^3)$&lt;/p&gt;
&lt;p&gt;$\begin{bmatrix}
a_0 \
a_1 \
a_2 \
a_3 \
\vdots \
a_n \
\end{bmatrix} \begin{bmatrix}
0^0 &amp;amp; 0^1 &amp;amp; 0^2 &amp;amp; 0^3 &amp;amp; \dots  &amp;amp; 0^n \
1^0 &amp;amp; 1^1 &amp;amp; 1^2 &amp;amp; 1^3 &amp;amp; \dots  &amp;amp; 1^n \
2^0 &amp;amp; 2^1 &amp;amp; 2^2 &amp;amp; 2^3 &amp;amp; \dots  &amp;amp; 2^n \
3^0 &amp;amp; 3^1 &amp;amp; 3^2 &amp;amp; 3^3 &amp;amp; \dots  &amp;amp; 3^n \
\vdots &amp;amp; \vdots &amp;amp; \vdots &amp;amp; \vdots &amp;amp; \ddots  &amp;amp; \vdots \
n^0 &amp;amp; n^1 &amp;amp; n^2 &amp;amp; n^3 &amp;amp; \dots  &amp;amp; n^n \
\end{bmatrix} =
\begin{bmatrix}
y_0 \
y_1 \
y_2 \
y_3 \
\vdots \
y_n \
\end{bmatrix}$&lt;/p&gt;
&lt;p&gt;Sau một lúc osint thì ta nhận thấy rằng đây là ma trận Vandermonde và nó liên quan đến Larange interpolation (nội suy larange).
Và ta có được công thức:&lt;/p&gt;
&lt;p&gt;$f(x)=\sum_{i=1}^{n+1} f(x_i) * \prod_{j\neq i \ j = 1}^{n+1} \frac{x-x_j}{x_i-x_j}$&lt;/p&gt;
&lt;p&gt;Cùng với đó ta cũng tìm thấy được một blog: https://codeforces.com/blog/entry/94143&lt;/p&gt;
&lt;p&gt;Đầu tiên ta sẽ giảm độ phức tạp của việc tính $\prod_{j\neq i}(x_i-x_j)$&lt;/p&gt;
&lt;p&gt;Thay vì với mỗi vòng lặp ta sẽ chạy lại để tính thì ta có thể làm như sau:&lt;/p&gt;
&lt;p&gt;$\prod_{j\neq i}(x_i-x_j)$ = $\lim_{x\to x_i}\frac{\prod_{j=1}^{n+1} (x-x_j)}{x-x_i}$&lt;/p&gt;
&lt;p&gt;Ở đây to có thể nhân hết vào và chia đi $x_i - x_i$. Điều tuyệt vời là ta có thể sử dụng lim để xử lý việc $x_i - x_i = 0$&lt;/p&gt;
&lt;p&gt;$\lim_{x\to x_i}\frac{\prod_{j=1}^{n+1} (x-x_j)}{x-x_i}=\lim_{x\to x_i}\frac{d}{dx}(\prod_{i=1}^{n+1} x-x_j)$&lt;/p&gt;
&lt;p&gt;Thế nên việc của ta là chỉ cần tìm $P&apos;(x)$ với $P(x) = \prod_{i=1}^{n+1} x-x_j$ rồi thế từng $x_i$ vô.&lt;/p&gt;
&lt;p&gt;Tiếp theo ta sẽ đến giảm độ phức tạp của việc nhân nhiều đa thức&lt;/p&gt;
&lt;p&gt;Ta biết rằng nhân đa thức trong sage có $O(n \ log(n))$&lt;/p&gt;
&lt;p&gt;Giả sử có m đa thức thì nếu nhân từ từ từng đa thức lại với nhau ta sẽ mất $O(m * n log(n))$&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://hackmd.io/_uploads/BkANnVh1A.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Còn nếu ta sử dụng chia để trị để nhân đa thức thì ta sẽ mất $(nlog(n) * log(m))$&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://hackmd.io/_uploads/Bk7Gp4hy0.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Giờ việc cuối cùng của ta là là thiết lập công thức&lt;/p&gt;
&lt;p&gt;Đặt $v_i=\frac{y_i}{P&apos;(x_i)}$&lt;/p&gt;
&lt;p&gt;--&amp;gt; $f(x)=\sum_{i=1}^{n+1} v_i\prod_{j\neq i}(x-x_j)$&lt;/p&gt;
&lt;p&gt;$f(x)=f_0(x)P_1(x)+f_1(x)P_0(x)$ với:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$f_0(x)=\sum_{i=1}^{\lfloor\frac{n+1}{2}\rfloor} v_i\prod_{j\neq i,1\leq j\leq \lfloor\frac{n+1}{2}\rfloor}(x-x_j)$&lt;/li&gt;
&lt;li&gt;$f_1(x)=\sum_{i=\lfloor\frac{n+1}{2}+1\rfloor}^{n+1} v_i\prod_{j\neq i,\lfloor\frac{n+1}{2}\rfloor\leq j\leq n+1}(x-x_j)$&lt;/li&gt;
&lt;li&gt;$P_0(x)=\prod_{1\leq j\leq \lfloor\frac{n+1}{2}\rfloor}(x-x_j)$&lt;/li&gt;
&lt;li&gt;$P_1(x)=\prod_{\lfloor\frac{n+1}{2}\rfloor\leq j\leq n+1}(x-x_j)$&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Code dưới đây đã mất tầm 6 - 7 tiếng để chạy và mình đã để chạy qua đêm để ra FLAG. (huhu)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from tqdm import tqdm

MOD = 7514777789

points = []

for line in open(&apos;encoded.txt&apos;, &apos;r&apos;).read().strip().split(&apos;\n&apos;):
    x, y = line.split(&apos; &apos;)
    points.append((int(x), int(y)))

F = GF(MOD)
R.&amp;lt;x&amp;gt; = F[&apos;x&apos;]

def multiply_poly_list(poly_list):
    if len(poly_list) == 1:
        return poly_list[0]
    n = len(poly_list) // 2
    return multiply_poly_list(poly_list[:n]) * multiply_poly_list(poly_list[n:])

P = multiply_poly_list([x - i for i in range(len(points))])
d_P = derivative(P)

def calc_poly(points, start_index, bar):
    bar.update(int(1))
    n = len(points)
    if n == 1:
        x_i = points[0][0]
        y_i = points[0][1]
        v = y_i / d_P(x_i)
        return v


    f0 = calc_poly(points[:n//2], start_index, bar)
    f1 = calc_poly(points[n//2:], start_index + n//2, bar)

    p0 = multiply_poly_list([x - (i + start_index) for i in range(n//2)])
    p1 = multiply_poly_list([x - (i + start_index) for i in range(n//2, n)])
    return f0 * p1 + f1 * p0

n = 2 * len(points) - 1
bar = tqdm(total=int(n))
poly = calc_poly(points, 0, bar)
coeffs = poly.numerator().list()

with open(&apos;out.txt&apos;, &apos;w&apos;) as f:
    f.write(&apos;\n&apos;.join([str(i) for i in coeffs]))

print(&quot;Finished&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;listarr = [int(_) for _ in open(&quot;out.txt&quot;, &quot;r&quot;).read().strip().split(&apos;\n&apos;)]

print(listarr)

open(&apos;output.bmp&apos;, &apos;wb&apos;).write(bytearray(listarr[:-1]))

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://hackmd.io/_uploads/SyXKomhkA.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>UIUCTF 2024</title><link>https://dphuoc.github.io/posts/uiuctf/</link><guid isPermaLink="true">https://dphuoc.github.io/posts/uiuctf/</guid><pubDate>Mon, 01 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Last weekend, I played UIUCTF 2024 with my team &lt;strong&gt;thefwncrew&lt;/strong&gt;. My team finished top 16 in this CTF.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-1.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I solved all Cryptography challenges, and 1 misc challenge. Here is my writeup for challenges that are solved by me.&lt;/p&gt;
&lt;h1&gt;MISC&lt;/h1&gt;
&lt;h2&gt;Slot Machine&lt;/h2&gt;
&lt;p&gt;We have onboard entertainment! Try your luck on our newly installed slot machine.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ncat --ssl slot-machine.chal.uiuc.tf 1337&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;Downloads&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://uiuctf-2024-rctf-challenge-uploads.storage.googleapis.com/uploads/7b0918d4a7d18e34f068eac99f1e97d6aef31a8ed9689a7897b115e9514bfd66/chal.py&quot;&gt;chal.py&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Solution&lt;/h3&gt;
&lt;p&gt;This challenge asks me for a hex string such that it has longest prefix collision in SHA256&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;hash = sha256(bytes.fromhex(lucky_number)[::-1]).digest()[::-1].hex()

length = min(32, int(input(&quot;How lucky are you feeling today? Enter the number of slots: &quot;)))

if len(set(hash[:length])) == 1:
    print(&quot;Congratulations! You&apos;ve won:&quot;)
    flag = open(&quot;flag.txt&quot;).read()
    print(flag[:min(len(flag), length)])
else:
    print(&quot;Better luck next time!&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then I found the site: https://crypto.stackexchange.com/questions/110956/what-s-the-smallest-known-sha256-hash-that-was-ever-produced and its hash is &lt;code&gt;0000000000000000000000005d6f06154c8685146aa7bc3dc9843876c9cefd0f&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;After that you can use this command to find the block
https://github.com/sylvainpelissier/bitcoinhash&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;python3 bitcoinHash.py -v -u  https://blockchain.info/rawblock/0000000000000000000000005d6f06154c8685146aa7bc3dc9843876c9cefd0f

f5b209498bfad650eb000b1917fc119b601e799c18c00b6e562e71300ed1cc93
0ffdcec9763884c93dbca76a1485864c15066f5d000000000000000000000000
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

io = process([&quot;ncat&quot;, &quot;--ssl&quot;, &quot;slot-machine.chal.uiuc.tf&quot;, &quot;1337&quot;])

test = &quot;f5b209498bfad650eb000b1917fc119b601e799c18c00b6e562e71300ed1cc93&quot;
test = bytes.fromhex(test)[::-1]

io.sendlineafter(b&quot;number: &quot;, test.hex().encode())
io.sendlineafter(b&quot;slots: &quot;, b&quot;24&quot;)
io.interactive()
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;FLAG&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;uiuctf{keep_going!_3cyd}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Cryptography&lt;/h1&gt;
&lt;h2&gt;X Marked the Spot&lt;/h2&gt;
&lt;p&gt;A perfect first challenge for beginners. Who said pirates can&apos;t ride trains...&lt;/p&gt;
&lt;h3&gt;Downloads&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://uiuctf-2024-rctf-challenge-uploads.storage.googleapis.com/uploads/9122a5606d14d1ded3cbd5907af07c55f80db318f0ea761c16531d0327acd09b/public.py&quot;&gt;public.py&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://uiuctf-2024-rctf-challenge-uploads.storage.googleapis.com/uploads/150449f10624289342a0f5b2d427b3c09eda679b84c5062def1eccf114bdb505/ct&quot;&gt;ct&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Solution&lt;/h3&gt;
&lt;p&gt;Simple xor problem so i just put my script here.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

from Crypto.Util.number import *

ct = open(&quot;ct&quot;, &quot;rb&quot;).read()
test = b&quot;uiuctf{&quot;
key = xor(ct, test)[:7] + xor(b&apos;}&apos;, ct[-1])

print(xor(key, ct))
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;FLAG&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;uiuctf{n0t_ju5t_th3_st4rt_but_4l50_th3_3nd!!!!!}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Without a Trace&lt;/h2&gt;
&lt;p&gt;Gone with the wind, can you find my flag?&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ncat --ssl without-a-trace.chal.uiuc.tf 1337&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;Downloads&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://uiuctf-2024-rctf-challenge-uploads.storage.googleapis.com/uploads/c17ae894b0920b181b2d753a693fc678e6d0806045e3d7ba6519111dce60898b/server.py&quot;&gt;server.py&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Solution&lt;/h3&gt;
&lt;p&gt;This challege encodes FLAG into a 5x5 matrix (L).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;f = [bytes_to_long(bytes(FLAG[5 * i: 5 * (i + 1)], &apos;utf-8&apos;)) for i in range(5)]
F = [ 
    [0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0],
]
for i in range(5):
    F[i][i] = f[i]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then it takes L times M which is a 5x5 matrix which input being the data we choose.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def inputs():
    print(&quot;[WAT] Define diag(u1, u2, u3. u4, u5)&quot;)
    M = [
        [0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0],
    ]
    for i in range(5):
        try:
            M[i][i] = int(input(f&quot;[WAT] u{i + 1} = &quot;))
        except:
            return None
    return M
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The data we choose must match the challenge&apos;s conditions.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def check(M):
    def sign(sigma):
        l = 0
        for i in range(5):
            for j in range(i + 1, 5):
                if sigma[i] &amp;gt; sigma[j]:
                    l += 1
        return (-1)**l

    res = 0
    for sigma in permutations([0,1,2,3,4]):
        curr = 1
        for i in range(5):
            curr *= M[sigma[i]][i]
        res += sign(sigma) * curr
    return res

 if check(M) == 0:
    print(&quot;[WAT] It&apos;s not going to be that easy...&quot;)
    return
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, Challenge returns us the diagonal sum of the matrix L * M.&lt;/p&gt;
&lt;p&gt;Basically, we know that&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;And we are received $$F_{1}x_{1} + F_{2}x_{2} + F_{3}x_{3} + F_{4}x_{4} + F_{5}x_{5}$$&lt;/p&gt;
&lt;p&gt;If we have 5 equations like that, we can recover FLAG&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from sage.all import *
from Crypto.Util.number import *

M = Matrix([[2, 1, 1, 1, 1], [1, 2, 1, 1, 1], [1, 1, 2, 1, 1], [1, 1, 1, 2, 1], [1, 1, 1, 1, 2]])

M = M.inverse()

res = Matrix([2504408575853, 2440285994541, 2426159182680, 2163980646766, 2465934208374])

res = res * M
FLAG = b&quot;&quot;
for i in res[0]:
    FLAG += long_to_bytes(int(i))

print(FLAG)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;FLAG&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;uiuctf{tr4c1ng_&amp;amp;&amp;amp;_mult5!}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Determined&lt;/h2&gt;
&lt;p&gt;&quot;It is my experience that proofs involving matrices can be shortened by 50% if one throws the matrices out.&quot;&lt;/p&gt;
&lt;h3&gt;Downloads&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://uiuctf-2024-rctf-challenge-uploads.storage.googleapis.com/uploads/7eca7b6525ba52d872109a5296ec74f3a4e58df6c534bfc5f3b54db0a83413f4/server.py&quot;&gt;server.py&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://uiuctf-2024-rctf-challenge-uploads.storage.googleapis.com/uploads/abd71bfb434d295415aed9b5f1f3cd5d84652605cf272461c83ed0b09160bafd/gen.txt&quot;&gt;gen.txt&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://uiuctf-2024-rctf-challenge-uploads.storage.googleapis.com/uploads/d0be34e73989114ae65eadcf8c1c7a16770c17cfe293e9d12245ecaf54cbac30/gen.py&quot;&gt;gen.py&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Solution&lt;/h3&gt;
&lt;p&gt;In sagemath, we can use symbolic to guess the output of the function.&lt;/p&gt;
&lt;p&gt;I use $$p, q, r$$ are variables and after some tries, I choose 0 for the first row and 1 for others. Then the server will give me $$p * q - p * r$$&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from sage.all import *
from Crypto.Util.number import *

test = 69143703864818353130371867063903344721913499237213762852365448402106351546379534901008421404055139746270928626550203483668654060066917995961156948563756763620082694010964627379305998217766380375890208111586340805642016329892173840181871705216180866148524640933007104761115889694872164169659985765092765835334
n = 158794636700752922781275926476194117856757725604680390949164778150869764326023702391967976086363365534718230514141547968577753309521188288428236024251993839560087229636799779157903650823700424848036276986652311165197569877428810358366358203174595667453056843209344115949077094799081260298678936223331932826351
e = 65535
c = 72186625991702159773441286864850566837138114624570350089877959520356759693054091827950124758916323653021925443200239303328819702117245200182521971965172749321771266746783797202515535351816124885833031875091162736190721470393029924557370228547165074694258453101355875242872797209141366404264775972151904835111

test = n - test
p = gcd(test, n)
q = n // p
phi = (p - 1) * (q - 1)
d = inverse(e, phi)
FLAG = pow(int(c), int(d), int(n))
print(long_to_bytes(FLAG))
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;FLAG&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;uiuctf{h4rd_w0rk_&amp;amp;&amp;amp;_d3t3rm1n4t10n}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Naptime&lt;/h2&gt;
&lt;p&gt;I&apos;m pretty tired. Don&apos;t leak my flag while I&apos;m asleep.&lt;/p&gt;
&lt;h3&gt;Downloads&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://uiuctf-2024-rctf-challenge-uploads.storage.googleapis.com/uploads/b51159dd5cbcc1bcdc977c5b007d5031c7fb5ecd0f165f887c78d2a3e3bb1d59/pub.txt&quot;&gt;pub.txt&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://uiuctf-2024-rctf-challenge-uploads.storage.googleapis.com/uploads/677be7c953fb7ce87b9fd8210b2dd250d166f28ad1f1c1d56b0d7716619da65d/enc_dist.sage&quot;&gt;enc_dist.sage&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Solutions&lt;/h3&gt;
&lt;p&gt;It&apos;s the basic Subset Sum Problem so I just put my script here.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from sage.all import *
from Crypto.Util.number import *

a =  [66128, 61158, 36912, 65196, 15611, 45292, 84119, 65338]
ct = [273896, 179019, 273896, 247527, 208558, 227481, 328334, 179019, 336714, 292819, 102108, 208558, 336714, 312723, 158973, 208700, 208700, 163266, 244215, 336714, 312723, 102108, 336714, 142107, 336714, 167446, 251565, 227481, 296857, 336714, 208558, 113681, 251565, 336714, 227481, 158973, 147400, 292819, 289507]

FLAG = b&quot;&quot;
for i in ct:
    M = Matrix(ZZ, 9, 9)
    for j in range(8):
        M[j, j] = 2
        M[j, -1] = a[j]
        M[-1, j] = 1
    
    M[-1, -1] = i
    M = M.LLL()
    bits = &quot;&quot;
    for j in M[0][:-1]:
        if j == 1:
            bits += &quot;0&quot;
        else:
            bits += &quot;1&quot;
    FLAG += long_to_bytes(int(bits, 2))

print(FLAG)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;FLAG&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;uiuctf{i_g0t_sleepy_s0_I_13f7_th3_fl4g}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Snore Signatures&lt;/h2&gt;
&lt;p&gt;These signatures are a bore!&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ncat --ssl snore-signatures.chal.uiuc.tf 1337&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;Downloads&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://uiuctf-2024-rctf-challenge-uploads.storage.googleapis.com/uploads/b477a42de9e90bafc5bd16405fee97b4b532d9b96e25330eb55ae1c2aa45e4f0/chal.py&quot;&gt;chal.py&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Solutions&lt;/h3&gt;
&lt;p&gt;When we look deeply into the function&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def snore_sign(p, q, g, x, m):
    k = randint(1, q - 1)
    r = pow(g, k, p)
    e = hash((r + m) % p) % q
    s = (k + x * e) % q
    return (s, e)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We see that server don&apos;t verify the messages, so we send $m$ and $m + p$ respectively to server.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

io = process([&quot;ncat&quot;, &quot;--ssl&quot;, &quot;snore-signatures.chal.uiuc.tf&quot;, &quot;1337&quot;])
context.log_level = b&apos;&apos;
p = eval(io.recvline().strip().split(b&quot;=&quot;)[1])
q = eval(io.recvline().strip().split(b&quot;=&quot;)[1])
g = eval(io.recvline().strip().split(b&quot;=&quot;)[1])
for i in range(10):
    io.sendlineafter(b&quot;m = &quot;, str(i).encode())
    s = eval(io.recvline().strip().split(b&quot;=&quot;)[1])
    e = eval(io.recvline().strip().split(b&quot;=&quot;)[1])
    io.sendlineafter(b&quot;m = &quot;, str(i + p).encode())
    io.sendlineafter(b&quot;s = &quot;, str(s).encode())

io.interactive()
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;FLAG&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;uiuctf{add1ti0n_i5_n0t_c0nc4t3n4ti0n}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Groups&lt;/h2&gt;
&lt;p&gt;My friend told me that cryptography is unbreakable if moduli are Carmichael numbers instead of primes. I decided to use this CTF to test out this theory.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ncat --ssl groups.chal.uiuc.tf 1337&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;Downloads&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://uiuctf-2024-rctf-challenge-uploads.storage.googleapis.com/uploads/8cac43edf21c7fd322af3087e5387030fd60b9cb18a14c664abb5c280b30c579/challenge.py&quot;&gt;challenge.py&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Solutions&lt;/h3&gt;
&lt;p&gt;This problem we just generate Carmichael numbers and send it to server.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from sage.all import *
import operator
 
first_primes = list(primes(10**7))
 
factors = [2**3, 3, 5, 7, 11, 13, 17]
lam = reduce(operator.mul, factors)
 
P = []
for p in primes(min(10000000, lam)):
    if p &amp;lt; 400:
        continue
    if lam % p and lam % (p - 1) == 0:
        P.append(p)
 
prodlam = reduce(lambda a, b: a * b % lam, P)
prod = reduce(lambda a, b: a * b, P)

proof.arithmetic(False)
 
while 1:
    numlam = 1
    num = 1
    for i in range(20):
        p = choice(P)
        numlam = numlam * p % lam
        num = num * p
        if numlam != prodlam or prod % num != 0:
            continue
        q = prod // num
        print(&quot;candidate&quot;, q)
        print(factor(q))
        break
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, we have &lt;strong&gt;q&lt;/strong&gt; and easily solve this problem by &lt;code&gt;discrete_log&lt;/code&gt; in sagemath&lt;/p&gt;
&lt;h3&gt;FLAG&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;uiuctf{c4rm1ch43l_7adb8e2f019bb4e0e8cd54e92bb6e3893}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Key in a Haystack&lt;/h2&gt;
&lt;p&gt;I encrpyted the flag, but I lost my key in an annoyingly large haystack. Can you help me find it and decrypt the flag?&lt;/p&gt;
&lt;h3&gt;Downloads&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://uiuctf-2024-rctf-challenge-uploads.storage.googleapis.com/uploads/2b60f2f3509c3c337a24cee66613655488c5159a582750129fb47af0f903d69c/chal.py&quot;&gt;chal.py&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Solutions&lt;/h3&gt;
&lt;p&gt;If you know about birthday paradox, you will solve this challenge.&lt;/p&gt;
&lt;p&gt;I save all state of haystack and find the gcd of new haystack with all old haystacks. Finally i can find all 300 primes in haystack and have the key.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from Crypto.Util.number import getPrime
from Crypto.Util.Padding import pad
from Crypto.Cipher import AES
from sage.all import *


from hashlib import md5
from math import prod
import sys
from pwn import *

sys.set_int_max_str_digits(0)

lists = []
haystack = 0
enc = 0
cnt = 0
while True:

    # io = process([&quot;python3&quot;, &quot;chal.py&quot;])
    io = process([&quot;ncat&quot;, &quot;--ssl&quot;, &quot;key-in-a-haystack.chal.uiuc.tf&quot;, &quot;1337&quot;])
    # context.log_level = &apos;DEBUG&apos;
    print(cnt)
    cnt += 1
    enc = io.recvline().strip().split(b&quot;:&quot;)[1][1:].decode()
    haystack = eval(io.recvline().strip().split(b&quot;:&quot;)[1][1:])
    save = haystack
    for i in range(len(lists)):
        key = gcd(haystack, lists[i])
        if key == 1:
            continue
        if haystack != 1 and len(bin(haystack)[2:]) &amp;lt;= 50:
            print(haystack)
            print(len(bin(haystack)[2:]))
            FLAG = AES.new(
	            key = md5(b&quot;%d&quot; % haystack).digest(),
	            mode = AES.MODE_ECB
            ).decrypt(bytes.fromhex(enc))
            print(FLAG)
            exit()

        haystack = haystack // key
    if cnt &amp;gt; 15:
        print(len(bin(haystack)[2:]))
        if len(bin(haystack)[2:]) &amp;lt; 3000:
            print(haystack)
            print(enc)
        
    lists.append(save)
    io.close()
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;FLAG&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;uiuctf{Finding_Key_Via_Small_Subgroups}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>WannaGame Cyber Knight 2024</title><link>https://dphuoc.github.io/posts/wannagame/</link><guid isPermaLink="true">https://dphuoc.github.io/posts/wannagame/</guid><pubDate>Mon, 24 Jun 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Warmup&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;chall.py&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;from Crypto.Util.number import * 
from Crypto.Util.Padding import pad 
from flag import flag 
from gmpy2 import next_prime

message = b&quot;The challenge&apos;s flag is: &quot; + flag
assert len(message) % 3 == 0

part1 = message[ : len(message)//3]
part2 = message[len(message)//3 : 2*len(message)//3]
part3 = message[2*len(message)//3 : ]
sep = &quot;#&quot;*50

## part 1: Warm-up!!!
p1 = getPrime(512)
q1 = getPrime(512)
e1 = 11
n1 = p1 * q1
c1 = pow(bytes_to_long(part1), e1, n1)
print(f&apos;{n1 = }&apos;)
print(f&apos;{c1 = }&apos;)
print(sep)

## part 2: One more warm-up
p2 = getPrime(1024)
q2 = getPrime(1024)
n2 = p2*q2 
e2 = 17
print(f&apos;{n2 = }&apos;)
print(f&apos;Real: {pow(bytes_to_long(part2), e2, n2)}&apos;)
print(f&apos;Fake: {pow(bytes_to_long(pad(part2, 16)), e2, n2)}&apos;)
print(sep)

## part 3: The last &quot;warm-up&quot;
p3,q3 = getPrime(1024), getPrime(1024)
n3 = p3 * q3
power1, power2 = getPrime(128), getPrime(128)
hint1 = pow((5*p3 + q3), power1, n3)
hint2 = pow((3*p3 - 6*q3), power2, n3)

e3 = next_prime(hint1 + hint2)
c3 = pow(bytes_to_long(part3), e3, n3)
print(f&apos;{power1 = }&apos;)
print(f&apos;{power2 = }&apos;)
print(f&apos;{n3 = }&apos;)
print(f&apos;{hint1 = }&apos;)
print(f&apos;e3 = {e3}&apos;)
print(f&apos;c3 = {c3}&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Solution&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Ta thấy &lt;strong&gt;FLAG&lt;/strong&gt; được chia làm 3 phần có độ dài bằng nhau và được encrypt theo những cách riêng.&lt;/li&gt;
&lt;li&gt;Trong lúc diễn ra giải mình đã decrypt part 2 đầu tiên&lt;/li&gt;
&lt;li&gt;Ta có thể thấy được phần &lt;strong&gt;Real&lt;/strong&gt; là &lt;strong&gt;FLAG&lt;/strong&gt; sau khi encrypt còn &lt;strong&gt;FAKE&lt;/strong&gt; là &lt;code&gt;pad(FLAG)&lt;/code&gt; sau khi decrypt&lt;/li&gt;
&lt;li&gt;Từ đây ta có thể bruteforce hết 16 trường hợp padding có thể và tìm &lt;strong&gt;GCD&lt;/strong&gt; giữa 2 phương trình sẽ ra được (x - &lt;strong&gt;FLAG&lt;/strong&gt;)&lt;/li&gt;
&lt;li&gt;Part 2:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;from sage.all import *
from Crypto.Util.number import *
from Crypto.Util.Padding import pad

n2 = 19474122562778153703776906234103999694212644666605609184003798714902765132186442270466413951685774096672600567147343733732980015161843616032962680857426708013412213676449861264155297288378479051436442079937058938840721841860491557712477170366628773688474634289507561262017382779613838956398073685517438883550013563737919252844748012781298212795883037816078874734356565506037923530508679278288097627681354570158653097456586858900957218344882318145320067184688094419746980554117499065442646088691226289819440434740945671726394148366572739206464970977471727197412322136575227898097076974504287523260885786539573649783161
Real = 15636713266108700464255355282572617369877416762456096086219951374063737359559203674454786265210431923339051086905021926447634666321503985769492720281115251569517394409997291661224097948320454420612478527160246859233690225189444979403485318497052362506638619146425116246060495195223943704647051304805754627681254961634405541974848733951519522486396630609303328097791062133825888690738129275001884247653912942268589773666008633702027740289575546500039837328482189167641136880443555991865875325701485342918164649959219812272157959283006261464655737656031536878365520892164396734804695179528627342810772907739309174183304
Fake = 1901702486995025261538235880685531918771879286327896110111123890041702504494161154076737842357693655100992364579118174208323657012644698015659577716664514828814910889887457132697217080176306194565595589316187612945830703382031867854831608485926311704058671506309214522654866677161067649723883561755848469509892417562279714400249673848323509379109641015242793126408247724149712397397815568653344550396176143675147065423111733067480933601053636423334747702190300411842735906299712817499994397771633456847764374609445235276286713165285446045446123174200222508610983202190618557770618976371164659678708230871711252574428

def mygcd(f,g):
    while g != 0:
        r = g
        g = f % g
        f = r
    return f/f.lc()

P = PolynomialRing(Zmod(n2), &apos;x&apos;)   
x = P.gen()
p1 = x ** 17 - Real
test = b&apos;&apos;
for i in range(16):
    padding = pad(test, 16)[i:]
    p2 = (2 ** ((16 - i) * 8) * x + bytes_to_long(padding)) ** 17 - Fake
    t = mygcd(p1, p2)
    if t.degree() == 1:
        print(long_to_bytes(int(-t[0]) % n2))
        break
    test += b&apos;\x01&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;_lA_15_h1hi__75f26f70b3fa1343153e
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Tiếp đến là part 1, sau khi decrypt xong part 2 ta biết được độ dài của từng thành phần là 33 ký tự và ta cũng biết được 25 ký tự đầu + thêm &quot;W1{&quot; là 28 ký tự. Giờ ta chỉ cần small_roots để tìm nốt phần còn lại.&lt;/li&gt;
&lt;li&gt;Part 1:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;from sage.all import *
from Crypto.Util.number import *
from Crypto.Util.Padding import pad


n1 = 78100017093042237362585803611409807219210686771960485296694671535293298684240391999007937128416933959949645805107079926652343615109335481443408319617478348436412156203990196717356668119025919695447421356649708220217528303406969275048476878616591756855326600516077838718677582495646251901652030452534739980093
c1 = 21635094418936882229318713100837056867730099226953717083953338957077431886511252266065867668680214410547969294123420920750052299754539343424046939642019856856637610693840992502539517724666655388883948397275320947350529293996784254524553566619189584795444284261967162826834735297201762488824862476117397669601

message = b&quot;The challenge&apos;s flag is: W1{\x00\x00\x00\x00\x00&quot;
message = bytes_to_long(message)

P = PolynomialRing(Zmod(n1), &apos;x&apos;)   
x = P.gen()
f = (message + x) ** 11 - c1

FLAG = f.small_roots(X=2**50, beta=0.5, epsilon=0.015)
print(b&quot;The challenge&apos;s flag is: W1{&quot; + long_to_bytes(int(FLAG[0])))
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;The challenge&apos;s flag is: W1{A_p0r
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Đến với part cuối cùng, bạn có thể tham khảo Modular Binomials&lt;/li&gt;
&lt;li&gt;Giờ bạn chỉ cần bruteforce hết tất cả các hint2 phù hợp cho đến khi nào xuất hiện $p$ là được&lt;/li&gt;
&lt;li&gt;Part 3:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;import math
from Crypto.Util.number import *
from Crypto.Util.Padding import pad
from gmpy2 import next_prime

power1 = 216515682056074917890546762360422775759
power2 = 181261651362416112447857910756974715253
n3 = 21359428772367567762465177652844235016399133661109374171468438854776561246101192389880378563002849491963133317822943728663659947135787662718455826488579139110050277672340965370240377096647337916266432987314270685198434379314123632571454622139263629066529934934145277818436942937601606973322506307539554768214679444922661752071123001107723044536675764939605450418550872933311130411355136155266638811137191949645399770280529254355150837851996371018266855150538183941848828564579160123399867316204489663532672172085306182442910667707542525506401104797512848908939121437800995819119455775357620426817733038470495147276793
hint1 = 21165771737478725950851391268640839742282738803460982495702522148737396455865260539601340347151173375617657055655041043517629027492240234235183580296831387302596600695810822254431016035932293471628759197250919622480691606652634091857970182748360357623125515085919940174799049558949629746381693045991973156293505919951290743073241929003963830080590083449251389368841868741731209817737242746289090221167945649864511277017186337822956848075038306043349755593388069666507069280026419826266727104285440789673438901905195589837692674191838634684368474851772930333812341283933491515890683850912453399208298386277684434445251
e3 = 37168304783168980393920173579379710993074140739837792931728618759911393587739974232300850198277091657299514225039110963445571485237776790392542202542871178828780578794970964528554601212794008256754381717435669983473010005148311575428821001052237868999060224515275873668669213982474419104490433570572105845650251833635244326980880559203371594232931397547585750248068741794286062945536039069446641110263273308725041343806223241749757368437839639116744539107461153105289738651458945715419945820612645097201055521095139028646379860663948813756288318543675883368961217287301785966051639519026541020372279840744062219039191
c3 = 1096068813757106861072993580055082418953963625562486119593706360815623910450146273178847639647973866883042811190136050041007753762866893600939527521717867763640548952449681014994092425911219237532793217675033536632826439864589482883751579581380877418411747043359264793736856167378058319757084322522347500331245555941134923517945671002893760566068668447306048296218747434127148067031352038287679665515172443331784546991573906825482519792803285599056922851777942853546318660474033734361350200902964188996923538488714615644961271863386265655804388494579456404108898159557142645993950324859997370434010675900211652602515
hint2 = 16002533045690254443068782310738871250791401936376810436026096611173997131874713692699509851125918281681857169384069919927942457745536556157358622246039791526183978099160142274123585176861714785125622520184750360992318398495677483570850818303877511375934709429355933493870164423524789358108740524580132689356745913683953583907638630199407764152341314098334360879226873052554853127798796323157550889095327658860530066789036903926800520362801333073394783514073083438782669371432525889153218716327204307527616619189943438808687186472110179071919843691902953035148876003368294450160955668114087621163981454466377784586475

while True:
    p = math.gcd((pow(3, (-power1 * power2), n3) * pow(hint2, power1, n3) - pow(5, (-power2 * power1), n3) * pow(hint1, power2, n3)) * (n3 - 6) % n3, n3)
    if p != 1:
        break
    
    hint2 += 1

p3 = p
p3 = 149980597760000270288020712564730421423970532153531109538283372659249090766781034501997790624701674901461522882220113357725378245572684453212159175961586586191733952945436353476578468558720453741235833653421187406171537615120165747876478240633282821626182454380149314563971827086993047558004886558019962996419
q3 = n3 // p3
assert p3 * q3 == n3

phi = (p3 - 1) * (q3 - 1)
d = pow(e3, -1, phi)
m = pow(c3, d, n3)
print(long_to_bytes(m))
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;a91411ae238_happy_hacking!!!!!!!}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Kết hợp cả 3 part lại ta có được&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;W1{A_p0r_lA_15_h1hi__75f26f70b3fa1343153ea91411ae238_happy_hacking!!!!!!!}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;435&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;server.py&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;from Crypto.Cipher import AES 
from Crypto.Util import Counter
from Crypto.Util.number import long_to_bytes as ltb, bytes_to_long as btl
from Crypto.Util.Padding import pad, unpad
import os



GREEN = &apos;\033[92m&apos;
RED = &apos;\033[91m&apos;
YELLOW = &apos;\033[33m&apos;
NORM = &apos;\033[0m&apos;

BANNER = \
    f&quot;&quot;&quot;
    {YELLOW} 
 ___       __   ________  ________   ________   ________  ___       __     _____  ________      
|\  \     |\  \|\   __  \|\   ___  \|\   ___  \|\   __  \|\  \     |\  \  / __  \|\   ___  \    
\ \  \    \ \  \ \  \|\  \ \  \\\ \  \ \  \\\ \  \ \  \|\  \ \  \    \ \  \|\/_|\  \ \  \\\ \  \   
 \ \  \  __\ \  \ \   __  \ \  \\\ \  \ \  \\\ \  \ \   __  \ \  \  __\ \  \|/ \ \  \ \  \\\ \  \  
  \ \  \|\__\_\  \ \  \ \  \ \  \\\ \  \ \  \\\ \  \ \  \ \  \ \  \|\__\_\  \   \ \  \ \  \\\ \  \ 
   \ \____________\ \__\ \__\ \__\\\ \__\ \__\\\ \__\ \__\ \__\ \____________\   \ \__\ \__\\\ \__\\
    \|____________|\|__|\|__|\|__| \|__|\|__| \|__|\|__|\|__|\|____________|    \|__|\|__| \|__|

    {NORM}
Once upon a time, deep in the forest, there lies a great treasure but protected with a digital padlock. Rumor said that whoever could open the treasure would be rewarded with great riches. You, as a treasure hunter, decided to go on a trip to uncover this elusive prize. After facing countless obstacles and challenges, you finally reached the location of the treasure. Next to the treasure lies a truncated poem. You, as a master in cryptography also, will not give up easily right? Goodluck! ;)
    &quot;&quot;&quot;



menu = \
f&quot;&quot;&quot;\
{NORM}
1. ECB_encrypt
2. CBC_decrypt
3. Crack Password
{&quot;~&quot;*100}
&quot;&quot;&quot;



class Riddle:
    def __init__(self, key, nonce):
        self.key = key
        self.nonce = nonce

    def GCM_encrypt(self, plaintext):
        &quot;&quot;&quot;
        Return encryption of AES-GCM with provided plaintext
        &quot;&quot;&quot;
        cipher_ = AES.new(self.key, AES.MODE_GCM, self.nonce)
        return cipher_.encrypt_and_digest(plaintext)

    def ECB_encrypt(self, plaintext):
        &quot;&quot;&quot;
        Return encryption of AES-ECB with provided plaintext
        &quot;&quot;&quot;
        cipher_ = AES.new(self.key, AES.MODE_ECB)
        return cipher_.encrypt(plaintext)

    def CBC_decrypt(self, iv, ciphertext):
        &quot;&quot;&quot;
        Return decryption of AES-CBC with provided (ciphertext, iv)
        &quot;&quot;&quot;
        cipher_ = AES.new(self.key, AES.MODE_CBC, iv)
        return cipher_.decrypt(ciphertext)



print(BANNER)

cipher = Riddle(key = os.urandom(16), nonce = os.urandom(12))


secret = os.urandom(256)
poem = \
pad(b&quot;&quot;&quot;\
The security code is simple, an intricate dance of numbers.
A shield against intruders, a fortress for slumbers.
Digits align in harmony, a secret melody they sing,
Guarding the treasures of a realm, where secrets take their wing : \
&quot;&quot;&quot;, 16) + secret

print(secret.hex())


ciphertext, tag = cipher.GCM_encrypt(poem)

print(f&quot;ct = &apos;{ciphertext.hex()}&apos;&quot;)
print(f&quot;tag = &apos;{tag.hex()}&apos;&quot;)


while True:
    print(menu)
    option = int(input(&quot;Your choice &amp;gt; &quot;))
    try:
        match option:
            case 1:
                m = bytes.fromhex(input(&quot;Please give me your message (in hex) &amp;gt; &quot;))
                c = cipher.ECB_encrypt(m)
                print(c.hex())
            case 2:
                iv = bytes.fromhex(input(&quot;Please give me your iv (in hex) &amp;gt; &quot;))
                c = bytes.fromhex(input(&quot;Please give me your ciphertext (in hex) &amp;gt; &quot;))
                m = cipher.CBC_decrypt(iv, c)
                print(m.hex())
            case 3:
                key = bytes.fromhex(input(&quot;Give me your secret (in hex) &amp;gt; &quot;))
                if key == secret:
                    FLAG = open(&apos;flag.txt&apos;, &apos;r&apos;).read()
                    print(f&quot;{GREEN}Wow you did surprise me!! Here is your reward {FLAG}&quot;)
                    exit()
                else:
                    print(&quot;Ahh, try better next time :(&quot;)
                    exit()
            case _:
                print(&quot;No idea what you choose :(&quot;)
    except Exception as e: 
        print(f&quot;{RED}An exception occurred: {str(e)}&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Solution&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Bài này cho ta &lt;strong&gt;ciphertext&lt;/strong&gt; và &lt;strong&gt;tag&lt;/strong&gt; đã được encrypt qua AES GCM cũng với các chức năng &lt;strong&gt;ECB_encrypt&lt;/strong&gt; và &lt;strong&gt;CBC_decrypt&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Ta biết phần encrypt của GCM hoặc động giống như Counter nên ta có thể đưa phần xor(ciphertext, plaintext) qua &lt;strong&gt;CBC_decrypt&lt;/strong&gt; với IV = 0 để tìm lại được Counter&lt;/li&gt;
&lt;li&gt;Giờ ta chỉ cần cộng dần Counter lên rồi cho đi qua &lt;strong&gt;ECB_encrypt&lt;/strong&gt; rồi lấy kết quả đó xor với ciphertext sẽ được xâu gốc&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;from Crypto.Cipher import AES 
from Crypto.Util import Counter
from Crypto.Util.number import *
from Crypto.Util.Padding import pad, unpad
import os
from pwn import *

# io = process([&apos;python3&apos;, &apos;server.py&apos;])
io = remote(&quot;45.122.249.68&quot;, 20024)

context.log_level = &apos;debug&apos;

secret = os.urandom(256)
poem = \
pad(b&quot;&quot;&quot;\
The security code is simple, an intricate dance of numbers.
A shield against intruders, a fortress for slumbers.
Digits align in harmony, a secret melody they sing,
Guarding the treasures of a realm, where secrets take their wing : \
&quot;&quot;&quot;, 16) + secret

io.recvuntil(b&apos;ct = &apos;)
ct = io.recvline().strip().decode()[1:-1]
ct = bytes.fromhex(ct)


test = xor(ct, poem)

io.sendafter(b&apos;&amp;gt; &apos;, b&apos;2\n&apos;)
io.sendafter(b&apos;&amp;gt; &apos;, b&quot;00&quot; * 16 + b&quot;\n&quot;)
io.sendafter(b&apos;&amp;gt; &apos;, test.hex().encode() + b&apos;\n&apos;)

res = io.recvline().strip().decode()

res = res[:32]
counter = bytes_to_long(bytes.fromhex(res))

payload = b&quot;&quot;
while len(payload) != len(ct):
    payload += long_to_bytes(counter)
    counter += 1

io.sendafter(b&apos;&amp;gt; &apos;, b&apos;1\n&apos;)
io.sendafter(b&apos;&amp;gt; &apos;, payload.hex().encode() + b&apos;\n&apos;)

res = io.recvline().strip().decode()
res = bytes.fromhex(res)

payload = xor(res, ct)[240:]
io.sendafter(b&apos;&amp;gt; &apos;, b&apos;3\n&apos;)
io.sendafter(b&apos;&amp;gt; &apos;, payload.hex().encode() + b&apos;\n&apos;)

io.interactive()
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;Wow you did surprise me!! Here is your reward W1{7hE_S3cRe7_L13s_4m0N9_M0De5_0f_0p3r4ti0n_cddde2b1d3eec22ffcd8c871faca7639}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;AESSS&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;server.py&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;import os
import random
from Crypto.Cipher import AES
from Crypto.Util import Counter
from Crypto.Hash import SHA256
from secret import FLAG

flag1 = FLAG[:len(FLAG)//3].encode()
flag2 = FLAG[len(FLAG)//3:2*len(FLAG)//3].encode()
flag3 = FLAG[2*len(FLAG)//3:].encode()

assert len(flag1) == len(flag2) == len(flag3) == 48

def pad(data, block_size):
    return data + (block_size - len(data) % block_size) * bytes([block_size - len(data) % block_size])


def challenge_part1(flag):
    key = os.urandom(16)
    iv = os.urandom(16)
    options = [&apos;ecb&apos;, &apos;cbc&apos;, &apos;cfb&apos;, &apos;ofb&apos;, &apos;ctr&apos;]
    suboptions = [&apos;data&apos;, &apos;flag&apos;]

    for _ in range(3):  # you only have 3 tries, stingy huh?
        [option, suboption, *more] = input(&apos;&amp;gt; &apos;).split(&apos; &apos;)
        if option not in options:
            print(&apos;invalid option!&apos;)
            continue 
        if suboption not in suboptions:
            print(&apos;invalid suboption!&apos;)
            continue
        options.remove(option)

        if suboption == &apos;data&apos;:
            message = bytes.fromhex(more[0])
        else:
            message = flag

        if option == &apos;ecb&apos;:
            cipher = AES.new(key, AES.MODE_ECB)
        elif option == &apos;cbc&apos;:
            cipher = AES.new(key, AES.MODE_CBC, iv)
        elif option == &apos;cfb&apos;:
            cipher = AES.new(key, AES.MODE_CFB, iv, segment_size=128)
        elif option == &apos;ofb&apos;:
            cipher = AES.new(key, AES.MODE_OFB, iv)
        elif option == &apos;ctr&apos;:
            cipher = AES.new(key, AES.MODE_CTR,
                             counter=Counter.new(16, prefix=iv[:14]))

        ciphertext = cipher.encrypt(message)
        print(ciphertext.hex())
    else:
        print(&apos;No more options!&apos;)


def challenge_part2(flag):
    key = os.urandom(16)

    while True:
        try:
            options = input(&apos;&amp;gt; &apos;).split(&apos; &apos;)
            if (options[0] == &quot;encrypt_flag&quot;):
                iv = os.urandom(16)
                cipher = AES.new(key, AES.MODE_GCM, iv)
                message = flag
                ciphertext= cipher.encrypt(message)
                print(&quot;Encrypted flag and tag, iv:&quot;, ciphertext.hex(), iv.hex())

            elif (options[0] == &quot;encrypt_data&quot;):
                iv = bytes.fromhex(options[1])
                cipher = AES.new(key, AES.MODE_GCM, iv)
                message = bytes.fromhex(input(&apos;input data:&apos;))
                ciphertext= cipher.encrypt(message)
                print(&quot;Encrypted message and tag, iv:&quot;, ciphertext.hex(), iv.hex())
            else:
                print(&quot;Invalid option!&quot;)
        except Exception as e:
            print(&quot;An error occurred: &quot;, e)
            break


def challenge_part3(flag):
    try:
        salt = os.urandom(2) # prevent rainbow table attack and ... brute force attack?
        password_file_path = os.path.join(os.path.dirname(__file__), &quot;rockyou.txt&quot;) # rockyou.txt is a list of common passwords
        print(f&quot;Looking for rockyou.txt at: {password_file_path}&quot;)

        if not os.path.isfile(password_file_path):
            print(f&quot;Error: {password_file_path} not found&quot;)
            return

        with open(password_file_path, &quot;rb&quot;) as file:
            passwords = file.readlines()[:100]

        password = random.choice(passwords).strip()
        key = SHA256.new(password + salt.hex().encode()).digest()[:16]
        cipher = AES.new(key, AES.MODE_ECB)
        ciphertext = cipher.encrypt(pad(password, 16))
        print(&quot;Encrypted password:&quot;, ciphertext.hex())

        input_password = input(&apos;input password:&apos;)
        if input_password.encode() == password:
            print(&quot;Correct password!&quot;)
            print(&quot;Good job! Here is your flag:&quot;, flag)
        else:
            print(&quot;Incorrect password!&quot;)

    except FileNotFoundError:
        print(&quot;rockyou.txt not found&quot;)
        return
    except Exception as e:
        print(f&quot;An error occurred: {e}&quot;)
        return


def main():
    chall_option = input(&apos;challenge option:&apos;)
    if chall_option == &apos;1&apos;:
        challenge_part1(flag1)
    elif chall_option == &apos;2&apos;:
        challenge_part2(flag2)
    elif chall_option == &apos;3&apos;:
        challenge_part3(flag3)
    else:
        print(&apos;Invalid option!&apos;)


if __name__ == &apos;__main__&apos;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Solution&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Part 1:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;import os
import random
from Crypto.Cipher import AES
from Crypto.Util import Counter
from Crypto.Util.Padding import pad
from Crypto.Hash import SHA256
from pwn import *


io = remote(&quot;45.122.249.68&quot;, 20026)

context.log_level = &quot;DEBUG&quot;

io.sendlineafter(b&quot;option:&quot;, b&apos;1&apos;)
io.sendline(b&quot;cfb data &quot; + b&quot;00&quot; * 48)
enc1 = io.recvline().strip()[2:].decode()
print(enc1)
io.sendline(b&quot;ofb flag&quot;)
enc2 = io.recvline().strip()[2:].decode()
print(enc2)
enc1 = bytes.fromhex(enc1)
enc2 = bytes.fromhex(enc2)
print(xor(enc1, enc2))
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;W1{AES_1s_w1d3ly_us3d_1n_r3al_w0rld_applic4t10ns
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Part 2:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;import os
import random
from Crypto.Cipher import AES
from Crypto.Util import Counter
from Crypto.Util.Padding import pad
from Crypto.Hash import SHA256
from pwn import *

io = remote(&quot;45.122.249.68&quot;, 20026)

context.log_level = &quot;DEBUG&quot;

io.sendlineafter(b&quot;option:&quot;, b&apos;2&apos;)
io.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;encrypt_flag&quot;)
io.recvuntil(b&quot;iv: &quot;)
enc1, iv = io.recvline().strip().decode().split(&quot; &quot;)
enc1 = bytes.fromhex(enc1)

io.sendlineafter(b&quot;&amp;gt; &quot;, b&quot;encrypt_data&quot; + b&quot; &quot; + iv.encode())
io.sendlineafter(b&quot;input data:&quot;, b&quot;00&quot; * 48)
io.recvuntil(b&quot;iv: &quot;)
enc2, iv = io.recvline().strip().decode().split(&quot; &quot;)

enc2 = bytes.fromhex(enc2)
print(xor(enc1, enc2))
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;_4nd_1s_c0ns1d3r3d_t0_b3_0n3_0f_th3_m0st_s3cur3_
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Part 3:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;import os
import random
from Crypto.Cipher import AES
from Crypto.Util import Counter
from Crypto.Util.Padding import pad
from Crypto.Hash import SHA256
from pwn import *

io = remote(&quot;45.122.249.68&quot;, 20026)

context.log_level = &quot;DEBUG&quot;

io.sendlineafter(b&quot;option:&quot;, b&apos;3&apos;)
io.recvuntil(b&quot;password: &quot;)
enc = io.recvline().strip().decode()
print(enc)
with open(&quot;rockyou.txt&quot;, &quot;rb&quot;) as file:
    passwords = file.readlines()[:100]

for password in passwords:
    password = password.strip()
    print(password)
    for bf1 in range(256):
        for bf2 in range(256):
            salt = bytes([bf1]) + bytes([bf2])
            key = SHA256.new(password + salt.hex().encode()).digest()[:16]
            cipher = AES.new(key, AES.MODE_ECB)
            ciphertext = cipher.encrypt(pad(password, 16))
            if ciphertext.hex() in enc or enc in ciphertext.hex():
                print(&quot;True&quot;)
                io.sendlineafter(b&quot;input password:&quot;, password)
                io.interactive()
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;alg0r1thms_1n_pr4ct1c3_66e14a195cd4b2e9d5059ca1a
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Ghép cả 3 lại:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;W1{AES_1s_w1d3ly_us3d_1n_r3al_w0rld_applic4t10ns_4nd_1s_c0ns1d3r3d_t0_b3_0n3_0f_th3_m0st_s3cur3_alg0r1thms_1n_pr4ct1c3_66e14a195cd4b2e9d5059ca1a
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Master of gacha&lt;/h1&gt;
&lt;h2&gt;Solution&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *
from sage.all import *
import random
import json
from Crypto.Util.number import *
from hashlib import sha256 

class Twister:
    N = 624
    M = 397
    A = 0x9908b0df

    def __init__(self):
        self.state = [[(1 &amp;lt;&amp;lt; (32 * i + (31 - j))) for j in range(32)] for i in range(624)]
        self.index = 0

    @staticmethod
    def _xor(a, b):
        return [x ^ y for x, y in zip(a, b)]

    @staticmethod
    def _and(a, x):
        return [v if (x &amp;gt;&amp;gt; (31 - i)) &amp;amp; 1 else 0 for i, v in enumerate(a)]

    @staticmethod
    def _shiftr(a, x):
        return [0] * x + a[:-x]

    @staticmethod
    def _shiftl(a, x):
        return a[x:] + [0] * x

    def get32bits(self):
        if self.index &amp;gt;= self.N:
            for kk in range(self.N):
                y = self.state[kk][:1] + self.state[(kk + 1) % self.N][1:]
                z = [y[-1] if (self.A &amp;gt;&amp;gt; (31 - i)) &amp;amp; 1 else 0 for i in range(32)]
                self.state[kk] = self._xor(self.state[(kk + self.M) % self.N], self._shiftr(y, 1))
                self.state[kk] = self._xor(self.state[kk], z)
            self.index = 0

        y = self.state[self.index]
        y = self._xor(y, self._shiftr(y, 11))
        y = self._xor(y, self._and(self._shiftl(y, 7), 0x9d2c5680))
        y = self._xor(y, self._and(self._shiftl(y, 15), 0xefc60000))
        y = self._xor(y, self._shiftr(y, 18))
        self.index += 1

        return y

    def getrandbits(self, bit):
        return self.get32bits()[:bit]


class Solver:
    def __init__(self):
        self.equations = []
        self.outputs = []

    def insert(self, equation, output):
        for eq, o in zip(self.equations, self.outputs):
            lsb = eq &amp;amp; -eq
            if equation &amp;amp; lsb:
                equation ^= eq
                output ^= o

        if equation == 0:
            return

        lsb = equation &amp;amp; -equation
        for i in range(len(self.equations)):
            if self.equations[i] &amp;amp; lsb:
                self.equations[i] ^= equation
                self.outputs[i] ^= output

        self.equations.append(equation)
        self.outputs.append(output)

    def solve(self):
        num = 0
        for i, eq in enumerate(self.equations):
            if self.outputs[i]:
                # Assume every free variable is 0
                num |= eq &amp;amp; -eq

        state = [(num &amp;gt;&amp;gt; (32 * i)) &amp;amp; 0xFFFFFFFF for i in range(624)]
        return state

def get_random_bytes(num):
    x = os.urandom(num)
    y = random.randbytes(num)
    # print(x.hex(), y.hex())
    return x + y


def inv(n, q):
    return egcd(n, q)[0] % q


def egcd(a, b):
    s0, s1, t0, t1 = 1, 0, 0, 1
    while b &amp;gt; 0:
        q, r = divmod(a, b)
        a, b = b, r
        s0, s1, t0, t1 = s1, s0 - q * s1, t1, t0 - q * t1
        pass
    return s0, t0, a


def sqrt(n, q):
    assert n &amp;lt; q
    for i in range(1, q):
        if i * i % q == n:
            return (i, q - i)
        pass
    raise Exception(&quot;not found&quot;)

########## ECC Implement ##########

Coord = collections.namedtuple(&quot;Coord&quot;, [&quot;x&quot;, &quot;y&quot;])


class EC(object):
    &quot;&quot;&quot;System of Elliptic Curve&quot;&quot;&quot;
    def __init__(self, a, b, q, n):
        &quot;&quot;&quot;elliptic curve as: (y**2 = x**3 + a * x + b) mod q
        - a, b: params of curve formula
        - q: prime number
        &quot;&quot;&quot;
        assert 0 &amp;lt; a and a &amp;lt; q and 0 &amp;lt; b and b &amp;lt; q and q &amp;gt; 2
        assert (4 * (a ** 3) + 27 * (b ** 2))  % q != 0
        self.a = a
        self.b = b
        self.q = q
        self.zero = Coord(0, 0)
        self.order = n
        pass

    def is_valid(self, p):
        if p == self.zero: return True
        l = (p.y ** 2) % self.q
        r = ((p.x ** 3) + self.a * p.x + self.b) % self.q
        return l == r

    def at(self, x):
        assert x &amp;lt; self.q
        ysq = (x ** 3 + self.a * x + self.b) % self.q
        y, my = sqrt(ysq, self.q)
        return Coord(x, y), Coord(x, my)

    def neg(self, p):
        return Coord(p.x, -p.y % self.q)

    def add(self, p1, p2):
        if p1 == self.zero: return p2
        if p2 == self.zero: return p1
        if p1.x == p2.x and (p1.y != p2.y or p1.y == 0):
            return self.zero
        if p1.x == p2.x:
            l = (3 * p1.x * p1.x + self.a) * inv(2 * p1.y, self.q) % self.q
            pass
        else:
            l = (p2.y - p1.y) * inv(p2.x - p1.x, self.q) % self.q
            pass
        x = (l * l - p1.x - p2.x) % self.q
        y = (l * (p1.x - x) - p1.y) % self.q
        return Coord(x, y)

    def mul(self, p, n):
        r = self.zero
        m2 = p
        while 0 &amp;lt; n:
            if n &amp;amp; 1 == 1:
                r = self.add(r, m2)
                pass
            n, m2 = n &amp;gt;&amp;gt; 1, self.add(m2, m2)
            pass
        return r

    # def order(self, g):
    #     assert self.is_valid(g) and g != self.zero
    #     for i in range(1, self.q + 1):
    #         if self.mul(g, i) == self.zero:
    #             return i
    #         pass
    #     raise Exception(&quot;Invalid order&quot;)
    # pass

class DSA(object):
    def __init__(self, ec : EC, g : Coord):
        self.ec = ec
        self.g = g
        self.n = ec.order
        pass

    def gen(self, priv):
        assert 0 &amp;lt; priv and priv &amp;lt; self.n
        return self.ec.mul(self.g, priv)

    def sign(self, hashval, priv, r):
        assert 0 &amp;lt; r and r &amp;lt; self.n
        m = self.ec.mul(self.g, r)
        return (m.x, inv(r, self.n) * (hashval + m.x * priv) % self.n)
 
    def validate(self, hashval, sig, pub):
        assert self.ec.is_valid(pub)
        assert self.ec.mul(pub, self.n) == self.ec.zero
        w = inv(sig[1], self.n)
        u1, u2 = hashval * w % self.n, sig[0] * w % self.n
        p = self.ec.add(self.ec.mul(self.g, u1), self.ec.mul(pub, u2))
        return p.x % self.n == sig[0]
    pass

##### redo this after done code
P = 0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff
A = 0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc
B = 0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b
curve_order = 0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551
G = Coord(0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296, \
          0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5)
ec_object = EC(A, B, P, curve_order)
dsa_object = DSA(ec_object, G)

Ln = 256

num = 1500
bit = 24

twister = Twister()
output = []


# io = process([&apos;python3&apos;, &apos;server.py&apos;])
io = remote(&quot;45.122.249.68&quot;, 20025)
# Register
io.sendlineafter(b&quot;&amp;gt;&amp;gt; &quot;, b&quot;2&quot;)
io.sendlineafter(b&quot;username: &quot;, b&quot;admin&quot;)
io.recvuntil(b&quot;Account information:\n&quot;)
user = []
cookies = []
user.append(io.recvline().strip())
io.recvuntil(b&quot;Cookie: &quot;)
cookies.append(io.recvline().strip())

# login
io.sendlineafter(b&quot;&amp;gt;&amp;gt; &quot;, b&quot;1&quot;)
io.sendlineafter(b&quot;information: &quot;, user[0])
io.sendlineafter(b&quot;cookie: &quot;, cookies[0])

# Gacha
outputs = []
for i in range(num):
    print(i)
    io.sendlineafter(b&quot;&amp;gt;&amp;gt; &quot;, b&quot;4&quot;)
    io.sendlineafter(b&quot;number: &quot;, b&quot;1&quot;)
    io.recvuntil(b&quot;Lucky number is: &quot;)
    res = io.recvline().strip()
    res = int(res)
    res = res &amp;amp; 0xffffff
    res = res.to_bytes(3, &quot;little&quot;)
    outputs.append(int.from_bytes(res, &quot;big&quot;))

# io.interactive()
# outputs = [int.from_bytes(random.randbytes(3)[::-1], &quot;big&quot;) for _ in range(num)]
equations = [twister.getrandbits(bit) for _ in range(num)]

solver = Solver()
for i in range(num):
    print(i)
    io.sendlineafter(b&quot;&amp;gt;&amp;gt;&quot;, b&quot;3&quot;)
    for j in range(bit):
        solver.insert(equations[i][j], (outputs[i] &amp;gt;&amp;gt; (bit - 1 - j)) &amp;amp; 1)

state = solver.solve()
recovered_state = (3, tuple(state + [0]), None)
random.setstate(recovered_state)

for i in range(num):
    assert outputs[i] == random.getrandbits(bit)

n = 50
context.log_level = &apos;debug&apos;

for i in range(n):
    # register
    io.sendlineafter(b&quot;&amp;gt;&amp;gt; &quot;, b&quot;2&quot;)
    io.sendlineafter(b&quot;username: &quot;, b&quot;admin&quot;)
    user_id = get_random_bytes(8)
    io.recvuntil(b&quot;Account information:\n&quot;)
    user.append(io.recvline().strip())
    io.recvuntil(b&quot;Cookie: &quot;)
    cookies.append((io.recvline().strip(), random.randbytes((curve_order).bit_length() // 16).hex()))


B = 2 ** 128

M = Matrix(QQ, n + 3, n + 3)
M[0, 0] = B
M[1, 1] = B
M[2, 2] = B / curve_order

for i in range(n):
    hashval = bytes_to_long(sha256(user[i + 1]).digest()[:Ln])
    r, s = cookies[i + 1][0].decode().split(&quot;.&quot;)
    k = int(cookies[i + 1][1], 16)
    r = int(r, 16)
    s = int(s, 16)
    M[0, i + 3] = inverse(s, curve_order) * inverse(2 ** 128, curve_order) * hashval % curve_order
    M[1, i + 3] = inverse(2 ** 128, curve_order) * (-k % curve_order) % curve_order
    M[2, i + 3] = inverse(s, curve_order) * inverse(2 ** 128, curve_order) * r % curve_order
    M[i + 3, i + 3] = curve_order

def register_account(username : str, isAdmin : bool, privkey: int):
    tmp = {&quot;username&quot; : username, &quot;isAdmin&quot; : isAdmin}

    user_id = get_random_bytes(8)
    tmp[&quot;userID&quot;] = user_id.hex()


    while True:
        random_k = int.from_bytes(get_random_bytes((curve_order).bit_length() // 16), &quot;big&quot;)
        if random_k &amp;lt; curve_order:
            break
    
    tmp_hash = bytes_to_long(sha256(json.dumps(tmp).encode()).digest()[:Ln])
    r, s = dsa_object.sign(tmp_hash, privkey, random_k)

    cookie = long_to_bytes(r).hex() + &quot;.&quot; + long_to_bytes(s).hex()
    return tmp, cookie

M = M.LLL()
for i in M:
    if i[0] == B and i[1] == B:
        print(i[-1] * 2 ** 128 % curve_order + int(cookies[-1][1], 16))
        k = i[-1] * 2 ** 128 % curve_order + int(cookies[-1][1], 16)
        hashval = bytes_to_long(sha256(user[-1]).digest()[:Ln])
        r, s = cookies[-1][0].decode().split(&quot;.&quot;)
        r = int(r, 16)
        s = int(s, 16)
        d = (k * s - hashval) * inverse(r, curve_order) % curve_order
        print(d)
        payload1, payload2 = register_account(&quot;admin&quot;, True, d)
        print(payload1)
        io.sendlineafter(b&quot;&amp;gt;&amp;gt; &quot;, b&quot;1&quot;)
        io.sendlineafter(b&quot;information: &quot;, json.dumps(payload1).encode())
        io.sendlineafter(b&quot;cookie: &quot;, payload2)
        io.sendlineafter(b&quot;&amp;gt;&amp;gt; &quot;, b&quot;3&quot;)
        io.interactive()
        
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;W1{it_1s_n3v3r_saf3_t0_us3_n0n_secure_cryptogr4phic_r4nd0mizat!0n}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item></channel></rss>