Cryptography

Challenge
Link

number (247 pts)

encryptor (409 pts)🥇

valorand (490 pts)

Regulus (490 pts)

see are see(500 pts)🥇

prospero (500 pts)🥇

number (247 pts)

Description

-

Solution

Diberikan soal sebagai berikut

flag = "itf{REDACTED}"

def obfuscate(flag):
    diction = {0:1,1:2,2:3,3:4,4:5}
    flag = flag[::-1]
    temp = []
    temp.append(ord(flag[0]))
    for i in range(1, len(flag)):
        temp.append(ord(flag[i]) ^ temp[-1])
    res = []
    for i in temp:
        res.append(i + diction[i % 5])
    return res

print(obfuscate(flag))

Karena kita tahu nilai akhir dari flag yaitu } jadi kita bisa manfaatkan hal tersebut untuk bruteforce per byte. Dengan algoritma yang sama kita bisa bruteforce dan bandingkan dengan hasil enkripsi. Berikut solver yang kami gunakan

import string

res = [126, 39, 126, 32, 118, 47, 33, 124, 41, 77, 37, 128, 30, 113, 93, 58, 17, 121, 28, 120, 45, 87, 35, 66, 119, 14, 113, 30, 119]
diction = {0:1,1:2,2:3,3:4,4:5}

known = ord("}")
temp = [known]
flag = "}"
while len(temp)!=len(res):
	for i in string.printable[:-6]:
		tmp = ord(i)^temp[-1]
		tmp2 = tmp + diction[tmp % 5]
		try:
			if(tmp2==res[len(temp)]):
				temp.append(tmp)
				flag += i
		except Exception as e:
			break
print(flag[::-1])

Flag : itf{3asy_obu5c4te_ha_h4_ha__}

encryptor (409 pts)

Description

-

Solution

Diberikan soal sebagai berikut

from pwn import xor

def banner():
    print("""=======================================
welcome to message encryptor/decryptor
=======================================
choose one of the following options:
\033[33m
1.) encrypt
2.) decrypt\033[37m
=======================================""")

def enc(flag):
    enc = b''
    enc2 = b''
    for i in range(len(flag)):
        enc += xor(flag[i].encode(), i)
    for i in range(int(len(flag)/2)):
        enc2 += chr(enc[i]).encode() if i % 5 == 5 else chr(enc[i] - 1).encode()
        enc2 += chr(enc[eval(f'-{i+1}')]).encode()
    open('enc_message.txt', 'wb').write(enc2)


def dec(enc):
    print("Accidently delete the decryption function, can you help me to fix it?")
    

def main():
    banner()
    while True:
        try:
            choice = int(input("choose method \033[33m>>>\033[39m "))
            if int(choice) == 1:
                msg = input("enter the message: ")
                if len(msg)%2 != 0:
                    print("encrypted message should be even!")
                    continue
                enc(msg)
                print("message encrypted successfully!")
                exit("=======================================")
            else:
                encrypted = open('enc_message.txt', 'rb').read()
                dec(encrypted)
                exit("=======================================")
        except ValueError:
            print("invalid input, please input number above")
            continue
        except KeyboardInterrupt:
            print("\nbye")
            exit("=======================================")

if __name__ == "__main__":
    main()

Kita bisa mendapatkan nilai flag dengan mengembalikan index flag kembali seperti awal (index setelah di encrypt adalah index 0 , index -1, dst ) . Kemudian tinggal +1 xor dengan index untuk nilai pada index 0 - len(flag)//2 dan xor dengan index saja untuk len(flag)//2 - len(flag). Berikut solver yang kami gunakan

enc = "hXtVcFwK6QcPt~}vVBiha~i}_F`vP#9u~JMaj|"

pref = ""
suff = ""
for i in range(0,len(enc),2):
	pref += enc[i]
	suff += enc[i+1]
dec = pref + suff[::-1]
flag = ""

