[20210828 WMCTF] WP in Crypto direction

Keywords: cryptology

woc, it's too difficult. It's just a fairy fighting. I don't have any confidence to continue when I see the number of people

All a, master's blog , then the environment was turned off at the moment of sending WP

Four questions, one in the backpack, one in the AGCD of RSA, one in AES.OCB, and the last unclear test point

WM

Crypto-checkin(recuring)

As we all know, L1near is a famous hacker. He wrote a fully automatic water swarm robot for W & M. we stole the simple version of L1near during development and obtained the interactive interface. Can you help us find the flag hidden by L1near?

Link

The next day, update

Please submit the smallest of the Legal Secrets. The title is not clear, which has caused trouble to all masters

<title>W&M exclusive robot</title>

<style>
    h1 {text-align:center}
    p {text-align:center}
    form {text-align:center}
</style>

<head>
    <body>
        <h1>Fully automatic rp system</h1>
        <p>Come and get your rp value today!</p>
        <hr></hr>
        <form action="show.php" method="post">
        <input type="hidden" id="rp" name="rp" value="rp">
        <input type="submit" value="Get today's rp!">
        </form>
    </body>
</head>

<script>
    function ran(){
        var rp = Math.floor( Math.random() * 100);
        document.getElementById("rp").setAttribute('value',rp);
    }
    ran();
</script>

<!--
-------------------------------------------------------
Maybe there are some Easter eggs?
So where are them?
-->

rp is an integer from 0 to 99, but post100 will get

<title>W&M exclusive robot</title>

<style>
    h1 {text-align:center}
    p {text-align:center}
</style>

<h1>Fully automatic rp system</h1>
<p>Come and get your rp value today!</p>
<hr></hr>

<p>Your rp value:100</p>
<p>Wow! Golden legend!<!-- so why not try to post 'flag' as rp? --></p>

I didn't analyze anything. I'll sort it out a little

  • 0~19

    Ah-ha! There is a idiot!

  • 20~39

    Gee, this is too miserable.

  • 40~59

    Oh, you almost passed it!

  • 60~79

    Fortunately, you passed 60.

  • 80~99

    You are Koi! Congratulations!

  • 100

    Wow! Golden legend!<!-- so why not try to post 'flag' as rp? -->

  • flag

    Your rp value:1620418829165478
    
    What happend to my bot?????
    
    Let me find something in my backpack which can fix this bug!
    

Write a post script for the first time and hold a memorial ceremony

from Crypto.Util.number import *
import requests


def exDigit(String):
    d = 0
    for j in String:
        if '0' <= j <= '9':
            d = d * 10 + int(j)
    return d

x = 999999999999999999999999999999999
url = "http://47.104.243.99:10000/show.php"
r = requests.post(url, {"rp": 2017515922459700})
for i in range(65537):
    if 'flag' in r.text or 'WMCTF' in r.text or 'wmctf' in r.text or '1620418829165478' in r.text:
        print(r.text)
        print(x)
    line = r.text[205:][:37]
    t = exDigit(line)
    if t < x:
        x = t
    print(t)
    payload = {
        "rp": t
    }
    r = requests.post(url, payload)

However, after reading WP, I found that the information collected was useless. I directly exploded in situ

It's a backpack, This backpack on CTF Wiki is very easy to understand , master the following concepts: Super increment, Merkle Hellman knapsack encryption, and 01 lattice cracking

But I don't understand the series after reading WP. First, I want to know how to see that the knapsack is encrypted behind the machine, and then why post 2^i+a can get the value of each item of the knapsack. What are I and a respectively

The current understanding is that 1620418829165478 obtained by post flag is the ciphertext, and then the private key is cracked and decrypted

But there are still many doubts and incomprehensions. It's too difficult. Follow up

Here we are. This is the third reappearance in recent days. After reading master Striving's blog, I finally understand it more or less. Well, the chicken is my own

When I saw the backpack I thought of, I always thought it meant backup

A brief introduction to knapsack encryption is learned from la guy again

First of all, the knapsack problem is the knapsack problem we are familiar with. The following passage may be able to regain our memory

  • Private key generation

    Select a super increment set { s 1 , s 2 , ⋯   , s n } \{s_1, s_2, \cdots, s_n\} {s1​,s2​,⋯,sn​}

    The so-called super increasing set is to satisfy that the i-th number is greater than the sum of all previous numbers

  • Public key generation

    1. Select modulus m to ensure n > ∑ i = 1 n s i n>\sum\limits_{i=1}^n s_i n>i=1∑n​si​

    2. Select the multiplier w to ensure ( w ,   m ) = 1 (w,\ m)=1 (w,   m)=1, where W is also the private key

    3. Generate public key set t i t_i ti​, t i ≡ w s i   ( m o d   m ) t_i\equiv ws_i\ (mod\ m) ti​≡wsi​ (mod m)

  • encryption

    The binary of each bit of plaintext b is b i b_i bi​
    c = ∑ i = 1 n t i b i   ( m o d   m ) c=\sum\limits_{i=1}^nt_ib_i\ (mod\ m) c=i=1∑n​ti​bi​ (mod m)

  • decrypt

    Ask first w − 1 w^{-1} w − 1, then
    b = ∑ i = 1 n w − 1 t i b i   ( m o d   m ) = ∑ i = 1 n s i b i   ( m o d   m ) b=\sum\limits_{i=1}^nw^{-1}t_ib_i\ (mod\ m)=\sum\limits_{i=1}^ns_ib_i\ (mod\ m) b=i=1∑n​w−1ti​bi​ (mod m)=i=1∑n​si​bi​ (mod m)

