Skip to content

Latest commit

 

History

History
187 lines (151 loc) · 4.8 KB

README.md

File metadata and controls

187 lines (151 loc) · 4.8 KB

Babycrypt

We are given with elf64 binary and notes.dat file.

The binary asks for key and text and gives out encrypted text.

The notes contains output from program with different inputs:

key: %key%
text: test_test_test_test_test
Encoded: 7685737a9f7895737a9f84857b769f7a657b769f78898378
         
key: %key%
text: qwertyuiopasdfgh
Encoded: 717785747885858d6f7e917364686776

key: %key%
text: skIllaoInasJjklqo19akq9k13k45k69alq1
Encoded: 7393a992708d8fad708d83aa7273707d6f3939856b7d398bb53b8b34b573b6c5618e7135
         

key: %key%
text: %flag%
Encoded: 8185748f7b3b3a3565454584b8babbb8b441323ebc8b3a86b5899283b9c2c56d64388889b781


*Note: in all three cases used one key*

After some reversing we can see that key is being shuffled and applied to input text. After that it prints encrypted text.

void make_text(char *data)
{
  unsigned __int64 v1;
  int v2; 
  int v3; 
  int v4; 
  int i; 

  for ( i = 0; ; ++i )
  {
    v1 = i;
    if ( v1 >= text_len(data + 88) )
      break;
    v2 = *(char *)get_text(data + 88, i);
    v3 = *(_DWORD *)get_key(data, i % 16) ^ v2; // xor
    v4 = v3 + *(_DWORD *)get_key(data, i % 16); // add
    sub_55E323148EC6((__int64)(data + 112), (__int64)&v4);
  }
}

Solution:

The solution is simple. since we know 3 inputs and 3 outputs, we can find all possible keys for given pairs.

After that we can find possible plaintexts for given encrypted flag.

import binascii
from z3 import *
import string

outputs = {
    'test_test_test_test_test' : binascii.unhexlify('7685737a9f7895737a9f84857b769f7a657b769f78898378'),
    'qwertyuiopasdfgh' : binascii.unhexlify('717785747885858d6f7e917364686776'),
    'skIllaoInasJjklqo19akq9k13k45k69alq1' : binascii.unhexlify('7393a992708d8fad708d83aa7273707d6f3939856b7d398bb53b8b34b573b6c5618e7135')
}

mapping = {
    0:0,
    1:4,
    2:8,
    3:12,
    4:13,
    5:14,
    6:15,
    7:11,
    8:7,
    9:3,
    10:2,
    11:1,
    12:5,
    13:9,
    14:10,
    15:6
}

# apply key shuffle
def map_key(key):
    new_key = [0 for i in range(len(key))]
    for i, v in enumerate(key):
        new_key[mapping[i]] = v
    return ''.join(new_key)

# apply key shuffle reverse
def map_key_rev(key):
    new_key = [0 for i in range(len(key))]
    new_mapping = {}
    for k, v in mapping.items():
        new_mapping[v] = k
    for i, v in enumerate(key):
        new_key[new_mapping[i]] = v
    return ''.join(new_key)

# encryption
def encode_byte(inp_text, inp_key):
    return ((inp_text ^ inp_key) + inp_key) & 0xff

# helper function
def to_byte(x):
    return bytes([x])

# find all possible keys
# input  - dict{plaintext : encypted_text}
# output - all possible keys
def get_keys(outputs):
    s = Solver()
    key_len = 16
    possible_keys = []
    # make key 16 bytes long 
    # key has to be in hexdigits range
    for i in range(0, key_len):
        globals()['b%i' % i] = BitVec('b%i' % i, 8)
        s.add(Or(And(globals()['b%i' % i] >= 48, globals()['b%i' % i] <= 57), And(globals()['b%i' % i] >= 97, globals()['b%i' % i] <= 104)))
    

    for text, output in outputs.items():
        for i in range(len(text)):
            s.add(encode_byte(ord(text[i]), globals()['b%i' % (i % 16)]) == output[i])
 
    #print(s)
	# find all possible keys
    while s.check() == sat:
        model = s.model()
        block = []
        out = ''
        for i in range(key_len):
            c = globals()['b%i' % i]
            out += chr(model[c].as_long())
            block.append(c != model[c])
        s.add(Or(block))
        possible_keys.append(map_key_rev(out))
    return possible_keys


# input - keys
# output - all possible plaintext
def solve(keys):
    encoded_flag = binascii.unhexlify('8185748f7b3b3a3565454584b8babbb8b441323ebc8b3a86b5899283b9c2c56d64388889b781')
    flag_len = len(encoded_flag)
    for key in keys:
        k = map_key(key)
        s = Solver()
        # gen flag
        for i in range(0, flag_len):
            globals()['b%i' % i] = BitVec('b%i' % i, 8)
            s.add(And(globals()['b%i' % i] >= 32, globals()['b%i' % i] <= 127))

        for i in range(flag_len):
            s.add(encode_byte(globals()['b%i' % i], ord(k[i % len(k)])) == encoded_flag[i])
        if s.check() == sat:
            out = ''
            model = s.model()
            for i in range(flag_len):
                c = globals()['b%i' % i]
                out += chr(model[c].as_long())
            print(out)

if __name__ == "__main__":
    #assert 'aaaabbbbccccdddd' == map_key_rev('acccaddcaddbabbb')
    #sys.exit(main())
    keys = get_keys(outputs)
    solve(keys)

And we get all plaintexts

Aero{381a95d003629088c8f1ebc189ab6fe7}
Aero{3:1a95d003629088c:f1ebc189ab6fe7}