for i in range(len(dec)//2):
	flag += chr((ord(dec[i])+1)^i)
for i in range(len(dec)//2,len(dec)):
	flag += chr((ord(dec[i]))^i)
print(flag)

Flag : itf{3asy_chall_5o_you_c4n_get_happier}

valorand (490 pts)

Description

-

Solution

Diberikan soal sebagai berikut

#!/usr/bin/python3
from Crypto.Util.number import *
import random, os, base64
from libnum import grey_code
from valo import agents, maps, flag

LEN = 32
SECRET = os.urandom(LEN//2)

def next_prime(x):
    y = x | 1
    while not isPrime(y) or y == x:
        y += 2
    return y

def they_are_so_dead(x):
    a,b,c = x[0],x[1],x[2]
    for i in range((LEN << 11)+1):
        a,b,c = list(map(grey_code, [a,b,c]))
    return [a,b,c]

def invitation_code(x):
    z = random.getrandbits(x * 8)
    # print(z)
    rand = z.to_bytes(x, 'little')
    return rand.hex()

class RANDOM:
    def __init__(self, seed, m):
        self.seed = seed
        self.m = next_prime(m)
        self.a = random.randint(1, m-1)
        self.c = random.randint(1, m-1)

    def next(self):
        self.seed = (self.seed * self.a + self.c) % self.m
        return self.seed

def encrypt(secret,mod):
    seed,mod = bytes_to_long(secret),int(mod[:LEN],16)
    prng = RANDOM(seed, mod)
    l_rand = they_are_so_dead([prng.next() for _ in range(3)])
    return base64.b64encode(b' >//< '.join([long_to_bytes(i) for i in l_rand])).decode()

def banner():
    print("\n" + "="*15 + " Hallo Pemain Satu " + "="*15)
    print("\n1) Get Invitation Code")
    print("2) Encrypted Secret")
    print("3) Guessing for Flag")
    print("4) I Don't Care\n")

while True:
    try:
        banner()
        choose = int(input("> "))
        if choose == 1:
            print("Hey mike, Let's play Valorand\n")
            print(f"Here's my Invitation Code: {invitation_code(LEN)} ")
        elif choose == 2:
            print(encrypt(SECRET, invitation_code(LEN)))
        elif choose == 3:
            print(SECRET)
            awikwok = bytes_to_long(SECRET)
            print(awikwok)
            idx = awikwok % len(agents)
            print("\nAs a friend, I'll test you to guess my favourite Agent & Map. Would ya?")
            ga = input("Your guess (agent): ")
            if (ga == agents[idx]):
                idx = (awikwok//(idx+1)) % len(maps)
                gm = input("Your guess (map): ")
                if (gm == maps[idx]):
                    print("No way, I bet you can't leaked my Secret haha")
                    gs = input("Secret (in hex): ").strip()
                    if (bytes.fromhex(gs) == SECRET):
                        print(f"Well, I think you deserve this {flag}")
                        exit()
                    else:
                        print("Ohhh damnn so close")
                        exit()
                else:
                    print("Wrong!")
                    exit()
            else:
                print("Wrong!, pffftt ur not my friend")
                exit()
        elif choose == 4:
            print("Understandable, Have a Great Day")
            exit()
        else:
            print("You choose violence huh?!\n")
    except:
        break

Untuk nilai getrandbits kita bisa predict, kita hanya perlu mendapatkan 624*32 bits number untuk dapat memprediksi nilai selanjutnya. Untuk grey_code bisa di reverse dengan function dari libnum yaitu rev_grey_code. Untuk class RANDOM merupakan lcg, jadi bisa dicrack untuk nilai nilai multiplier dan incrementnya dengan minimal 3 states yang diketahui. Selanjutnya kita perlu mendapatkan state awal, bisa dengan melakukan modular inverse terhadap nilai multiplier lalu dikalikan dengan state dan disubtract dengan nilai increment (https://stackoverflow.com/questions/2911432/reversible-pseudo-random-sequence-generator) . Setelah dapat nilai secret tinggal lakukan hal yang sama seperti pada soal dan dapat flag. Berikut solver yang kami gunakan

from pwn import *
import base64
from Crypto.Util.number import *
from randcrack import RandCrack
from libnum import rev_grey_code
from valo import agents, maps, flag

LEN = 32

def int_from_bytes(xbytes: bytes) -> int:
    return int.from_bytes(xbytes, 'little')

def decrypt(ct,mod):
    seed,mod = bytes_to_long(secret),int(mod[:LEN],16)
    prng = RANDOM(seed, mod)
    l_rand = they_are_so_dead([prng.next() for _ in range(3)])
    return base64.b64encode(b' >//< '.join([long_to_bytes(i) for i in l_rand])).decode()

def rev_they_are_so_dead(x):
    a,b,c = x[0],x[1],x[2]
    for i in range((LEN << 11)+1):
        a,b,c = list(map(rev_grey_code, [a,b,c]))
    return [a,b,c]

def gcdExtended(a, b): 
    if a == 0 :  
        return b,0,1
    gcd,x1,y1 = gcdExtended(b%a, a) 
    x = y1 - (b//a) * x1 
    y = x1 
    return gcd,x,y
      
def modinv(b, n):
    g, x, _ = gcdExtended(b, n)
    if g == 1:
        return x % n

def crack_unknown_increment(states, modulus, multiplier):
    increment = (states[1] - states[0]*multiplier) % modulus
    return modulus, multiplier, increment

def crack_unknown_multiplier(states, modulus):
    multiplier = (states[2] - states[1]) * modinv(states[1] - states[0], modulus) % modulus
    return crack_unknown_increment(states, modulus, multiplier)

def next_prime(x):
    y = x | 1
    while not isPrime(y) or y == x:
        y += 2
    return y

class Rnd:
	
	def __init__(self,state,mod,inc,mul):
		self.x = state
		self.M = mod
		self.A = mul
		self.C = inc
	
	def prev(self):
		ainverse = gcdExtended(self.A, self.M)[1]
		self.x = (ainverse * ((self.x) - self.C)) % self.M
		return self.x;

rc = RandCrack()
# r = process("./chall.py")
r = remote("15.235.143.42",24480)
r.recvuntil(b"> ")
for i in range(78):
	r.sendline(b"1")
	r.recvuntil(b"Code: ")
	tmp = int_from_bytes(bytes.fromhex(r.recvline().strip().decode()))
	while tmp > 0:
		rc.submit(tmp % (1 << 32))
		tmp = tmp >> 32
r.recvuntil(b"> ")
r.sendline(b"2")
tmp = base64.b64decode(r.recvline().strip().decode()).split(b' >//< ')
l_rand = [bytes_to_long(i) for i in tmp]
list_prng = rev_they_are_so_dead(l_rand)
z = rc.predict_randrange(0, 115792089237316195423570985008687907853269984665640564039457584007913129639935)
rand = z.to_bytes(LEN, 'little')
mod = rand.hex()
mod = next_prime(int(mod[:LEN],16))
mod, mul, inc = crack_unknown_multiplier(list_prng,mod)
rnd = Rnd(list_prng[0],mod,inc,mul)
SECRET = rnd.prev()
awikwok = SECRET
idx = awikwok % len(agents)
r.recvuntil(b"> ")
r.sendline(b"3")
r.recvuntil(b"(agent): ")
r.sendline(agents[idx].encode())
idx = (awikwok//(idx+1)) % len(maps)
r.recvuntil(b"(map): ")
r.sendline(maps[idx].encode())
r.recvuntil(b"(in hex): ")
r.sendline(long_to_bytes(SECRET).hex().encode())
r.interactive()

Flag : itf{s00000_M4nYyy_Vu1n3r4b1LyTYY_iN_r44nnD00mNe5555}

Regulus (490 pts)

Description

-

Solution

Diberikan soal sebagai berikut

#!/usr/bin/env python3
from Crypto.Util.number import *

FLAG = open('flag.txt', 'rb').read()


def gen_key(e):
    while True:
        p = getPrime(1024)
        q = getPrime(1024)

        if GCD(e, p - 1) != 1:
            n = p * q
            x = (p | q) & (~p | ~q)

            return x, n


e = 17
x, n = gen_key(e)

m = bytes_to_long(FLAG)
c = pow(m, e, n)

print(f"e = {e}")
print(f"x = {x}")
print(f"n = {n}")
print(f"c = {c}")

x adalah p^q , jadi kami menggunakan solver berikut untuk mendapatkan p dan q https://github.com/sliedes/xor_factor/blob/master/xor_factor.py . Selanjutnya karena gcd(e,p-1) != 1 maka seharusnya public exponent menjadi invalid (tidak bisa langsung didapatkan plaintext dengan rsa biasa). Selanjutnya kami cek apakah q-1 relatif prima dengan e atau tidak ternyata iya. Kami coba asumsikan bahwa nilai plaintext < q , jika iya maka cukup menggunakan q saja kita bisa mendapatkan plaintext dan ternyata bisa. Berikut solver yang kami gunakan

#!/usr/bin/env python3

import math
import sys
from Crypto.Util.number import *

def check_cong(k, p, q, n, xored=None):
    kmask = (1 << k) - 1
    p &= kmask
    q &= kmask
    n &= kmask
    pqm = (p*q) & kmask
    return pqm == n and (xored is None or (p^q) == (xored & kmask))

def extend(k, a):
    kbit = 1 << (k-1)
    assert a < kbit
    yield a
    yield a | kbit

def factor(n, p_xor_q):
    tracked = set([(p, q) for p in [0, 1] for q in [0, 1]
                   if check_cong(1, p, q, n, p_xor_q)])

    PRIME_BITS = int(math.ceil(math.log(n, 2)/2))

    maxtracked = len(tracked)
    for k in range(2, PRIME_BITS+1):
        newset = set()
        for tp, tq in tracked:
            for newp_ in extend(k, tp):
                for newq_ in extend(k, tq):
                    # Remove symmetry
                    newp, newq = sorted([newp_, newq_])
                    if check_cong(k, newp, newq, n, p_xor_q):
                        newset.add((newp, newq))

        tracked = newset
        if len(tracked) > maxtracked:
            maxtracked = len(tracked)
    print('Tracked set size: {} (max={})'.format(len(tracked), maxtracked))

    # go through the tracked set and pick the correct (p, q)
    for p, q in tracked:
        if p != 1 and p*q == n:
            return p, q

    assert False, 'factors were not in tracked set. Is your p^q correct?'

def main():
    n = 22451551366816023348447360202706900510830228629535815632658578221314261064933474109636193729071628012788940344452133851852017366927912454304020151569077485428297287005092834399390331099292171142378091606316748297786818433520789846641672149800540227266293454818259245555037809687786408161265527128130286378132948469437045923528803839460472658089176281722635258117945691238035953639144567185393378025198781483772862757035177291776200594778755058897094464206604922106079336841950344777388210700915854307396511199737299628895177354590685847912303822981430221023684494499862378536004374159141470325183538271583329616455757
    p_xor_q = 13325746550403466212714844471707212937581458917071528889507316337472192514813105181935336789043645998333195565571097932091147031399645852915634658166184147103093354018188111637363471637779171476786034005445587323443827018127885697909742606323087732854102560044520839819469235327883849488746246247697743171692

    e = 17
    p, q = factor(n, p_xor_q)
    phi_q = q-1
    assert(math.gcd(e,q-1) == 1)
    assert(p*q == n)
    d = inverse(e,phi_q)
    c = 9047109186695716774493044743719567175241644184089507442584534823490756021179430936117703186628297798259962946105389678055937492529139624918819050121145820040744493946685696954852665988130200490572330390736017156342489807822905818339681033123588313594877193383455078089960731734527174011784218941887134291367527052665464983565114074040366884625418398659288026406454520940740270300118969354963533194264073904597417067604434831121704761033826371763757844708719978440319590584091409656789067656896398639616192828948187752275446654682648902660578332094386655437184555798059618096082012599980495812821602065652614873657083
    print("pt < q == ",pow(c,d,q)<q)
    print(long_to_bytes(pow(c,d,q)))

if __name__ == '__main__':
    main()

Flag : itf{math_problem_require_math_solution}

Last updated