---
# System prepended metadata

title: PearlCTF
tags: [Writeup_CTF]

---

# PearlCTF
### Baby Message Out

```python3=
#!/usr/bin/env python3
from Crypto.Util.number import getPrime, isPrime, bytes_to_long
from flag import flag
from math import log
import sys

n = pow(10, 5)
sys.setrecursionlimit(n)

def nextPrime(p):
    if isPrime(p):
        return p
    else:
        return nextPrime(p + 61)

p = getPrime(256)
q = nextPrime(nextPrime(17*p + 1) + 3)
r = nextPrime(29*p*q)
s = nextPrime(q*r + p)
t = nextPrime(r*s*q)

print(f"{p = }")
print(f"{q = }")
print(f"{r = }")
print(f"{s = }")
print(f"{t = }")
n = p*q*r*s*t
e = 65537
m = bytes_to_long(flag)
c = pow(m, e, n)
print(f"c: {c}")
print(f"n: {n}")

```

```python3=
c: 1838979268925267202615818395622122484893202706167722156383681963142079180161531237936585341507802530813847301831886857107840552940103096671213346624172927405178565892385733216702672851783423891229382579221604596673156151519463795808010572632138076246765227545034269340790242411584582692558549837221897037971538915332866933837117883228953143242965510280921618063635225603250391037969273250144174802927111408106890496901519737395485115788436384012937788617630381389196078818501905993385084557887716148781944416313908553565974546239241287340505018036547080504009483360838747251073440799502791555942879229132369697126614944991347408598924472158561247485575276852356316968119522601927483600978106155875300324966339458896992260918634629607740522754923275555209240690069838566642421234035117257380757518902549668151860260985363114235340791362277047923494577018035035653075155240898800910887230020812289524262582877697129108304758256250595391379019020972909027192107727799235384374670082518555887502478560056865020860127002
n: 485889862688353809145356849681869098056742135790325154633816315764783289109350404808839464043297429168130293002483316664607770068196944445394490388957147529192164743228508084885030734914365245916125607677185307000076761646830755541107131137865984020648243428286711853728051263128076845261298240617545417811431124904304446607845470628505361436115651109768237103000946120781642652665829238385087624612205525857191218811570122228639908283598707567056620352822139985271817003262919597858495812801663331842855143930997258260164422858382713240717739568744168349527025412634588705503126870149875268600539407786323924132037483972529603572727892740460450963315253482630345996561505957763886776070413535287129043703855880444326909520084249767518737040222995586717034060176093473694948961503759770499124924886087468497100053775745634680192678426965540847613838559522315570932873854027804486662281273550134766570419630624914801192395060568099367828616501877701994364906741451132770474154689411119296104818089636891131318495955929

```

Ta thấy rằng, có một hàm sinh số nguyên tố như sau:
```python3=
def nextPrime(p):
    if isPrime(p):
        return p
    else:
        return nextPrime(p + 61)
```

Và có 5 số nguyên tố được sinh ra
```python3=
p = getPrime(256)
q = nextPrime(nextPrime(17*p + 1) + 3)
r = nextPrime(29*p*q)
s = nextPrime(q*r + p)
t = nextPrime(r*s*q)
```

Ta thử chạy một code mới và thu được như sau (mình tự chạy với số p khác bài)
```python3=
p =               (p)
q =        (3 + 17*p + 1037*k1)
r = (29*p*(3 + 17*p + 1037*k1) + 61*k2)
s = ((29*p*(3 + 17*p + 1037*k1) + 61*k2)*(3 + 17*p + 1037*k1) + p + 61*k3)
t = ((29*p*(3 + 17*p + 1037*k1) + 61*k2)*(3 + 17*p + 1037*k1)*((29*p*(3 + 17*p + 1037*k1) + 61*k2)*(3 + 17*p + 1037*k1) + p + 61*k3) + 61*k4)

```

Từ việc khai triển trên, ta thấy được rằng
$$q \approx 17p$$
$$r \approx 29pq \approx 29 \cdot 17p^2$$
$$s \approx qr \approx 17p \cdot 29 \cdot17 p^2 \approx 29 \cdot17^2 p^3 $$
$$t\approx rsq \approx 29 \cdot 17p^2 \cdot 29 \cdot 17^2 p^3 \cdot 17p = 29^2\cdot17^4p^6$$
$$n = pqrst \approx 29^4\cdot17^8p^{13}$$