The cryptosystem has a density of d = n l o g 2 ( m a x { t i } ) d=\frac{n}{log_2(max\{t_i\})} d=log2​(max{ti​})n​

Then it's what I can't understand. Master Striving said that master Chunge said every post 2 i 2^i 2i, you will find that the period is 32, and the obtained value is the value in the public key set. Borrowing data is equivalent to knowing the public key set and ciphertext. Plaintext is required

t = [97005071980911, 32652300906411, 73356817713575, 108707065719744, 103728503304990, 49534310783118, 53330718889073, 2121345207564, 46184783396167, 115771983454147, 64261597617025, 2311575715655, 56368973049223, 84737125416797, 24316288533033, 82963866264519, 101019837363048, 25996629336722, 41785472478854, 68598110798404, 40392871001665, 94404798756171, 54290928637774, 112742212150946, 91051110026378, 124542182410773, 40388473698647, 22059564851978, 57353373067776, 80692115733908, 84559172686971, 28186390895657]
c = 1620418829165478

With this, we can find the density

from math import log

d = len(t) / log(max(t), 2)
# d = 0.6834156494834176

It's said that low density can be done directly. I don't understand the principle. Copying a script also uses grid

t = [97005071980911, 32652300906411, 73356817713575, 108707065719744, 103728503304990, 49534310783118, 53330718889073, 2121345207564, 46184783396167, 115771983454147, 64261597617025, 2311575715655, 56368973049223, 84737125416797, 24316288533033, 82963866264519, 101019837363048, 25996629336722, 41785472478854, 68598110798404, 40392871001665, 94404798756171, 54290928637774, 112742212150946, 91051110026378, 124542182410773, 40388473698647, 22059564851978, 57353373067776, 80692115733908, 84559172686971, 28186390895657]
c = 1620418829165478
n = len(t)
M = Matrix.identity(n)

last_row = [0 for x in t]
M_last_row = Matrix(ZZ, 1, len(last_row), last_row)

ct = 1620418829165478
last_col = t[:]
last_col.append(ct)
M_last_col = Matrix(ZZ, len(last_col), 1, last_col)

M = M.stack(M_last_row)
M = M.augment(M_last_col)

X = M.LLL()
target = X[-1][:-1]
ans = [abs(k) for k in target]
flag = int(''.join([str(i) for i in ans])[::-1], 2)
# 4159506287

Magic changed la guy's script

Then I heard that the string of numbers of the decrypted flag were post ed in the past to get the flag. Did you get it

Crypto-ocb(unsolved)

L1near is tired of the life of AK competition. He wants to take this opportunity to learn AES.OCB, which is his latest encryption system. Please help him see what the problem is.

from ocb.aes import AES # https://github.com/kravietz/pyOCB
from base64 import b64encode, b64decode
from Crypto.Util.number import *
from hashlib import sha256
from secret import flag
from ocb import OCB
import socketserver
import signal
import string
import random
import os

xor = lambda s1 , s2 : bytes([x1^x2 for x1,x2 in zip(s1,s2)])
def check(data):
    try:
        if(len(data) % 16 != 0):
            return False
        for i in range(data[-1]):
            if(data[-1] != data[-1-i]):
                return False
        return True
    except:
        return False
def pad(data):
    if check(data):
        return data
    padlen = 16 - len(data) % 16
    return data + padlen * bytes([padlen])
def unpad(data):
    if not check(data):
        return data
    return data[:-data[-1]]

