
diberikan file cipher.py dan service implementasinya dalam netcat yang berikut isinya :
import time
exptables=[REDACTED]
def main():
while(2>1):
time.sleep(2)
inp = input('''Please Select Your Mode\\n
[1] Encrypt a message with a custom key
[2] Decrypt a message
[3] Get the flag
[4] Exit
\\n''')
if not(inp in ('1','2','3','Encrypt a message with a custom key','Decrypt a message','Get the flag','4','Exit')):
print('Sorry, please try again!')
else:
if inp=='1' or inp=='Encrypt a message with a custom key':
try:
encrypt()
except Exception as e:
print('Error Encrypting! Please try again\\n' + e)
elif inp=='2' or inp=='Decrypt a message':
try:
decrypt()
except Exception as e:
print('Error decrypting! Please try again\\n' + e)
elif inp=='4' or inp=='Exit':
return True
else:
try:
getFlag()
except:
print('Error getting flag! Please try again')
def encrypt():
pt = str(input('Enter your plaintext: '))
try:
key = input('Enter your 6 byte key (ex. 0011AABBCCDD): ').strip()
binKey = str(bin(int('1'+key,base=16)))[3:]
except:
print('Invalid Key! Please ensure that your input is 6 bytes!')
return -1
if(len(binKey)!=48):
print('Error with key! Please ensure key is 6 bytes long!')
return -1
binPT=''
for chr in pt:
binPT+='{0:08b}'.format(ord(chr))
binCText=''
binPT=pad(binPT)
for i in range(0,len(binPT),48):
binCText+=expand(xor(binPT[i:i+48],binKey))
print('\\nYour ciphertext is: \\n' + binCText+'\\n\\n')
def decrypt():
ctext = str(input('Enter your ciphertext as binary (ex. 0011001101010101000011110000000011111111): ')).strip()
try:
key = input('Enter your 6 byte key (ex. 0011FFDDCCBB): ').strip()
binKey = str(bin(int('1'+key,base=16)))[3:]
except:
print('Invalid Key! Please ensure that your input is 6 bytes!')
return -1
if(len(binKey)!=48):
print('Error with key! Please ensure key is 6 characters long!')
return -1
binPText=''
for i in range(0,len(ctext),72):
binPText+=xor(unexpand(ctext[i:i+72]),binKey)
decodedMessage=''
for i in range(0,len(binPText),8):
decodedMessage+=str(chr(int(binPText[i:i+8],2)))
print('\\nHere is your plaintext back: \\n ' + decodedMessage+'\\n\\n')
def getFlag():
print('''
111100010001010001101000000101110001010001100001100001100001000101101010010010010001100001101010000101010010111000011001010001101110111000101110010010010001111000000101101101110001010001110001010001000101000101111100010010010101000101110001111000010101100001011001010001011110011001010001101010010010100001010010010001011001111000110011010001010010010001010101111100101010100001101010101101110001100001010001011001110011000101100001010010110011101101010010100001110011011001101101101101011001101101100001
''')
def unexpand(ctext):
unexp = ''
for i in range(0,len(ctext),6):
for j in range(0,4):
try:
lsb = exptables[j].index(ctext[i:i+6])
unexp += '{0:02b}'.format(j)
unexp += '{0:02b}'.format(lsb)
except:
continue
return unexp
def pad(ptext): ##DONE
if len(ptext)%48!=0:
bytesToAdd = (48-(len(ptext)%48))//8
for i in range(0,bytesToAdd):
ptext+='{0:08b}'.format(i)
elif len(ptext)==0:
raise ValueError("Invalid plaintext length")
return ptext
def xor(ptext,key):
text=''
for i in range(0,48):
text+=str(int(ptext[i])^int(key[i]))
return text
def expand(ctext):
ct=''
for i in range(0,len(ctext),4):
msb = ctext[i:i+2]
lsb = ctext[i+2:i+4]
exp = exptables[int(msb,2)][int(lsb,2)]
ct+=exp
return ct
if __name__=='__main__':
main()
Jadi kita akan disuguhkan menu yakni 1, 2, 3, dan 4. Selanjutnya kita coba ke 1
Opsi 1. kita bakal enkripsi suatu pesan dengan key sesuka kita. Bagaimana cara kerjanya?
Opsi 2. Dia membalik subtitusi dengan enxpand, yang disitu jelas bahwa exptables adalah list of list dengan ukuran 4x4, dan maisng-masing elemennya berisikan bit sepanjang 6.
maka serangan yang bisa kita gunakan adalah choosen ciphertext dan choosen key.
kalau kita melihat dari proses enkripsi, kita bisa pilih ct dan key sedemikian sehingga memetakan ke 0000 0001 0010 …. 1111 untuk merecover exptables, selanjutnya kita juga tau bahwa format flag adalah TUCTF{ yang pas sekali panjangnya 6, berarti plaintext ini ketika dienkripsi tidak perlu padding, sehingga kita bia memperoleh key nya dengan plaintext ini, setelah mendapatkan exptables dan key untuk flag maka tinggal dekripsi seperti pada challenge. Berikut solvernya
from pwn import *
def pad(ptext): ##DONE
if len(ptext)%48!=0:
bytesToAdd = (48-(len(ptext)%48))//8
for i in range(0,bytesToAdd):
ptext+='{0:08b}'.format(i)
elif len(ptext)==0:
raise ValueError("Invalid plaintext length")
return ptext
def find_pt_key(target):
# Coba dengan plaintext sederhana
pt = "AAA"
binPT = ''
for chr in pt:
binPT += '{0:08b}'.format(ord(chr))
# Pad plaintext ke 48 bit
binPT=pad(binPT)
# Mencari binKey dengan XOR
binKey = ''
for i in range(48):
binKey += str(int(binPT[i]) ^ int(target[i]))
# Convert binKey ke hex sesuai format server
# Karena server menggunakan: str(bin(int('1'+key,base=16)))[3:]
# Maka kita perlu reverse prosesnya:
# 1. Tambahkan '100' di depan binary (karena [3:] memotong '0b1')
full_binary = '100' + binKey
# 2. Convert ke int
int_value = int(full_binary, 2)
# 3. Remove '1' dari depan hex
hex_key = hex(int_value)[3:].zfill(12)
print(f"Plaintext: {pt}")
print(f"Key (hex): {hex_key}")
print(f"Binary PT: {binPT}")
print(f"Binary Key: {binKey}")
# Verifikasi dengan cara server
test_binKey = str(bin(int('1'+hex_key,base=16)))[3:]
result = ''
for i in range(48):
result += str(int(binPT[i]) ^ int(test_binKey[i]))
print(f"XOR result: {result}")
print(f"Target : {target}")
print(f"Match: {result == target}")
print(f"Key length in bits: {len(test_binKey)}")
return hex_key
def recover_exptables(r):
pt = "AAA"
target1 = "000000010010001101000101011001111000100110101011"
hex_key1 = find_pt_key(target1)
target2 = "010001010110011110001001101010111100110111101111"
hex_key2 = find_pt_key(target2)
r.sendlineafter(b'Exit', b'1') # pilih encrypt
r.sendlineafter(b'plaintext:', pt.encode()) # input karakter apapun
r.sendlineafter(b'key', hex_key1.encode())
ctext1 = r.recvuntil(b'\\n\\n').decode().strip().split()
exptables = ctext1[-1]
r.sendlineafter(b'Exit', b'1') # pilih encrypt
r.sendlineafter(b'plaintext:', pt.encode()) # input karakter apapun
r.sendlineafter(b'key', hex_key2.encode())
ctext2 = r.recvuntil(b'\\n\\n').decode().strip().split()
exptables += ctext2[-1][48:]
exptables = [exptables[6*i:6*(i+1)] for i in range(16)]
exptables = [exptables[4*i:4*(i+1)] for i in range(4)]
return exptables
def unexpand(ctext):
unexp = ''
for i in range(0,len(ctext),6):
for j in range(0,4):
try:
lsb = exptables[j].index(ctext[i:i+6])
unexp += '{0:02b}'.format(j)
unexp += '{0:02b}'.format(lsb)
except:
continue
return unexp
def xorr(ptext,key):
text=''
for i in range(0,48):
text+=str(int(ptext[i])^int(key[i]))
return text
def recover_key(ct):
pt = "TUCTF{"
binPT=''
for chr in pt:
binPT+='{0:08b}'.format(ord(chr))
binKey =''
binKey+=xorr(unexpand(ct),binPT)
full_binary = '100' + binKey
# 2. Convert ke int
int_value = int(full_binary, 2)
# 3. Remove '1' dari depan hex
hex_key = hex(int_value)[3:].zfill(12)
return hex_key
r = remote('chal.tuctf.com', 30000)
exptables = recover_exptables(r)
print("exptables = ", exptables)
#exptables = [['010101', '011001', '101101', '110011'], ['010001', '000101', '100001', '111000'], ['101000', '011110', '011101', '101110'], ['111100', '110001', '010010', '101010']]
ct = "111100010001010001101000000101110001010001100001100001100001000101101010"
enc_flag = "111100010001010001101000000101110001010001100001100001100001000101101010010010010001100001101010000101010010111000011001010001101110111000101110010010010001111000000101101101110001010001110001010001000101000101111100010010010101000101110001111000010101100001011001010001011110011001010001101010010010100001010010010001011001111000110011010001010010010001010101111100101010100001101010101101110001100001010001011001110011000101100001010010110011101101010010100001110011011001101101101101011001101101100001"
[['010101', '011001', '101101', '110011'], ['010001', '000101', '100001', '000101'], ['101000', '011110', '011101', '011110'], ['111100', '110001', '010010', '110001']]
key = recover_key(ct)
print("key = ", key)
#key = 901d1e122024
r.sendlineafter(b'Exit', b'2') # pilih encrypt
r.sendlineafter(b'Enter your ciphertext as binary (ex. 0011001101010101000011110000000011111111):', enc_flag.encode()) # input karakter apapun
r.sendlineafter(b'Enter your 6 byte key (ex. 0011FFDDCCBB):', key.encode())
r.interactive()
#TUCTF{tr@ck_th3_exp@nsi0ns_and_r3v3rs3}