Từ đó, ta có thể tìm được giá trị sấp xỉ của p bằng cách 
$$p \approx \sqrt[13]{\dfrac{n}{29^4\cdot17^8}}$$

Sau đó ta sẽ bruteforce p, sao cho giá trị p là nghiệm của N.

```python3=
from Crypto.Util.number import isPrime, long_to_bytes
from gmpy2 import next_prime, iroot
import sys

n = pow(10, 5)
sys.setrecursionlimit(n)

def nextPrime(p):
    if isPrime(p):
        return p
    else:
        return nextPrime(p+61)

n = 485889862688353809145356849681869098056742135790325154633816315764783289109350404808839464043297429168130293002483316664607770068196944445394490388957147529192164743228508084885030734914365245916125607677185307000076761646830755541107131137865984020648243428286711853728051263128076845261298240617545417811431124904304446607845470628505361436115651109768237103000946120781642652665829238385087624612205525857191218811570122228639908283598707567056620352822139985271817003262919597858495812801663331842855143930997258260164422858382713240717739568744168349527025412634588705503126870149875268600539407786323924132037483972529603572727892740460450963315253482630345996561505957763886776070413535287129043703855880444326909520084249767518737040222995586717034060176093473694948961503759770499124924886087468497100053775745634680192678426965540847613838559522315570932873854027804486662281273550134766570419630624914801192395060568099367828616501877701994364906741451132770474154689411119296104818089636891131318495955929
p_approx = iroot(n//(pow(17,8)*pow(29,4)),13)[0]
print(p_approx)
for i in range(p_approx - 10000, p_approx + 10000):
    if n % i == 0:
        p = i
        break
print(p)
q = nextPrime(nextPrime(17*p+1)+3)
r = nextPrime(29*p*q)
s = nextPrime(q*r+p)
t = nextPrime(r*s*q)

phi = (p-1)*(q-1)*(r-1)*(s-1)*(t-1)
e = 65537
d = pow(e,-1,phi)
c = 1838979268925267202615818395622122484893202706167722156383681963142079180161531237936585341507802530813847301831886857107840552940103096671213346624172927405178565892385733216702672851783423891229382579221604596673156151519463795808010572632138076246765227545034269340790242411584582692558549837221897037971538915332866933837117883228953143242965510280921618063635225603250391037969273250144174802927111408106890496901519737395485115788436384012937788617630381389196078818501905993385084557887716148781944416313908553565974546239241287340505018036547080504009483360838747251073440799502791555942879229132369697126614944991347408598924472158561247485575276852356316968119522601927483600978106155875300324966339458896992260918634629607740522754923275555209240690069838566642421234035117257380757518902549668151860260985363114235340791362277047923494577018035035653075155240898800910887230020812289524262582877697129108304758256250595391379019020972909027192107727799235384374670082518555887502478560056865020860127002
m = pow(c,d,n)

print(long_to_bytes(m))
```

**Flag: pearl{7h1s_0n3_w4s_f0r_7h3_k1ds}**