BANNER =br'''
 __      __          ___                                           __                                               __       ___  
/\ \  __/\ \        /\_ \                                         /\ \__                                           /\ \__  /'___\ 
\ \ \/\ \ \ \     __\//\ \     ___    ___     ___ ___      __     \ \ ,_\   ___       __  __  __    ___ ___     ___\ \ ,_\/\ \__/ 
 \ \ \ \ \ \ \  /'__`\\ \ \   /'___\ / __`\ /' __` __`\  /'__`\    \ \ \/  / __`\    /\ \/\ \/\ \ /' __` __`\  /'___\ \ \/\ \ ,__\
  \ \ \_/ \_\ \/\  __/ \_\ \_/\ \__//\ \L\ \/\ \/\ \/\ \/\  __/     \ \ \_/\ \L\ \   \ \ \_/ \_/ \/\ \/\ \/\ \/\ \__/\ \ \_\ \ \_/
   \ `\___x___/\ \____\/\____\ \____\ \____/\ \_\ \_\ \_\ \____\     \ \__\ \____/    \ \___x___/'\ \_\ \_\ \_\ \____\\ \__\\ \_\ 
    '\/__//__/  \/____/\/____/\/____/\/___/  \/_/\/_/\/_/\/____/      \/__/\/___/      \/__//__/   \/_/\/_/\/_/\/____/ \/__/ \/_/ 
'''

MENU = br'''[+] 1.Encrypt
[+] 2.Decrypt
[+] 3.Get flag
[+] 4.Exit
'''

class Task(socketserver.BaseRequestHandler):
    def _recvall(self):
        BUFF_SIZE = 2048
        data = b''
        while True:
            part = self.request.recv(BUFF_SIZE)
            data += part
            if len(part) < BUFF_SIZE:
                break
        return data.strip()

    def send(self, msg, newline=True):
        try:
            if newline:
                msg += b'\n'
            self.request.sendall(msg)
        except:
            pass

    def recv(self, prompt=b'[-] '):
        self.send(prompt, newline=False)
        return self._recvall()

    def proof_of_work(self):
        random.seed(os.urandom(8))
        proof = ''.join([random.choice(string.ascii_letters+string.digits) for _ in range(20)])
        _hexdigest = sha256(proof.encode()).hexdigest()
        self.send(f"[+] sha256(XXXX+{proof[4:]}) == {_hexdigest}".encode())
        x = self.recv(prompt=b'[+] Plz tell me XXXX: ')
        if len(x) != 4 or sha256(x+proof[4:].encode()).hexdigest() != _hexdigest:
            return False
        return True

    def encrypt(self, nonce, message, associate_data=b''):
        assert nonce not in self.NONCEs
        self.NONCEs.append(nonce)
        self.ocb.setNonce(nonce)
        message = pad(message)
        tag, cipher = self.ocb.encrypt(bytearray(message), bytearray(associate_data))
        return (bytes(cipher), bytes(tag))
    
    def decrypt(self, nonce, cipher, tag, associate_data=b''):
        self.ocb.setNonce(nonce)
        authenticated, message = self.ocb.decrypt(*map(bytearray, (associate_data, cipher, tag)))
        message = unpad(message)
        if not authenticated:
            self.send(b"[!] Who are you???")
            return b''
        return message

    def handle(self):
        signal.alarm(60)
        self.send(BANNER)
        if not self.proof_of_work():
            self.send(b'[!] Wrong!')
            return
        
        self.send(b'[+] Welcome my friend!')
        self.send(b'[+] Can you find the secret through the easy encryption system?')

        aes = AES(128)
        self.ocb = OCB(aes)
        KEY = os.urandom(16)
        self.ocb.setKey(KEY)
        self.NONCEs = []

        while True:
            self.send(MENU, newline=False)
            choice = self.recv()
            if(choice == b'1'):
                try:
                    self.send(b'[+] Please input your nonce')
                    nonce = b64decode(self.recv())
                    self.send(b'[+] Please input your message')
                    message = b64decode(self.recv())
                    associate_data = b'from baby'
                    ciphertext, tag = self.encrypt(nonce, message, associate_data)
                    self.send(b"[+] ciphertext: " + b64encode(ciphertext))
                    self.send(b"[+] tag: " + b64encode(tag))
                except:
                    self.send(b"[!] ERROR!")
            elif(choice == b'2'):
                try:
                    self.send(b'[+] Please input your nonce')
                    nonce = b64decode(self.recv())
                    self.send(b'[+] Please input your ciphertext')
                    ciphertext = b64decode(self.recv())
                    self.send(b'[+] Please input your tag')
                    tag = b64decode(self.recv())
                    self.send(b'[+] Please input your associate data')
                    associate_data = b64decode(self.recv())
                    if associate_data == b'from admin':
                        self.send(b'[!] You are not admin!')
                        break
                    message = self.decrypt(nonce, ciphertext, tag, associate_data)
                    self.send(b'[+] plaintext: ' + b64encode(message))
                except:
                    self.send(b"[!] ERROR!")
            elif(choice == b'3'):
                try:
                    nonce = b'\x00'*16
                    message = flag
                    associate_data = b'from admin'
                    ciphertext, tag = self.encrypt(nonce, message, associate_data)
                    self.send(b"[+] ciphertext: " + b64encode(ciphertext))
                    self.send(b"[+] tag: " + b64encode(tag))
                except:
                    self.send(b"[!] ERROR!")
            elif(choice == b'4'):
                self.send(b'[+] Bye~')
                self.send(b'[+] See you next time!')
                break
            else:
                self.send(b'[!] What are you doing???')
                self.send(b'[!] Go away!')
                break

        self.request.close()

class ThreadedServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
    pass

class ForkedServer(socketserver.ForkingMixIn, socketserver.TCPServer):
    pass

if __name__ == "__main__":
    HOST, PORT = '0.0.0.0', 10001
    server = ForkedServer((HOST, PORT), Task)
    server.allow_reuse_address = True
    print(HOST, PORT)
    server.serve_forever()

Jinmen cup and hongminggu have similar problems. AES in ocb mode provides authentication. The process in this mode has not been studied carefully. Looking at the code, there is no way to bypass the associate_data == b'from admin'

Crypto-easylsb(recuring)

#!/usr/bin/python3
# encoding: utf-8
import random
import string
import sys
import os
from hashlib import sha256
import uuid
from Crypto.Util.number import *

password = # Hidden
flag = ('flag{' + str(uuid.uuid4()) + '}').encode()

def proof_of_work():
    random.seed(os.urandom(8))
    proof = ''.join([random.choice(string.ascii_letters+string.digits) for _ in range(20)]).encode()
    digest = sha256(proof).hexdigest()
    printf("sha256(XXXX+%s) == %s" % (proof[4:].decode(),digest))
    printf('Give me XXXX:')
    x = read_str()
    if len(x) != 4 or sha256(x.encode()+proof[4:]).hexdigest() != digest: 
        return False
    return True

def printf(message):
    sys.stdout.write('{0}\n'.format(message))
    sys.stdout.flush()
    sys.stderr.flush()

def read_str():
    return sys.stdin.readline().strip()

def read_int():
    return int(sys.stdin.readline().strip())

def next_prime(a):
    while not isPrime(a):
        a += 2
    return a

def get_prime(a):
    suffix = getPrime(368)
    return next_prime(a ** 2 + suffix + 1)

def generate_pubkey(key):
    p, q = get_prime(getPrime(512)), get_prime(key)
    n = p * q
    return n

def airdrop(a):
    n = generate_pubkey(a)
    printf('gift: {}'.format(n))
    return

def hint(n, e, c):
    printf('n = {}'.format(n))
    printf('e = {}'.format(e))
    printf('c = {}'.format(c))
    return

def leak():
    p = get_prime(getPrime(512))
    e = 0x1000
    c = pow(bytes_to_long(flag), e, p)
    
    hint(p, e, c)
    return

def backdoor():
    printf('Input your password:')
    user_input = read_str()
    if user_input.encode() == password:
        leak()
    else:
        printf('Wrong')
        exit(0)

if __name__ == '__main__':
    if not proof_of_work():
        exit(0)
    
    a = getPrime(512)
    p = get_prime(a)
    q = get_prime(getPrime(512))
    n = p * q
    e = 0x10001
    max_time = 5
    password_enc = pow(bytes_to_long(password), e, n)
    
    printf('====================================',)
    printf('1. Airdrop                          ',)
    printf('2. Backdoor                         ',)
    printf('3. Hint                             ',)
    printf('4. Exit                             ',)
    printf('====================================',)
    
    try:
        while True:
            printf('Your choice:')
            choice = read_int()
            if choice == 1:
                if max_time > 1:
                    airdrop(a)
                    max_time -= 1
                    printf('Done!')
                else:
                    printf('Greed will destroy you!')
                continue
            elif choice == 2:
                backdoor()
                printf('Done!')
                continue
            elif choice == 3:
                hint(n, e, password_enc)
                printf('Done!')
                continue
            elif choice == 4:
                printf('bye~')
                exit(0)
                continue
            else:
                printf('Invalid!')
                continue
    except:
        exit(-1)

The process is relatively simple. Although not, the idea is relatively clear. a and p are obtained through 4 groups of gift s and 1 n → \rightarrow → decrypt password → \rightarrow → decrypt the flag when e=4096 and the modulus is prime

At the beginning, master Shang reminded me that after trying, I found that the rounding result of q after root opening is the same as that of a
⌊ q ⌋ = a \lfloor \sqrt{q} \rfloor=a ⌊q ​⌋=a
Then assume q = n e x t _ p r i m e ( a 2 + s u f f i x 1 + 1 ) ,   p = n e x t _ p r i m e ( b 2 + s u f f i x 2 + 1 ) q=next\_prime(a^2+suffix_1+1),\ p=next\_prime(b^2+suffix_2+1) q=next_prime(a2+suffix1​+1), p=next_prime(b2+suffix2​+1)

So n=pq, you can get the high position of ab by giving n a root, but you don't know how to use it

Save a set of data and have a chance to study in the future

# nc 47.104.243.99 9999
import random
import string
import sys
import os
from hashlib import sha256
import uuid
from Crypto.Util.number import *
from pwn import *
from itertools import product


