---
# System prepended metadata

title: 'WRITEUP COMPFEST 15-2023 Quals [mirror]'

---

# WRITEUP COMPFEST 15-2023 Quals [mirror] - wondPing
## Choose Exponent **[crypto]**
We are given this following code
```python
from Crypto.Util.number import getPrime, bytes_to_long

FLAG = b"COMPFEST15{REDACTED}".ljust(256, b"\x00")

class RSA:
    def __init__(self):
        self.p = getPrime(1024)
        self.q = getPrime(1024)
        self.n = self.p * self.q
        # you can choose your own public exponent
        # self.e = 65537

    def encrypt(self, m, e):
        return pow(m, e, self.n)

    def decrypt(self, c, d):
        return pow(c, d, self.n)


def main():
    print("Welcome to RSA challenge!")
    print("In this challenge you can choose your own public exponent")

    rsa = RSA()
    m = bytes_to_long(FLAG)
    count = 0
    while count < 3:
        print("What do you want to do?")
        print("1. Get encrypted flag")
        print("2. Exit")

        option = input(">> ")
        if option == "1":
            e = int(input("Enter your public exponent (e cannot be 1 and even): "))
            if e == 1 or e % 2 == 0:
                print("loh gak bahaya tah")
                continue
            c = rsa.encrypt(m, e)
            print(f"Here is your encrypted flag: {c}")
            count += 1
        elif option == "2":
            print("Bye!")
            exit()
        else:
            print("Invalid option")
            continue
    
    print("You have reached maximum number of public exponent")

if __name__ == "__main__":
    main()

```
We get some standard encryption scheme using RSA. From there we can get encrypted flag that use $e$ is from our input. The return just the encrypted flag. Our idea to get the flag is just input $e$ = $3$ and getting different Modulus or $n$ and different encrypted flag, then solve it using CRT. But, we need to get the value of $n$. On the program with same $n$ we get 3 chance to input $e$ so we can getting $n$ from it. The following equation will help:
$$Flag^3 = C1{\space}(Mod{\space}n)$$$$Flag^{9} = C2{\space}(Mod{\space}n)$$$$Flag^{15} = C3{\space}(Mod{\space}n)$$ we will use $Flag^3$ as base to reproduce equation from another 2 equation else.
$$Flag^{9}=(Flag^3)^3=C1=C2^3{\space}(Mod{\space}n)$$$$k1*n=C1^3-C2$$ $$Flag^{15}=(Flag^3)^5=C1=C3^5{\space}(Mod{\space}n)$$$$k2*n=C1^5-C3$$. we can get the $n$ value by calculating $GCD(C1^3-C2,C1^5-C3)$. Then after getting $enc(Flag)$ and $n$ we can just use CRT to get the flag.
Final Solver:
```python
from pwn import *
from sympy.ntheory.modular import crt
from Crypto.Util.number import *
import gmpy2
import math

def startIO():
    io = remote("34.101.68.243","10004")
    return io

def getCip(io, e):
    io.recvuntil(b'>> ')
    io.sendline(b'1')
    io.recvuntil(b'(e cannot be 1 and even): ')
    io.sendline(str(e).encode())
    io.recvuntil(b'encrypted flag: ')
    c = int(io.recvline().decode().strip())
    return c

def getnc():
    io = startIO()
    f3 = getCip(io, 3)
    f9 = getCip(io, 9)
    f15 = getCip(io, 15)
    io.close()
    n1 = f3**3 - f9
    n2 = f3**5 - f15
    n = math.gcd(n1, n2)
    for i in range(2, 100):
        if(n%i==0):
            n//=i
    assert(len(bin(4))<=2048)
    return f3, n

if __name__ == "__main__":
    m = []
    c = []
    for i in range(3):
        c1, n = getnc()
        m.append(n)
        c.append(c1)
    hasil = crt(m, c)[0]
    root = gmpy2.iroot(hasil, 3)
    flag = long_to_bytes(root[0])
    print(flag)
```
![](https://hackmd.io/_uploads/H1TXn2qJ6.jpg)
> COMPFEST15{gcd_is_too_powerful_to_get_factorization_78010ce551}
## Swusjack Encryption **[crypto]**
We are given this following code
```python
from Crypto.Util.number import long_to_bytes, bytes_to_long

p = 1179478847235411356076287763101027881
e = 0x10001


def bytes_to_block(msg: bytes):
    res = []
    msg_int = bytes_to_long(msg)
    while msg_int:
        res.append(msg_int % (p**2))
        msg_int //= p**2
    return res


def block_to_bytes(blocks: list[int]):
    res = 0
    for i in range(len(blocks) - 1, -1, -1):
        res *= p**2
        res += blocks[i]
    return long_to_bytes(res)


class MultiplicativeGroup:
    def __init__(self, p, a, b):
        self.a = a
        self.b = b
        self.p = p

    def __mul__(self, other) -> "MultiplicativeGroup":
        a = (self.a * other.a - 6969 * self.b * other.b) % self.p
        b = (self.a * other.b + self.b * other.a - 69 * self.b * other.b) % self.p
        return MultiplicativeGroup(self.p, a, b)

    def __pow__(self, n) -> "MultiplicativeGroup":
        res = MultiplicativeGroup(self.p, 1, 0)
        base = self
        while n:
            if n & 1:
                res *= base
            base *= base
            n >>= 1
        return res
    
    def __repr__(self):
        return f"({self.a}, {self.b})"


if __name__ == "__main__":
    FLAG = open("flag.png", "rb").read()
    blocks = bytes_to_block(FLAG)
    enc = []
    for block in blocks:
        assert block < p**2
        a = block % p
        b = block // p
        m = MultiplicativeGroup(p, a, b)
        c = m ** e
        enc.append(c.a + c.b * p)
        
    open("flag.enc", "wb").write(block_to_bytes(enc))
```
The source code is encrypting the image that contains the flag. Firstly divide the flag.png to some block and every block is multiplicate by $e$ on Multiplicative Group. To get back the flag we can just multiplicate the every cipher block with $d$ that is inverse of $e$ on multiplicative group. To find $d$ we need to calculate order of the group. I having some try to caltulate order of group.
```python
a,b = (97, 13)
p = 821
print(p)
m = MultiplicativeGroup(p, a, b)
for i in range(2, 2*p**2):
    hasil = [int(i) for i in str(m**i).replace(")","").replace("(","").split(',')]
    if(1==hasil[0] and 0==hasil[1]):
        print(i)
        continue
```
from the source code above i trying to get point $(1,0)$. From that i getting some output, that was on $i$ equals ${i=224680,449360,674040,898720,...}$
from that i know that on $p=821$, the 3 value of i is equal to $p*p-1=674040$. From that i think the order maybe has formula $p*p-1$.
```python
for i in range(200):
    p = getPrime(500)
    if(i!=1000):p = 1179478847235411356076287763101027881
    exc = p*(p-1)
    # print("trying on i",i+1,"when p =",p, "expect",exc)
    print("trying on i",i+1)
    # base = MultiplicativeGroup(p, 1, 0)
    a,b = randint(1, p), randint(1,p)
    m = MultiplicativeGroup(p, a, b)
    hasil = [int(i) for i in str(m**(p*p-1)).replace(")","").replace("(","").split(',')]
    if(1==hasil[0] and 0==hasil[1]):
        continue
    else: print("not Validd")
```
from source code above i trying to prove my formula and from 200 trying and bigger $p$, then the output all of that is valid. Then after it i going to write my solver code to getting the flag. The step is parse cipher to block then find $d$, after it just multiplicate every block and convert back block to bytes to get image file. 
Final Solver:
```python
from Crypto.Util.number import *

p = 1179478847235411356076287763101027881
e = 0x10001

def bytes_to_block(msg: bytes):
    res = []
    msg_int = bytes_to_long(msg)
    while msg_int:
        res.append(msg_int % (p**2))
        msg_int //= p**2
    return res


def block_to_bytes(blocks: list[int]):
    res = 0
    for i in range(len(blocks) - 1, -1, -1):
        res *= p**2
        res += blocks[i]
    return long_to_bytes(res)

class MultiplicativeGroup:
    def __init__(self, p, a, b):
        self.a = a
        self.b = b
        self.p = p

    def __mul__(self, other) -> "MultiplicativeGroup":
        a = (self.a * other.a - 6969 * self.b * other.b) % self.p
        b = (self.a * other.b + self.b * other.a - 69 * self.b * other.b) % self.p
        return MultiplicativeGroup(self.p, a, b)

    def __pow__(self, n) -> "MultiplicativeGroup":
        res = MultiplicativeGroup(self.p, 1, 0)
        base = self
        while n:
            if n & 1:
                res *= base
            base *= base
            n >>= 1
        return res
    
    def __repr__(self):
        return f"({self.a}, {self.b})"

if __name__ == "__main__":
    datas = open("flag.enc","rb").read()
    order = p*p-1
    d = pow(e, -1, order)
    blocks = bytes_to_block(datas)
    plain = []
    for block in blocks:
        assert block < p**2
        a = block % p
        b = block // p
        m = MultiplicativeGroup(p, a, b)
        c = m ** d
        plain.append(c.a + c.b * p)
        
    open("flag.png", "wb").write(block_to_bytes(plain))
```
Flag.png after running solver
file ![](https://hackmd.io/_uploads/SkWATtq1T.jpg)
> COMPFEST15{find_the_order_of_group_81ee36780a}
## Knapsack **[crypto]**
We are given this following code
```python=
from collections import namedtuple
import random
from Crypto.Util.number import isPrime, GCD
from secret import message, key_size

PrivateKey = namedtuple("PrivateKey", "W q r")
PublicKey = namedtuple("PublicKey", "B")

def to_bits(m):
    _bin = lambda b: [1 if b & (1 << n) else 0 for n in range(7)]
    return sum([_bin(b) for b in m], [])


def to_bytes(bits):
    _byte = lambda b: sum([b[i] << i for i in range(7)])
    return bytes([_byte(bits[i : i + 7]) for i in range(0, len(bits), 7)])


def pad(m):
    return m + b"\x00" * (-len(m) % (key_size // 7))


def unpad(m):
    return m.rstrip(b"\x00")


def gen_private_key(key_size):
    W = []
    s = 6969

    # generate W
    for _ in range(key_size):
        w_i = random.randint(s + 1, 2 * s)
        assert w_i > sum(W)
        W.append(w_i)
        s += w_i

    # generate q
    while True:
        q = random.randint(2 * s, 32 * s)
        if isPrime(q):
            break

    # generate r
    r = random.randint(s + 1, q - 1)

    assert q > sum(W)
    assert GCD(q, r) == 1
    return PrivateKey(W, q, r)


def gen_public_key(private_key):
    B = []
    for w_i in private_key.W:
        B.append((private_key.r * w_i) % private_key.q)
    return PublicKey(B)


def encrypt(msg, public_key):
    msg_bit = to_bits(pad(msg))
    key_size = len(public_key.B)
    enc = []
    for i in range(0, len(msg_bit), key_size):
        enc.append(sum([msg_bit[i + j] * public_key.B[j] for j in range(key_size)]))
    return enc


def decrypt(enc, private_key):
    dec = []
    for c in enc:
        c_ = (c * pow(private_key.r, -1, private_key.q)) % private_key.q
        bits = []
        for w_i in reversed(private_key.W):
            if c_ >= w_i:
                bits.append(1)
                c_ -= w_i
            else:
                bits.append(0)
        dec += bits[::-1]
    return unpad(to_bytes(dec))

private_key = gen_private_key(key_size)
public_key = gen_public_key(private_key)
enc = encrypt(message, public_key)
dec = decrypt(enc, private_key)


assert dec == message

with open("output.txt", "w") as f:
    # f.write(f"B = {public_key.B}\n")
    f.write(f"enc = {enc}\n")
    f.write(f"{message[:1194].decode()}")
```
From that source code we know that the we have some secret message that encrypted with Merkle-Hellman algorithm. I guess that the Flag is some part of secret message then i just to attack the Merkle-Hellman algorithm to recover the flag. After searching i know that Merkle-Hellman is crackable using LLL-reduction algorithm then i want to apply it quickly. But, the problem is we just given the part of message and doesn't has any information about the public key and also the length of public key. So, firstly i want to recover the public key and attack the algorithm using LLL. We use this following matrix to reduce.

$$\left[\begin{array}{ccc}
2 & 0 & 0 & 0 & 0 & ... & Pk[0]\\
0 & 2 & 0 & 0 & 0 & ... & Pk[1]\\
0 & 0 & 2 & 0 & 0 & ... & Pk[2]\\
0 & 0 & 0 & 2 & 0 & ... & Pk[0]\\
... & ... & ... & ... & ... & ... & ...\\
... & ... & ... & ... & ... & 2 & Pk[n]\\
-1 & -1 & -1 & - & ... & -1 & -Enc[j]\\
\end{array}\right]$$ Return value of the reduction as my expected is $(-1/1,...,...,0)$ that point to value $(Mb[0], Mb[1], ..., ...)$. $Mb$ = bit of plaintext $(-1 = 0, 1= 1)$. $Pk$ = PublicKey.
For recovering the public key we given part of message and also the ciphertext of it. We can make some equation on following line:
$$Pk[1]{\space}*{\space}Mb[1]{\space}+{\space}Pk[2]{\space}*{\space}Mb[2]{\space}+{\space}Pk[3]{\space}*{\space}Mb[3]{\space}+{\space}...{\space}Pk[n]{\space}*{\space}Mb[n]{\space}={\space}enc[0]$$$$Pk[1]{\space}*{\space}Mb[n+1]{\space}+{\space}Pk[2]{\space}*{\space}Mb[n+2]{\space}+{\space}Pk[3]{\space}*{\space}Mb[n+3]{\space}+{\space}...{\space}Pk[n]{\space}*{\space}Mb[2*n]{\space}={\space}enc[1]$$$$...$$$$Pk[1]{\space}*{\space}Mb[(k-1)n+1]{\space}+{\space}Pk[2]{\space}*{\space}Mb[(k-1)n+2]{\space}+{\space}...{\space}Pk[n]{\space}*{\space}Mb[k*n]{\space}={\space}enc[k]$$ With value of $k$ is length ciphertext of known plaintext and $n$ is length of $publicKey$. From all of equation above we can solve it as Linear Equation System. We will use z3 solver on python to solve the system. But before it we use bruteforce for guessing the value of $n$ that is length of publicKey. We can also reduce the possibility by this following equation:
$$len(knownMessage){\space}*{\space}8{\space}<enc{\space}*{\space}len(publicKey)$$$$1094{\space}*{\space}8{\space}<{\space}177{\space}*{\space}len(publicKey)$$$$len(publicKey){\space}>{\space}50$$ Then after it we make the final script and running it.
Final Solver:
```python
from z3 import *
from sage.all import matrix


def to_bits(m):
    _bin = lambda b: [1 if b & (1 << n) else 0 for n in range(7)]
    return sum([_bin(b) for b in m], [])


def to_bytes(bits):
    _byte = lambda b: sum([b[i] << i for i in range(7)])
    return bytes([_byte(bits[i : i + 7]) for i in range(0, len(bits), 7)])


def pad(m, key_size):
    return m + b"\x00" * (-len(m) % (key_size // 7))


def unpad(m):
    return m.rstrip(b"\x00")

enc = [11777743254884910867736071000802359, 9885367164484426877141712841289221, 10856960655537648470866892845455709, 12396844046310131327328676182785384, 10293406405260841919973448808441389, 7161552265897968311561098524910942, 10615787983784797652230739276445941, 8750996343125558087794309091207648, 9793482204040387456132647801296313, 10082519515116179234452192268207537, 11320102966402368083376899357909591, 12863315726661485156488840690651082, 11531046537784628833143256540008389, 9286560942760408224853358742306869, 12279582004149322390043290795184438, 11978789745490392114224327243767043, 12084485742145391797013212600989700, 11561154470121507020306698832744599, 13178456331567650213084024227496278, 10196086379552872917716585823999514, 10601541281173337913507909606005653, 9966399811463401202257751291120170, 10250511746568637708134548840731312, 11889575565127642830776977900408626, 10933709862860216194904138708336773, 10007593807392566080878720263508671, 10843011316705174491702117107785768, 12383694531221582253577117167915563, 9894583959533524041648917678111635, 10430518900617276344682533425679992, 10018475657312899961053882989990531, 12880429380373138445215696957506949, 10918434549436697161520320355333263, 11400042022902061481939614685122963, 11171610137545211187916226582376885, 7554940907108436037367038464198228, 9695912009774929988863012317171859, 9343496763562026180227665632657363, 12025067720426566083965241093256942, 9658955369440797726004826519759606, 9428833271132121009515800913622083, 10461484260876120487748327356785073, 10940612465536330800162700132905186, 10750467235934085792526359728596178, 10678873223029328852630062166689316, 10051894872077260074152880418222275, 10497008510500960013913178933854405, 10753394290126674824447926145896263, 10374556791613714702994629355809965, 10751549259077891899074410410421186, 10497129585037258904358709726525823, 11713351721867966767470659598304708, 9750904136088440351393297931807531, 12920557770888166933984079266544430, 9835518991386093395547202596319386, 10686991553601958185529117081292430, 10817714646369847390214302366874082, 11656133992050865158028442465948881, 9710481682340951577750560981814297, 9812463701289538441884338771424591, 10553500394965127205851299497413972, 9072662701494366982448390007849710, 10626852662537677429807153297591631, 10320557743814566446047834649298263, 10229033571571432213114817752122773, 10137091208657689466011904565821444, 9671296686420595758174214901621293, 11060629505422615215479089478068342, 9363787514709375187692683504126271, 9282781227753261317425876892013992, 9792647279730520129037413182534118, 11813869200575530803652104759262730, 9161928319229922257558038982128902, 10971796325720348232207258964788834, 9768861656419748162363181789101894, 10457965191293035226753206288146876, 11507982245405850634450464634589014, 10178640325420144331673938576834887, 10422017659145361826713838891135351, 9522927671377163131409738991653604, 10718579523969523227649591475908544, 12001115266350043980981379604813238, 10578812558562041805075262672497200, 9752859065974451382515905408025221, 11167987407122180703265457924178842, 9254029087396928912823658881409506, 10883479949880070871921870163374012, 11641336159459821580913821804515986, 9831976055167640701792299948722247, 9607326851652988564446116205180982, 11804616236914324707289980227637258, 8737658801206619958913982157263731, 9321117326771341614560384118866848, 12434262148233111040506390593441974, 9094596632057590879309101458194330, 10919870415301977737620426338392154, 9973779461746337777601698299498727, 13077097958532704050556386545741606, 10169545420422687603182882727171540, 11112798322767421119202274397188040, 9686860625851189937448816700040764, 11346835372920653632886580259127170, 9653724702297290643613793338823616, 11402239574264886427635550872788202, 9164717106755214577990540059670310, 10757982233067007899585898815139468, 11491931619173132257869978491641067, 10320404508097598075786619727003330, 10360404911242274038733003166681825, 11504881273300385090543651942681693, 10050497788566704765830589138400435, 9538999475912234600157681944493317, 11563463623586812255485232857599142, 8346901243091379690454826112441168, 10985123722502869338191643390313317, 9889419332627564614605267676475582, 9859008421534798027137238190169291, 8938108903576444037425026088417415, 11680785377217252765318352340278111, 11827065014133601933199138466286954, 10313025714965640595018569900420070, 11417267430337929136320784840421909, 8422584002870384136094081858383799, 10690359392494235886344438075661972, 10471344316033728097890752546524525, 10297237481194543096768525745798780, 11852784405095354362694442350929004, 11119778357967519776338637639047638, 9866655586941191421363196046299360, 10190115156307836485874719502143486, 12043225446675699386463230372571576, 10552241846308353818649522824971213, 9988028333155853144238651873306651, 10091392579705422187015420572382039, 10005842533804787157435320367039807, 10290501388089990847500605282087698, 10260079649840625499533386845655403, 9880687720157416551773806961186599, 12012537693823600317312532902550958, 8433167876202052583892538043499959, 11291163813180795239823826678637357, 8012971667935976011881461871593275, 13055895017385493902965307298758371, 10956829329131089932931803183795475, 10648023387171769092056689660527946, 9220397506426318617705668568380205, 9871207467819032200547465506808880, 10934669185660283455617271150287701, 9467876649386646296389087904618660, 13794160913300054581464395357038327, 9079301979125027938314870240165138, 12857015710047013040592592010876990, 11760789607847616802409107522171732, 12101202285031769352939345225180229, 11479374430179263163764851706844319, 9618684327449521603105661594573419, 11368851597362018368159187165305341, 10926103543543835056336767571733674, 12113712395211988433505262029083924, 8409110871996716684073156006373610, 10634854008206235974941650718127360, 11191821014173140552936341375666462, 11734220008676991656373171468230169, 11700550791405431460693949785169775, 11027471624758702087033927626502411, 10564827560525376313267401679415349, 10127201149696841461429643317101068, 10726399432872273049792997541701017, 10991865026417715013894334993971459, 10613123876559868339081376626812279, 8265705744262621901191334665843770, 8839978095214156480555512903831932, 11523816542334107733475885455187708, 11396931014886569225447187208304949, 9148460817006566054942492973353282, 11486944793732160981882091263330869, 7987682971004889468582279658369686]
output= 'The Merkle-Hellman Knapsack Cryptosystem, developed by Ralph Merkle and Martin Hellman, is a public-key encryption algorithm known for its resistance to attacks using conventional computers. It operates on the principle of the knapsack problem, making it difficult to solve without the private key.\nIn this cryptosystem, a superincreasing knapsack is created as the public key. Each element of the knapsack is generated using a specific algorithm, ensuring that the sum of any subset of elements is unique. This property makes it challenging to deduce the original combination used to create the knapsack.\nTo encrypt a message, the plaintext is divided into binary bits and combined with the public key. This process results in a ciphertext that obscures the original message. Decrypting the ciphertext requires the knowledge of the private key, which is a set of carefully selected parameters used to generate the knapsack.\nThe security of the Merkle-Hellman Knapsack Cryptosystem relies on the complexity of solving the subset sum problem, which is considered computationally difficult. Traditional methods, such as brute-force attacks, are ineffective due to the large search space involved.'

out_bit = to_bits(output.encode())

def to_block(bit, keysize):
    ret = []
    for i in range(0, len(bit), keysize):
        ret.append(bit[i:i+keysize])
    if(len(ret[-1])!=keysize): ret = ret[:-1]
    return ret

def blockToMatrix(bloks, enc):
    mat = []
    for i in range(len(bloks)):
        rest = bloks[i]
        rest.append(enc[i])
        mat.append(rest)
    return mat

def solvingLL(pub, enc):
    mat = []
    for i in range(len(pub)):
        race = [0 for _ in range(len(pub))]
        race[i] = 2
        race.append(pub[i])
        mat.append(race)
    race = [-1 for _ in range(len(pub))]
    race.append(-enc)
    mat.append(race)
    lft = matrix(mat).LLL()
    for i in lft:
        check = True
        for j in i[:-1]:
            if(j!=-1 and j!=1):
                check = False
                break
        if(check):
            hasil = [0 if k==-1 else 1 for k in i[:-1]]
            return hasil
    return None

public = []
lastenc = 0
ksize = 0

for keySize in range(50, 1000):
    solv = Solver()
    varr = [Int('var'+str(i)) for i in range(keySize)]
    # print(varr)
    print("trying on keysize:",keySize)
    port = to_block(out_bit, keySize)
    print(len(port))
    mat = blockToMatrix(port, enc)
    for j in mat:
        func = 0
        for k in range(len(j[:-1])):
            if(j[k]==1):
                func += varr[k]
        func = func - j[-1]
        solv.add(func==0)
    for var in varr:
        solv.add(var>=0)
    print("searching solution")
    print(len(mat))
    if solv.check() == sat:
        lastenc = len(mat)
        public = []
        print("Getting solution")
        mdl = solv.model()
        for i in varr:
            public.append(mdl[i].as_long())
        print(public[0]+public[1])
        print(public)
        ksize = keySize
        break

# getting following value after running source code above, to get faster running just command source code above
# public = [202268999244591785584566464753289, 429481650350982224726540148422072, 513970026512572516113525776325248, 28117338492482390640953333703346, 186478220980676412258710097278824, 462107941653161013119183689513668, 251033680402139409627861954081627, 387816645824497842897510344498537, 164216600494137571823429526298731, 71778558816306530276260088958157, 479183011393572102154187215734921, 188237385509602029366682887942757, 164510164493942220113929554143015, 351957897387419262141620439154413, 559564023232732499808238313579215, 132277552177561352557338493209814, 178940764650522512349239779345091, 18866483643001119992915813508719, 65129268506159449846196687577134, 167844715331905178809461440317639, 187057779217236112720777337188447, 255235457949798322226331129469322, 542112960942493765359209270647723, 114204524812950672204089546427243, 53252437125337397373227709083049, 247770731648992733096183772815536, 370278558235654291454711551198693, 204891318167558074435123111701205, 346727118758224765373461446521918, 383267401562421973802058492472223, 417343176245713459916869587269198, 402777959260550580969689993776134, 256182522762462197049974160316147, 12633876948394660593286916001351, 314759385142403326392078244802058, 263384123584500671540118032288133, 38650772398307717379496756747526, 306247616243715512386307466259185, 18476145088228804143170873686713, 117347060602402743026648839960226, 532135003875215013523423589192213, 48852380988000293381223282153612, 392265050642625455268293151465954, 476031692087751937639870601144694, 313629857431146857184719536982718, 401494270926634643978458681547252, 469566569599947139086905470473261, 35198969362337022791203043174857, 302866869873710554417387933256528, 568209519326416227422311570586209, 346972863738514859986014024700232, 228494377266333974424439321983382, 273276775819051056644733188417501, 375104893892478155799313329948322, 505185562312989073710257542456512, 487212675194015362273133188431578, 482365437683654780746878113745270, 431965489061014360035520964200531, 408258373241369446583858893702995, 470031010237858817507296825649368, 257013818984828645730461138618543, 54947842649555394131632787661575, 515120777972555489955179566266686, 221636118181162865445925087810764, 261861417553256248241702651575262, 292909311966757680411011941074079, 574982374582780447492152113881912, 558873603864855849876018552771791, 297055452910545368134149122366836, 10217264473676430559765113019100]
# lastenc = 119
# ksize = 70
# mess = output.encode()
mess = b''
otp = []
for i in range(lastenc,len(enc)):
    halo = solvingLL(public, enc[i])
    if(halo==None): 
        otp += [0 for _ in range(70)]
        print("failed on i",i)
    elif(i%20==0): print("Success until i:",i)
    else:otp+=halo
adding = to_bytes(otp[:len(otp)-len(otp)%7])
mess += adding
print(mess)
```
![](https://hackmd.io/_uploads/rk4ZUcc1a.jpg)
> COMPFEST15{D4ngerr_LLL_1s_Ev3ryWh3r3_ed2c699bb3}

## Crypto Vault **[crypto]**
We are given this following code
```python
#!/bin/env python3
from flask import Flask, jsonify, request, render_template
import ecdsa
import ecdsa.ellipticcurve as EC
from flask_cors import CORS
import binascii
import ecdsa.util
app = Flask(__name__)
CORS(app)

curve = ecdsa.SECP256k1
G = curve.generator
n = G.order()
x = int('ce205d44c14517ba33f3ef313e404537854d494e28fcf71615e5f51c9a459f42', 16)
y = int('6080e22d9a44a5ce38741f8994ac3a14a6760f06dd1510b89b6907dfd5932868', 16)
Q = EC.Point(curve.curve, x, y)
PUBKEY = ecdsa.VerifyingKey.from_public_point(Q, curve)

# Convert the public key to standard format
PUBKEY_str = binascii.hexlify(PUBKEY.to_string()).decode()

@app.route('/')
def home():
    return render_template('index.html')

@app.route('/verify_signature', methods=['POST'])
def verify_signature():
    data = request.get_json()
    signature_hex = data['signature']
    message_hash = int(data['message_hash'], 16)
    # print(message_hash)
        # Convert the signature from standard format
    signature_bin = binascii.unhexlify(signature_hex)
    r = int.from_bytes(signature_bin[:32], 'big')
    s = int.from_bytes(signature_bin[32:], 'big')
    sig = ecdsa.ecdsa.Signature(r, s)
    
    result = verify_ecdsa_signature(sig, message_hash)
    
    response = {'result': result, 'pubkey': PUBKEY_str}
    return jsonify(response)

def verify_ecdsa_signature(sig, message_hash):
    m = message_hash
    if PUBKEY.pubkey.verifies(m, sig):
        return "this is the flag"
    else:
        return "skill issue ( ͡° ͜ʖ ͡°)"

if __name__ == '__main__':
    app.run(host="0.0.0.0", port=1984)
```
we given some service that just verifying ECDSA signature from input value $(r,s)$ and $hash(message)$. The source code doesn't has some filtering input then i think we can bypass the source code because we getting information about the $curve$ and also $publicKey$. The following line is formula on ECDSA Verify Signature: 
$$R'{\space}={\space}(h{\space}*{\space}s^{-1}){\space}*{\space}G{\space}+{\space}(r{\space}*{\space}s^{-1}){\space}*{\space}publicKey$$$$R'{\space}={\space}eq1{\space}+{\space}eq2$$$$eq1{\space}={\space}(h{\space}*{\space}s^{-1}){\space}*{\space}G{\space}$$$$eq2{\space}={\space}(r{\space}*{\space}s^{-1}){\space}*{\space}publicKey$$ we can make $eq1$ is equal to $(0,1)$ if $h$ = $curve.order()$. Then if we input $r$ = $s$ output is same as $publicKey$. $$eq2{\space}={\space}(r{\space}*{\space}r^{-1}){\space}*{\space}publicKey$$$$eq2{\space}={\space}1{\space}*{\space}publicKey$$ So we can input $r$ = $publicKey.x$ to satisfy $eq2$.
Solver:
```python
import ecdsa
import ecdsa.ellipticcurve as EC
import ecdsa.util

curve = ecdsa.SECP256k1
G = curve.generator
n = G.order()
x = int('ce205d44c14517ba33f3ef313e404537854d494e28fcf71615e5f51c9a459f42', 16)
y = int('6080e22d9a44a5ce38741f8994ac3a14a6760f06dd1510b89b6907dfd5932868', 16)
Q = EC.Point(curve.curve, x, y)
PUBKEY = ecdsa.VerifyingKey.from_public_point(Q, curve)
# print(n)

import requests
import binascii

# print(PUBKEY.pubkey)

# signature_hex = data['signature']
    # message_hash = int(data['message_hash'], 16)
m = n
r,s = x,1
sig = ecdsa.ecdsa.Signature(r, r)
hasil = PUBKEY.pubkey.verifies(m, sig)
assert hasil==True

rr = r.to_bytes(32, "big")
ss = r.to_bytes(32, "big")
sgrs = binascii.hexlify(rr+ss).decode()
mh = hex(m)[2:]

data = {"signature":sgrs,"message_hash":mh}
x = requests.post("http://127.0.0.1:1984/verify_signature",headers={"Content-Type": "application/json"},json=data)
print(x.content)
# print(hasil)
```
> I didn't get the flag because the Competition was over, the solver above is for local service