Tags: xor
Rating:
# Writeup for Bank CRYPTO (90)
> Description: Everyone knows that banks are insecure. This one super secure and only allows only 20 transactions per session. I always wanted a million on my account.
![crypto90.zip](crypto90.zip)
I started analysing behaviour of the given script
b@x:~/Desktop/task > python bank.py
WELCOME TO THE BANK BACKEND!
Possible commands:
help - Prints this message
create - Creates a new transaction with amount
complete <tid> <hash> - Completes a transaction to the current account. <tid> is the transaction ID to use and <hash> the verification hash.
Your balance: 0, 20 transactions left.
Command: create 1000
Transaction #0 over 1000 initiated. Please complete it using the verification code: 527f692949701f5f273a1e552248342366
Your balance: 0, 19 transactions left.
Command: complete 0 527f692949701f5f273a1e552248342366
Transaction completed!
Your balance: 1000, 19 transactions left.
Command: create 5000
Transaction #1 over 5000 initiated. Please complete it using the verification code: 123f296909305f1f677a5e15620c746326
Your balance: 1000, 18 transactions left.
Command: create 9999
Too many transactions or amount too big!
Your balance: 1000, 18 transactions left.
We need to have a million or more in 'your balance' and seems that it's impossible to create transactions of 9999$ or more
let's check how it's done in the code
class Transaction:
def __init__(self, a):
self.__a = a
self.__k = int(os.urandom(32).encode('hex'),16)
self.__v = True
[......]
class Transaction is created after we type command 'create' and it remembers the amount of money in __a
below is a fragment of code from command handled
elif("complete" in cmd):
[.......]
if(len(h) != 34):
print 'Wrong verification length'
continue
tn = A.get_t()[t] #get transaction of number t
tn = C.decrypt(tn, h) #decryption: tn - transaction, h - verification code
if(tn is None):
print 'Oops, verification failed!'
continue
r = A.add_m(tn) #adding money to account
we see that amount of added money to account is based on decrypted value, so let's investigate how encryption and decryption is done and check how to avoid 'verification failed'
def encrypt(self, t): # t is object of class Transaction
self.__r.set_x(t.get_k())
ct = ""
s = str(t) #(1)
l = len(s)
s += chr(0x09)*(17-l) #(2)
for c in s:
ct += chr( ord(c) ^ (self.__r.get_next() % 2**7) ) #(3)
return ct.encode('hex')
def decrypt(self, t, ct): # t - transaction ct - verification code
self.__r.set_x(t.get_k())
try:
ct = ct.decode('hex')
pt = ""
for c in ct:
pt += chr( ord(c) ^ (self.__r.get_next() % 2**7) )
if(not "TRANSACTION" in pt):
return None
a = int(pt.replace("TRANSACTION:",""))
t.set_a(a)
return t
except:
return None
the text is simply xor'ed with bytes generated by the class Randomizer
function encrypt get's a text value of object of class Transaction (1)
adds padding 0x09 where length is too small (2)
and xor's this text (3)
this text value of object looks like "TRANSACTION: 1000"
we can see this in below code:
class Transaction:
[....]
def __str__(self):
return "TRANSACTION: " + str(self.__a)
function decrypt is checking if transaction code is correct by checking of presence of text "TRANSACTION:" in the decrypted value
encrypted text looks like 'T' xor'ed with something, 'r' xor'ed with something2, 'a' xored with something3 and so on...
on the position 13 we have '1' xor x1 , 14 - '0' xor x2, 15 -'0' xor x3, 16 - '0' xor x4.
We can xor position 13 with '1' and after that xor with '9' and position 14 with '0' and '9' and so on
('1' xor x1) xor '1' xor '9' = x1 xor '9'
so we can change 1000 to 9999
but 9999*20 is still lower than million and we can't make verification code longer
but seems like we can also change space between ':' and a value to some number because only "TRANSACTION:" is removed before casting decrypted value to integer so let's change space also to '9' using same method as above
I created a code which changes verification code of value 1000 to code with value 99999
![bank_solv.py](bank_solv.py)
we need to create ~11 transaction