# flag = ('flag{' + str(uuid.uuid4()) + '}').encode()
# print(flag)
# flag{cb8365b5-a825-4c65-9251-b6827f0792ad}


def proof_of_work():
    # sha256(XXXX+cgDUNjezTPNSj91D) == 30fc93b19ef81e8755f3ee0e3df72722f66556b7636b5037f34d1beb981235b0
    proof = sh.recvline()
    tail = proof[12:28].decode()
    HASH = proof[23:97]
    for i in product(string.ascii_letters + string.digits, repeat=4):
        head = ''.join(i)
        t = hashlib.sha256((head + tail).encode()).hexdigest()
        if t == HASH:
            sh.sendline(head.encode())
            break


def proof_of_work2():
    # sha256(XXXX+cgDUNjezTPNSj91D) == 30fc93b19ef81e8755f3ee0e3df72722f66556b7636b5037f34d1beb981235b0
    proof = sh.recvline()
    tail = 'eqbj8j6Z9xvz3YiV'
    HASH = 'b5808aff39327c9ac49d209d10fe3e27c898d49faffc956bbf1d73523c44ce77'
    for i in product(string.ascii_letters + string.digits, repeat=4):
        head = ''.join(i)
        t = hashlib.sha256((head + tail).encode()).hexdigest()
        if t == HASH:
            sh.sendline(head.encode())
            break


context.log_level = 'debug'
sh = remote("47.104.243.99", 9999)
proof_of_work2()

e = 0x10001
n = 124478026101165354098037876421627662624056206605515177686194103211430464934743129994417330643128683345849733014275487857184516763016301408033382676283620282332485581507315430690690813831282519976585364463744017296315372258981215919387679949709396064987889800074036410663927631478105899096723790945928412829187822284593750473740315866322998068351563015099367643886154042581191841533888375305195743073059105310700318861167337672659772641786687582718180589854118978820530842381081568922213227168617789474006973152602334271699398178963791154954792676067153150646411025449463253194489657095241613282942586704728903727611399
c = 90647155870804971113806442051901226002120015769259333554192477899450971338831255790857101662710560234954831825416787459228033373486077151217415092360097814474283515220281223555587026056325099266316005605716929634353603643319859645167427538563242884591102004934790399528462112789803351851769047685792159647390050985871679243422993775721776244067168064933786611606433105514418429089777322132633028815660525070271128628044386434106685643657668695364607215033856398608992051550288297119711825866170869469834444973857013360900452988222767960318998636640763573797297203544581343736625672669946528644260077687270041162148579
gift1 = 430643544402084432319325961880416327356872029175895120742910502784460696485981655831364057771978842374920289740546998744096646780935886278222230684528731470188637076148307527311922452490801045278988434801896164340653915198079023711297016090027381126073802620204314765869166624636941907534206046998568042400815444697126334029985946496452932477337335924863188276040631646131204436116708742280199903183210826719901897273260766069768314579353548171372586771188839003301749872795307598319516051259672117483195538538878148292313730887085591272354625175614366936749367007177827223031514498275753340915542939818624965339274541
gift2 = 279643881521430665779764628210196159031443254319916096260435206316116655701344325784134050728686231352816394212502789612947929220430466611004330150352137570405484127780364316335386736272544877793446702006665399064591475517610575894857804921152265901610537191780251376268112843688812459951190257679817490601282013470378644045696567456486059374094892490322848884260103728441765221196492288890565220765116737467020984854284776188063793107604665880577892150257025900438921323929874583349697921571156857890185078774883450481945134786456867498237937223992977125106207044050316201931335150865420643200300919950666792333800421
gift3 = 237902069859826089956710602458488697197969935460375469157966706791637991891038954423106099106663742928616105443683571279895168734280020803510641968762322744746722455831059684745613465616901995570874116303439549541932451281441959514629564655972962203744852006794160278105621063202850402448076034174743227230202591123961117876362833492478366233652816443873213201410433457033307944305406209168085355438156499669719905462067847881209129983251184647052314353242784174374088582263983943733709287614092898665984536781786084591414804290805713181580225096207601673326693693442261927044483426965621699507399608913104482509541829
gift4 = 131184496439376311814751172869309509301398236134030748081290782986296909958428702969677021306310259793511587606469385852829507392096577310273567455635233040499932518933927338330158300947934921792366825549482737059128276134653805578959896357503546949681198843822945160611138388841031519307824760189249466171835761078895545203381195921789823129815826662876576368032722825159838976137103324588326186884693453137115752294499574361951327089081432442184727065530788376603390307277709197418051468405219378610308912749832078805547917787498228816440083434077213552664217150489211767711038795362880479839885325109115335568243823

Now you can reproduce the problem

First, supplement, get a, then we can see from the above conclusion a 2 a^2 a2 is the high position of q, isn't this the high position attack of p known by RSA

I don't know which equation system gets the lattice in WP