### Security ++ 
Secure.py
```python3=
flag = b'PearlCTF{day-la-flag}'
key = b'a'*8 + b'b'*8 + b'c'*8 + b'd'*8
from base64 import b64encode
from enc import encrypt_block, pad


def encrypt(data: bytes):
    pt = data + flag
    pt = pad(pt)
    block_count = len(pt) // 16
    encText = b''
    for i in range(block_count):
        if i % 2 == 0:
            encText += encrypt_block(pt[i * 16:i * 16 + 16], key[0:16])
        else:
            encText += encrypt_block(pt[i * 16:i * 16 + 16], key[16:])
    return encText


def main():
    while 1:
        msg = input("\nEnter plaintext: ").strip()
        res = encrypt(msg.encode())
        print(b64encode(res).decode())


if __name__ == '__main__':
    main()
```
enc.py
```python3=
from copy import copy

s_box = (
    0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
    0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
    0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
    0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
    0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
    0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
    0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
    0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
    0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
    0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
    0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
    0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
    0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
    0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
    0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
    0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16,
)


def bytes2matrix(text):
    return [list(text[i:i + 4]) for i in range(0, len(text), 4)]


def matrix2bytes(m):
    plaintext = b''
    for row in range(4):
        for j in range(4):
            plaintext += chr(m[row][j]).encode('latin-1')
    return plaintext


def pad(text: bytes):
    if len(text) % 16 != 0:
        pad_len = ((len(text) // 16) + 1) * 16 - len(text)
    else:
        pad_len = 0
    text = text.ljust(len(text) + pad_len, b'.')
    return text


def sub_bytes(state, sbox):
    for i in range(len(state)):
        for j in range(4):
            state[i][j] = sbox[state[i][j]]
    return state


def shift_rows(s):
    s[1][0], s[1][1], s[1][2], s[1][3] = s[1][1], s[1][2], s[1][3], s[1][0]
    s[2][0], s[2][1], s[2][2], s[2][3] = s[2][2], s[2][3], s[2][0], s[2][1]
    s[3][0], s[3][1], s[3][2], s[3][3] = s[3][3], s[3][0], s[3][1], s[3][2]
    return s


def gmul(a, b):
    if b == 1:
        return a
    tmp = (a << 1) & 0xff
    if b == 2:
        return tmp if a < 128 else tmp ^ 0x1b
    if b == 3:
        return gmul(a, 2) ^ a


def mix_col(col):
    temp = copy(col)
    col[0] = gmul(temp[0], 2) ^ gmul(temp[1], 3) ^ gmul(
        temp[2], 1) ^ gmul(temp[3], 1)
    col[1] = gmul(temp[0], 1) ^ gmul(temp[1], 2) ^ gmul(
        temp[2], 3) ^ gmul(temp[3], 1)
    col[2] = gmul(temp[0], 1) ^ gmul(temp[1], 1) ^ gmul(
        temp[2], 2) ^ gmul(temp[3], 3)
    col[3] = gmul(temp[0], 3) ^ gmul(temp[1], 1) ^ gmul(
        temp[2], 1) ^ gmul(temp[3], 2)
    return col


def add_round_key(state, round_key):
    for i in range(len(state)):
        for j in range(len(state[0])):
            state[i][j] = state[i][j] ^ round_key[i][j]
    return state


def encrypt_block(ptext: bytes, key):
    cipher = ptext
    rkey = bytes2matrix(key)

    for i in range(10):
        ctext = bytes2matrix(cipher)
        ctext = sub_bytes(ctext, s_box)
        ctext = shift_rows(ctext)
        temp = copy(ctext)
        for j in range(4):
            column = [temp[0][j], temp[1][j], temp[2][j], temp[3][j]]
            column = mix_col(column)
            for k in range(4):
                ctext[k][j] = column[k]
        ctext = add_round_key(ctext, rkey)
        cipher = matrix2bytes(ctext)
    return cipher
```

Solve.py
```python3=
#!/usr/bin/python3 
from pwn import*
from base64 import*
from string import printable

io = process(["python3", "secure.py"])
brt_char = b''
payload_1 = b'a'*16
flag = b''
for i in range(40):
    payload_1 = payload_1[1:] + brt_char
    for j in printable:
        payload = payload_1 + j.encode() + b'a'*(31-i)
        io.recvuntil(b'Enter plaintext: ')
        io.sendline(payload)
        data = io.recvuntil(b'\n',drop=True).decode()
        data = b64decode(data)
        if data[:16] == data[32:48]:
            flag += j.encode()
            brt_char = j.encode()
            print(flag)
            break

```

