Writeup bASe64 encRyption

SlashRoot 4.0 Quals

Posted by rmn0x01 on Thursday, October 31, 2019

Soal kriptografi penyisihan Slashroot 4.0 dengan poin 150.

Intro

File yang diberikan berupa

BASe64.py:             Python script, ASCII text executable
flag.enc:              ASCII text, with no line terminators
public.key:            ASCII text            

TLDR

  1. Reverse
  2. Get the flag

Full Steps

Script yang digunakan untuk enkripsi adalah sebagai berikut:

BASe64.py

from base64 import b64encode as b64
from Crypto.PublicKey import RSA
from Crypto.Util.number import bytes_to_long, long_to_bytes

FLAG = "SlashRootCTF"

def enc(key, msg, b6="", b4=""):
    key = RSA.importKey(key)
    for i in msg:
        b6 += chr(ord(i) ^ 6)
    msg = pow(int(str(bytes_to_long(b6))[::-1]), key.e, key.n)
    for i in long_to_bytes(msg):
        b4 += chr(ord(i) ^ 4)
    with open("flag.enc", "w") as f:
        f.write(b64(b4))
    return True

def main():
    with open("public.key", "r") as f:
        key = f.read()
    print enc(key, FLAG)

if __name__ == '__main__':
    main()

Sedangkan, isi public.key adalah:

-----BEGIN PUBLIC KEY-----
MIICIDANBgkqhkiG9w0BAQEFAAOCAg0AMIICCAKCAgEAzQWEvaQim2OTjvYaAUfd
YaBxMFPkBst4SQEd2+5pO4pbr1fK44hoEHQFYo5Ha7guyDoqno5fTu4m5M2KqNnL
uARaunefB8rT6fnCSjW/CJxpwZdX5AaIbWayJ6huVrCQCzx2+VgqrtWhPRbHD6jf
4GGDDwVrNRlOQbJ3RMg7J/15T2DPloVyRoYsmtCyGmIzivUFkdgbkMdlXRJZ7TVn
cOo7DA4h38+nMPwIJIAHqn1R3lP0FzUlo//uatyJaQ5jatQlDB5x/vgJPeeBHdmM
u3lNdVsUzjy33NKVoIZWfqxJpYKxJwX+bZQ42Ec1NN2Ke7SUGXX5aDpPKlP2whNM
Ov6P+wuVxidlf1qhRBNhWvWrH4fH4W7xe5IAxOBCK1HM6eYAFxPLVDYK/7kMo4Pt
llDTWu4Ltx1QjcD9uz9TDQ3VkMILkublVroA8YqHYYo/aWA/P0uZ904Xf19ir/C+
XvW1KJQptAMlGQpjMF9iLfyTlCwLKF8yJO+jsAWV/Lt5ZoX6CxjTZxLdMGe7y0/Y
JQ7zgTPTXjg0ahoaxba/P1aP18XCfoxRQiouLB9PQYrMnvt2amXoLE63uZWoeXOD
KYdVY9BGAibBWrhMbfipbrvbJJujdsCg3JYZtDjzaPGdNgeYvNt6isxSoXfWyS2H
Ly3wns237b8o9KKtrH7safMCAQM=
-----END PUBLIC KEY-----

Dari script enkripsi, diketahui bahwa flag melalui 4 proses untuk menjadi ciphertext, yaitu ketika diubah menjadi variabel b6, msg, b4 dan diakhiri dengan encode base-64 Untuk b6, b4 dan base-64 encoded cukup mudah untuk di-reverse, sedangkan untuk variabl msg :

msg = pow(int(str(bytes_to_long(b6))[::-1]), key.e, key.n)

Merupakan tahap enkripsi dari Rivest-Shamir-Adleman (RSA) [1], dengan menggunakan public.key yang telah diberikan sebelumnya.

Analisis public.key

>>> from Crypto.PublicKey import RSA
>>> f = open('public.key')
>>> key = RSA.importKey(f)
>>> key.e
3L
>>> key.n
836414977663093498775759020216084836787420889747126742590222185854655649873073300791388963754403587619900430690389934186556282921785037091589805802826975820835166555627383959573375136711725774406116744692068193869589975717330800216005499087088494925927014327106360539922969286048894096410595742930323646596100878026252182713607742090636350504755352743292192405445839235128399327562037361366282105095014339073306609318841094957471802025270431848632243971424509555603647057234859069806180843788643128732190794500160434421798288673361619358217792550868485866040698826656938130700425323076978350701743200524803990078316159539695961739443046404629652547008820956213066323862018864932528085519326267283169595263900555186081276632605668995689486184080220667713976574792165849198380630122640260570312634961955644417515208749099360863694638193878941457840863969700429613149615536210644219382544437563645304476810236583979502546800239799053984786414379132055277660873373112256477793540281201972249753036734243617427751951270679600494519213513255294979092208148143404145593464164806722183210920156429454257439313931990312733878634792913952731264476551969757599618884629710842993473677629101653755931029328588298297605000132481726448430532553203L

Vulnerability yang ditampilkan dari public.key tersebut ada pada eksponen-nya yang sangat kecil (3), seperti yang tertulis di [1]:

When encrypting with low encryption exponents (e.g., e = 3) and small values of the m, (i.e., m < n1/e) the result of me is strictly less than the modulus n. In this case, ciphertexts can be easily decrypted by taking the eth root of the ciphertext over the integers.

Artinya, plaintext dapat di-recover dengan cara mencari akar pangkat e (dalam soal ini, e = 3) dari ciphertext. Script solver-nya menjadi:

solver.py

import base64
from Crypto.PublicKey import RSA
from Crypto.Util.number import bytes_to_long, long_to_bytes
import gmpy2

f = open('public.key','r')
key = RSA.importKey(f)
g = open('flag.enc','r').read()
b4 = base64.b64decode(g)
msg = ''
for i in b4:
    msg+=chr(ord(i)^4)
msg = bytes_to_long(msg)
b6 = str(gmpy2.iroot(msg,3)[0])
b6 = b6[::-1]
b6 = long_to_bytes(b6)
flag = ''
for i in b6:
    flag+=chr(ord(i)^6)

print flag

Flag

SlashRootCTF{L0W_l0w_low4x_low_E_599d8554a71caf3d}

References

  1. https://en.wikipedia.org/wiki/RSA_(cryptosystem)
  2. https://crypto.stackexchange.com/questions/6713/low-public-exponent-attack-for-rsa
  3. https://cims.nyu.edu/~regev/teaching/lattices_fall_2004/ln/rsa.pdf
  4. https://en.wikipedia.org/wiki/Coppersmith%27s_attack