Well, the third wave reappears. I thought the agcd was the master's wrong number. It turned out to be my dish. It's a paper Approximate GCD

Look, the format and title correspond exactly

Ha ha, I can't understand it. Take ge and run away

x i x_i xi , obviously five groups n i n_i ni , the root sign is obtained, which is also confirmed above; λ \lambda λ It's 368, because in the paper r i r_i ri , is in the title s u f f i x i + 1 suffix_i+1 suffixi​+1

In this way, LLL() is performed on matrix B to obtain q 0 ⋅ 2 λ + 1 q_0\cdot 2^{\lambda +1} q0​⋅2 λ+ 1, divided by 2 λ + 1 2^{\lambda +1} two λ+ 1 is in the paper q 0 q_0 q0 , that is, with x 0 x_0 x0 , the corresponding one

So far, there is still one step missing, and then in the topic n = p q n=pq n=pq derivation
n = p q = ( a 2 + s u f f i x 1 + 1 ) ( b 2 + s u f f i x 2 + 1 ) n=pq=(a^2+suffix_1+1)(b^2+suffix_2+1) n=pq=(a2+suffix1​+1)(b2+suffix2​+1)
set up β = b 2 + s u f f i x 2 + 1 ,   δ \beta =\sqrt {b^2+suffix_2+1},\ \delta β=b2+suffix2​+1 ​,  δ Is the low position after p root opening, r = δ β r=\delta \beta r=δβ

Root n
n = ( a + δ ) β = a β + r \sqrt n=(a+\delta)\beta =a\beta +r n ​=(a+δ)β=aβ+r
This is the same as the form given in the paper, and we just figured it out q 0 q_0 q0# is here β \beta β, last ⌊ n / β ⌋ = ⌊ ( a β + r ) / β ⌋ \lfloor\sqrt n/\beta \rfloor =\lfloor (a\beta +r)/\beta\rfloor ⌊n ​/ β ⌋=⌊(a β+ r)/ β ⌋, obviously r / β r/\beta r/ β It's already a decimal part, and rounding is completely rounded off, so the result is a

The ans obtained should be b above. First use the master's script to get a

from sympy import root

e = 0x10001
n = 124478026101165354098037876421627662624056206605515177686194103211430464934743129994417330643128683345849733014275487857184516763016301408033382676283620282332485581507315430690690813831282519976585364463744017296315372258981215919387679949709396064987889800074036410663927631478105899096723790945928412829187822284593750473740315866322998068351563015099367643886154042581191841533888375305195743073059105310700318861167337672659772641786687582718180589854118978820530842381081568922213227168617789474006973152602334271699398178963791154954792676067153150646411025449463253194489657095241613282942586704728903727611399
c = 90647155870804971113806442051901226002120015769259333554192477899450971338831255790857101662710560234954831825416787459228033373486077151217415092360097814474283515220281223555587026056325099266316005605716929634353603643319859645167427538563242884591102004934790399528462112789803351851769047685792159647390050985871679243422993775721776244067168064933786611606433105514418429089777322132633028815660525070271128628044386434106685643657668695364607215033856398608992051550288297119711825866170869469834444973857013360900452988222767960318998636640763573797297203544581343736625672669946528644260077687270041162148579
gift1 = 430643544402084432319325961880416327356872029175895120742910502784460696485981655831364057771978842374920289740546998744096646780935886278222230684528731470188637076148307527311922452490801045278988434801896164340653915198079023711297016090027381126073802620204314765869166624636941907534206046998568042400815444697126334029985946496452932477337335924863188276040631646131204436116708742280199903183210826719901897273260766069768314579353548171372586771188839003301749872795307598319516051259672117483195538538878148292313730887085591272354625175614366936749367007177827223031514498275753340915542939818624965339274541
gift2 = 279643881521430665779764628210196159031443254319916096260435206316116655701344325784134050728686231352816394212502789612947929220430466611004330150352137570405484127780364316335386736272544877793446702006665399064591475517610575894857804921152265901610537191780251376268112843688812459951190257679817490601282013470378644045696567456486059374094892490322848884260103728441765221196492288890565220765116737467020984854284776188063793107604665880577892150257025900438921323929874583349697921571156857890185078774883450481945134786456867498237937223992977125106207044050316201931335150865420643200300919950666792333800421
gift3 = 237902069859826089956710602458488697197969935460375469157966706791637991891038954423106099106663742928616105443683571279895168734280020803510641968762322744746722455831059684745613465616901995570874116303439549541932451281441959514629564655972962203744852006794160278105621063202850402448076034174743227230202591123961117876362833492478366233652816443873213201410433457033307944305406209168085355438156499669719905462067847881209129983251184647052314353242784174374088582263983943733709287614092898665984536781786084591414804290805713181580225096207601673326693693442261927044483426965621699507399608913104482509541829
gift4 = 131184496439376311814751172869309509301398236134030748081290782986296909958428702969677021306310259793511587606469385852829507392096577310273567455635233040499932518933927338330158300947934921792366825549482737059128276134653805578959896357503546949681198843822945160611138388841031519307824760189249466171835761078895545203381195921789823129815826662876576368032722825159838976137103324588326186884693453137115752294499574361951327089081432442184727065530788376603390307277709197418051468405219378610308912749832078805547917787498228816440083434077213552664217150489211767711038795362880479839885325109115335568243823