### 3 spies
```python3=
#!/usr/bin/env python3

from Crypto.Util.number import getPrime, bytes_to_long

with open('flag.txt', 'rb') as f:
    flag = f.read()


n1 = getPrime(512)*getPrime(512)
n2 = getPrime(512)*getPrime(512)
n3 = getPrime(512)*getPrime(512)

e=3

m = bytes_to_long(flag)

c1 = pow(m,e,n1)
c2 = pow(m,e,n2)
c3 = pow(m,e,n3)
    
with open('encrypted-messages.txt', 'w') as f:
    f.write(f'n1: {n1}\n')
    f.write(f'e: {e}\n')
    f.write(f'c1: {c1}\n\n')
    f.write(f'n2: {n2}\n')
    f.write(f'e: {e}\n')
    f.write(f'c2: {c2}\n\n')
    f.write(f'n3: {n3}\n')
    f.write(f'e: {e}\n')
    f.write(f'c3: {c3}\n')

n1: 125267411676839013904356880992044234494446196964982422223130579882047339346910691451497681975351838034684254305738613386927222900898672184001345811471784343779083336010063097729870079645284178978512325038316112509718505547104307526489798594871208559607331790920412305711830820739308995357441030646151241475357
e: 3
c1: 53377681151597930200174280269480737905892580547675095951568028531545776989476273786562435486230550919422086944133253611872983670236114054374565938184593173194919064517779661178744278071496565181181705071524501841159717567250259220092464925447795412484629687708208662079791459184303259833667333882817260906165

n2: 101985110329687359982214188967281711679876126442294375297547334583432698756724057183438691227371260175904715854057793173086301783390154807726779286131084537704721881438398569476214173211311977143694032174701007005033830070482491565424683664984059187439768982994371382763048098663670188786016786612348042190633
e: 3
c2: 86370003324603283962938004647941072863866893771153362222202759619566185050496089684606274416415418388916028237984708280964054009059814813483639010674182298294505525549842057730933691736372086557397211586739691237738757897947336698446258197604918828646265244195686107866422922575275382813594250335044143485624

n3: 83259448903366278561128205003734328779222118906091604625605804813528274055482582431201682767294594942491788720967344243567819654813240542076250030802111361571504667752481579915864184180358691091092122509649590043074189547962292835856503625214027405901620103615424259796442446412031011575671410630232956892267
e: 3
c3: 25601241268900087228853235319569275926328919786631787991019848828558430219449358810095537362492238844266084660904521793373698736119824512458196492049138821633273765102576368573691391116632126183996786969554104441242376959688329346567745607825277943462236901478944551669406261301309719409165457168678763092118

```

```python3=
from Crypto.Util.number import long_to_bytes
from functools import reduce
from gmpy2 import iroot

n1 = 125267411676839013904356880992044234494446196964982422223130579882047339346910691451497681975351838034684254305738613386927222900898672184001345811471784343779083336010063097729870079645284178978512325038316112509718505547104307526489798594871208559607331790920412305711830820739308995357441030646151241475357
e = 3
c1 = 53377681151597930200174280269480737905892580547675095951568028531545776989476273786562435486230550919422086944133253611872983670236114054374565938184593173194919064517779661178744278071496565181181705071524501841159717567250259220092464925447795412484629687708208662079791459184303259833667333882817260906165

n2= 101985110329687359982214188967281711679876126442294375297547334583432698756724057183438691227371260175904715854057793173086301783390154807726779286131084537704721881438398569476214173211311977143694032174701007005033830070482491565424683664984059187439768982994371382763048098663670188786016786612348042190633
e=  3
c2= 86370003324603283962938004647941072863866893771153362222202759619566185050496089684606274416415418388916028237984708280964054009059814813483639010674182298294505525549842057730933691736372086557397211586739691237738757897947336698446258197604918828646265244195686107866422922575275382813594250335044143485624

n3 = 83259448903366278561128205003734328779222118906091604625605804813528274055482582431201682767294594942491788720967344243567819654813240542076250030802111361571504667752481579915864184180358691091092122509649590043074189547962292835856503625214027405901620103615424259796442446412031011575671410630232956892267
e =  3
c3 =  25601241268900087228853235319569275926328919786631787991019848828558430219449358810095537362492238844266084660904521793373698736119824512458196492049138821633273765102576368573691391116632126183996786969554104441242376959688329346567745607825277943462236901478944551669406261301309719409165457168678763092118

def chinese_remainder(n, a):
    sum = 0
    prod = reduce(lambda a, b: a*b, n)
    for n_i, a_i in zip(n, a):
        p = prod // n_i
        sum += a_i * mul_inv(p, n_i) * p
    return sum % prod

def mul_inv(a, b):
    b0 = b
    x0, x1 = 0, 1
    if b == 1: return 1
    while a > 1:
        q = a // b
        a, b = b, a%b
        x0, x1 = x1 - q * x0, x0
    if x1 < 0: x1 += b0
    return x1

m=chinese_remainder([n1,n2,n3],[c1,c2,c3])
flag = iroot(m,3)
print(flag)
```