f = lambda a: int(root(a, 2))
x0, x1, x2, x3, x4 = f(n), f(gift1), f(gift2), f(gift3), f(gift4)

B = matrix(ZZ, [[2 ^ 368, x1, x2, x3, x4], [0, -x0, 0, 0, 0], [0, 0, -x0, 0, 0], [0, 0, 0, -x0, 0], [0, 0, 0, 0, -x0]])
L = B.LLL()
ans = L[0][0] // 2 ^ 368

p0 = abs(ans)
a = x0 // p0

print(a)
# a = 25582847577564670038612582668140373129129959651036453923605273284793860890291221263498753328353767798264241675861426056503889321642277844202986695039010291

Then there is the familiar rhythm, but it is different from the general known p high-level attack a 2 a^2 a2 is almost the same as the number of bits of p, but I don't know how much the suffix of 368 bits is, so the essence is the same. Use the CopperSmith algorithm to find the suffix; The kbits here is 369, because 368 can't get out. Go to a larger place

a = 25582847577564670038612582668140373129129959651036453923605273284793860890291221263498753328353767798264241675861426056503889321642277844202986695039010291
n = 124478026101165354098037876421627662624056206605515177686194103211430464934743129994417330643128683345849733014275487857184516763016301408033382676283620282332485581507315430690690813831282519976585364463744017296315372258981215919387679949709396064987889800074036410663927631478105899096723790945928412829187822284593750473740315866322998068351563015099367643886154042581191841533888375305195743073059105310700318861167337672659772641786687582718180589854118978820530842381081568922213227168617789474006973152602334271699398178963791154954792676067153150646411025449463253194489657095241613282942586704728903727611399
pbar = a ** 2
kbits = 369
PR.<x> = PolynomialRing(Zmod(n))
f = pbar + x
roots = f.small_roots(X=2^kbits, beta=0.4)
# 967901962469872165537856438801710756065070673694594801499396171114255660549746759504438698205658088002955084386

Next, the regular steps of RSA get the password

Cou1d_I_get_Th3_passw03d_then_captu7e_the_fla9?

Because I didn't get the c of flag in the competition, I borrowed it from this master

As I said at the beginning, is e even or 2 12 2^{12} 212, and φ ( p ) = p − 1 \varphi(p)=p-1 φ( p)=p − 1 is not mutually prime. You should say PUTAOAO at this time. Your line is wrong. You can't say heaven or anything when you take revenge. It should be like this: my name is master Shang. In order to calm the regret and hatred of my friend 4XWi11 and let the spirit of my left friend Y1m0 rest in heaven, I need you to 4 apologize!

Square root, square root of finite field, Rabin, a shuttle

However, considering that 4096 here is relatively large, the square of finite field will be very slow. Use nthroot directly_ The mod function doesn't seem to work. The traditional rabin function doesn't work because the modulus is p; Seeing the master's enlightenment, I specially sent an article about rabin principle blog

Although I don't know nthroot_ Why not mod? Because P% 4 = 3 meets the most basic condition of rabin, it is necessary to meet this condition, and this condition is the condition with few results, so there is no pressure to directly open the 12th power

The key codes m1 = pow(c, (p + 1) // 4, p) and m2 = p - m1

from Crypto.Util.number import long_to_bytes

p = 496584754781581997154645314415051021632937719346451955222548277806458479939882609131615548616817732786901123585586203791585231652481101508165523306207307511005218236201069837205145881515297396218450658339325435517394532697652694250302927324547950654199907918057947165277944713164863611463887879016367147027651
e = 4096
c = 202821697585498721190880385651888326819052363235092021514522019296117832067188656931773131985516119359273814956340533509702817980744398402155886334655033938474295749168241550740096583920405311629354495691732306096266636370938656838375279086916114964255411601403125984312042419408682006688199111243135798564394

mi = []

for i in range(12):
    mi.append(pow(c, (p + 1) // 4, p))
    mi.append(p - pow(c, (p + 1) // 4, p))
    c = pow(c, (p + 1) // 4, p)

for i in mi:
    t = long_to_bytes(i)
    if b'WMCTF' in t:
        print(t)
        break

Then it seems that Striving's blog is also available in the limited field. It seems that I didn't understand the essence. ctfshow unusualrsa series blog In fact, with sage, everything is not complicated

from Crypto.Util.number import *
p = 496584754781581997154645314415051021632937719346451955222548277806458479939882609131615548616817732786901123585586203791585231652481101508165523306207307511005218236201069837205145881515297396218450658339325435517394532697652694250302927324547950654199907918057947165277944713164863611463887879016367147027651
e = 4096
c = 202821697585498721190880385651888326819052363235092021514522019296117832067188656931773131985516119359273814956340533509702817980744398402155886334655033938474295749168241550740096583920405311629354495691732306096266636370938656838375279086916114964255411601403125984312042419408682006688199111243135798564394
R.<x> = Zmod(p)[]
f = x ^ e - c
f = f.monic()
res1 = f.roots()
print(res1)
# res1 = [(496584754781581997154645314415051021632937719346451955222548277806458479939882609131615548616817732786901123585586203791585231652481101508165523306207307511005218236201069837205145881515297396218450658339313214656968189495352306293673615992017103882095004555948437432049586089024300970437646867574391499674950, 1), (12220860426343202300387956629311332530846772104903362109509733228358624140562641026241011441975647352701, 1)]
c = 12220860426343202300387956629311332530846772104903362109509733228358624140562641026241011441975647352701
print(long_to_bytes(c))

It's true. It runs faster than expected

Crypto-ezl1near(unsolved)

from Crypto.Util.number import long_to_bytes , bytes_to_long , getPrime , inverse
from Crypto.Cipher import AES
import socketserver , signal
import random
import string
from hashlib import sha256
import os
from secret import flag
q = 2**24

def getrandbits(n):
    return bytes_to_long(os.urandom(n // 8+1)) >> (8-n%8)

class server(socketserver.BaseRequestHandler):
    

    def _recv(self):
        data = self.request.recv(1024)
        return data.strip()

    def _send(self, msg, newline=True):
        if isinstance(msg , bytes):
            msg += b'\n'
        else:
            msg += '\n'
            msg = msg.encode()
        self.request.sendall(msg)

    def proof_of_work(self):
        random.seed(os.urandom(8))
        proof = ''.join([random.choice(string.ascii_letters+string.digits) for _ in range(20)])
        _hexdigest = sha256(proof.encode()).hexdigest()
        self._send(f"sha256(XXXX+{proof[4:]}) == {_hexdigest}".encode())
        self._send(b'Give me XXXX: ')
        x = self._recv()
        if len(x) != 4 or sha256(x+proof[4:].encode()).hexdigest() != _hexdigest:
            self.send('wrong')
            return False
        return True

    def genrsa(self):
        _p = getPrime(1024)
        _q = getPrime(1024)
        self.n = _p * _q
        self.e = 65537
        self.d = inverse(self.e , (_p-1)*(_q-1))

    def to_vec(self,num , length):
        vec = []
        while length > 0:
            vec = [num % q] + vec
            num //= q
            length -= 1
        return vec

    def to_mat(self,numlist):
        M =[]
        for i in numlist:
            M.append(self.to_vec(i , 40))
        return M

    def enc(self, key , m):
        key = self.to_mat(key)
        res = []
        for i in range(40):
            temp = 0
            for j in range(16):
                temp += m[j]* key[j][i]
                temp %= q
            res.append(temp)
        return res
    def handle(self):
        signal.alarm(120)
        self.proof_of_work()
        self.genrsa()
        self._send(str(self.n))
        self._send(str(self.e))
        secret = [1] + [2*getrandbits(23)-1 for _ in range(15)]
        self._send(b'Please generate key for me and I will give you my secret.But you have only two chances.')
        for i in range(2):
            key = []
            f0 = getrandbits(480)
            key.append(f0)
            self._send(str(pow(f0 , self.e , self.n)))
            f0 += f0 << 480
            for j in range(15):
                self._send('key'+str(i+1) + ':')
                c = int(self._recv())
                m = pow(c , self.d , self.n)
                f = m - f0
                f %= self.n
                key.append(f)
            c = self.enc(key , secret)
            self._send('Thanks, here is your cipher:' + str(c))
        self._send(b'do you know the secret?')
        guess = [int(i) for i in self._recv().split(b' ')]
        if len(guess) == 16:
            for j in range(16):
                if guess[j] != secret[j]:
                    break
            else:
            
                self._send(b'congratulations. here is your flag:')
                self._send(flag)
                return 0
        else:
            self._send(b'L1near don\'t care.')
        

class ForkedServer(socketserver.ForkingMixIn, socketserver.TCPServer):
    pass

if __name__ == "__main__":
    HOST, PORT = '0.0.0.0', 10000
    server = ForkedServer((HOST, PORT), server)
    server.allow_reuse_address = True
    server.serve_forever()

I'm not in the mood. I can't watch it anymore

It can be said to be a major mistake. The password burst 0 in the whole process. I'm sorry for the master of web and misc

Posted by SteveMellor on Fri, 03 Sep 2021 19:01:15 -0700