2

I have an old application that stores the username and password in plaintext but encoded by an unknown encoding algorithm to me, it seams like base64, but I think it's not, It seams that any word longer than 22 or 23 is truncated, but unfortunately there's some odd examples that changes the last character

for example:

Input: ABCDEFGHIJKLMNOPQRSTUV
Output: //DeparF3/jFcmVGBf5LcmNHYqJBmw

Input: ABCDEFGHIJKLMNOPQRSTUVWXYZ
Output: //DeparF3/jFcmVGBf5LcmNHYqJBmw

Input: ABCDEFGHIJKLMNOPQRSTUV1
Output: //DeparF3/jFcmVGBf5LcmNHYqJBm2

Input: ABCDEFGHIJKLMNOPQRSTUV123456
Output: //DeparF3/jFcmVGBf5LcmNHYqJBm2

Input: 1234567890123456789012
Output: j4Cu1dq1r4i1CB84e4QxFAUtCMYl/w

Input: 12345678901234567890123
Output: j4Cu1dq1r4i1CB84e4QxFAUtCMYl/2

Input: 123456789012345678901234
Output: j4Cu1dq1r4i1CB84e4QxFAUtCMYl/2

Input: 1234567890123456789012a
Output: j4Cu1dq1r4i1CB84e4QxFAUtCMYl/z

Input: 1234567890123456789012ab
Output: j4Cu1dq1r4i1CB84e4QxFAUtCMYl/z

Input: 1234567890123456789012c
Output: j4Cu1dq1r4i1CB84e4QxFAUtCMYl/z

Input: 1234567890123456789012F
Output: j4Cu1dq1r4i1CB84e4QxFAUtCMYl/x

Input: 1234567890123456789012V
Output: j4Cu1dq1r4i1CB84e4QxFAUtCMYl/w

Input: 1A2B3C4D5E6F7G8H9I0J1K
Output: j/Ovo9zArPS5fRhMf/c8agtcAbwlhg

Input: 1A2B3C4D5E6F7G8H9I0J1K2
Output: j/Ovo9zArPS5fRhMf/c8agtcAbwlhm

Input: 1A2B3C4D5E6F7G8H9I0J1K3
Output: j/Ovo9zArPS5fRhMf/c8agtcAbwlhm

Input: 1A2B3C4D5E6F7G8H9I0J1KL
Output: j/Ovo9zArPS5fRhMf/c8agtcAbwlhh

Input: 1A2B3C4D5E6F7G8H9I0J1KM
Output: j/Ovo9zArPS5fRhMf/c8agtcAbwlhh

Input: A1B2C3D4E5F6G7H8I9J0KL
Output: /4Pf06yw3ITJDWg8D4dMGnsse8ZfgQ

Input: A1B2C3D4E5F6G7H8I9J0KLM
Output: /4Pf06yw3ITJDWg8D4dMGnsse8ZfgR

Input: A1B2C3D4E5F6G7H8I9J0KL7
Output: /4Pf06yw3ITJDWg8D4dMGnsse8ZfgW

Input: abcABC123/+321++1GWZVV
Output: 39D+oK3AqYK/FwU5eoEvCQNSZqxCmw

Input: abcABC123/+321++1GWZVV1
Output: 39D+oK3AqYK/FwU5eoEvCQNSZqxCm2

Input: lkjsd2093jljsdLJSDl12A
Output: 0tn3kouxqIm/UkJgO9RIaGFRXccmjA

Input: lkjsd2093jljsdLJSDl123
Output: 0tn3kouxqIm/UkJgO9RIaGFRXccm/g

Input: lkjsd2093jljsdLJSDl1234
Output: 0tn3kouxqIm/UkJgO9RIaGFRXccm/m

Input: aaaaaaaaaaaaaaaaaaaaaa
Output: 39P8gI7i+dHtWU9rKdFlQ1N0UJd1rA

Input: aaaaaaaaaaaaaaaaaaaaaaa
Output: 39P8gI7i+dHtWU9rKdFlQ1N0UJd1rD

Input: aaaaaaaaaaaaaaaaaaaaaa1
Output: 39P8gI7i+dHtWU9rKdFlQ1N0UJd1rG

Input: aaaaaaaaaaaaaaaaaaaaaaF
Output: 39P8gI7i+dHtWU9rKdFlQ1N0UJd1rB

Input: 1111111111111111111111
Output: j4Os0N6yqYG9CR87eYE1EwMkAMcl/A

Input: 11111111111111111111111
Output: j4Os0N6yqYG9CR87eYE1EwMkAMcl/G

Input: a
Output: 3w==

Input: c
Output: 3Q==

Input: ab
Output: 39A=

Input: abc
Output: 39D+

Input: abcd
Output: 39D+hQ==

Input: abcde
Output: 39D+hYo=

Input: abcdef
Output: 39D+hYrl

Input: abcdefg
Output: 39D+hYrl/w==

Input: abcdefgh
Output: 39D+hYrl/9g=

Input: 123
Output: j4Cu

Input: 1234
Output: j4Cu1Q==

Input: 12345
Output: j4Cu1do=

Input: 123456
Output: j4Cu1dq1

Any idea? Thanks!

6
  • Are you sure about encoding of the string "ab" ?
    – w s
    Jul 18, 2019 at 7:48
  • I have an idea but it doesn't fit to encoding of "ab" string (was it "Gg" by any chance ?) . It looks like base64 of the original string xored with some at least 8 byte length constant (specifically 0xbe,0xb2,0x9d,0xe1,0xef,0x83,0x98,0xb0).
    – w s
    Jul 18, 2019 at 8:07
  • 1
    Can you post more encoded strings (preferably long) ?
    – w s
    Jul 18, 2019 at 8:15
  • The last one is an oddity too. I was expecting a custom base64 alphabet, but the '1' appears twice, once for index 13, and once for index 54 which makes no sense. Either this is an error on your end or it's not simply a custom base64 alphabet. Jul 18, 2019 at 13:33
  • @ws You are right! It's the encoding of "Gg" not "ab", so Gg="+dU=" and "ab"="39A=", I corrected it in the examples
    – ksr
    Jul 20, 2019 at 13:46

2 Answers 2

0

OK, the encoding goes as follows:

  1. Byte xor the input string with [0xbe,0xb2,0x9d,0xe1,0xef,0x83,0x98,0xb0,0x8c,0x38,0x2e,0x0a,0x48,0xb0,0x04,0x22,0x32,0x15,0x31,0xf6,0x14,0xcd,0x51]
  2. Encode it to base64
  3. Take first 30 bytes of base 64 encoded string and write it down, which will be a result.

In fact the last byte of the key is not used as whole (only msb nibble used), you can insert anything from a range [0x50-0x5f] instead of 0x51.

As you correctly stated, the output is truncated, so you can extract only the first 22 letters of the encoded string.

The following python code decodes the provided outputs and checks the algorithm:

import base64
# our inputs, input and output correspondingly
pairs = [
    ("ABCDEFGHIJKLMNOPQRSTUV","//DeparF3/jFcmVGBf5LcmNHYqJBmw"),
    ("ABCDEFGHIJKLMNOPQRSTUVWXYZ","//DeparF3/jFcmVGBf5LcmNHYqJBmw"),
    ("ABCDEFGHIJKLMNOPQRSTUV1","//DeparF3/jFcmVGBf5LcmNHYqJBm2"),
    ("ABCDEFGHIJKLMNOPQRSTUV123456","//DeparF3/jFcmVGBf5LcmNHYqJBm2"),
    ("1234567890123456789012","j4Cu1dq1r4i1CB84e4QxFAUtCMYl/w"),
    ("12345678901234567890123","j4Cu1dq1r4i1CB84e4QxFAUtCMYl/2"),
    ("123456789012345678901234","j4Cu1dq1r4i1CB84e4QxFAUtCMYl/2"),
    ("1234567890123456789012a","j4Cu1dq1r4i1CB84e4QxFAUtCMYl/z"),
    ("1234567890123456789012ab","j4Cu1dq1r4i1CB84e4QxFAUtCMYl/z"),
    ("1234567890123456789012c","j4Cu1dq1r4i1CB84e4QxFAUtCMYl/z"),
    ("1234567890123456789012F","j4Cu1dq1r4i1CB84e4QxFAUtCMYl/x"),
    ("1234567890123456789012V","j4Cu1dq1r4i1CB84e4QxFAUtCMYl/w"),
    ("1A2B3C4D5E6F7G8H9I0J1K","j/Ovo9zArPS5fRhMf/c8agtcAbwlhg"),
    ("1A2B3C4D5E6F7G8H9I0J1K2","j/Ovo9zArPS5fRhMf/c8agtcAbwlhm"),
    ("1A2B3C4D5E6F7G8H9I0J1K3","j/Ovo9zArPS5fRhMf/c8agtcAbwlhm"),
    ("1A2B3C4D5E6F7G8H9I0J1KL","j/Ovo9zArPS5fRhMf/c8agtcAbwlhh"),
    ("1A2B3C4D5E6F7G8H9I0J1KM","j/Ovo9zArPS5fRhMf/c8agtcAbwlhh"),
    ("A1B2C3D4E5F6G7H8I9J0KL","/4Pf06yw3ITJDWg8D4dMGnsse8ZfgQ"),
    ("A1B2C3D4E5F6G7H8I9J0KLM","/4Pf06yw3ITJDWg8D4dMGnsse8ZfgR"),
    ("A1B2C3D4E5F6G7H8I9J0KL7","/4Pf06yw3ITJDWg8D4dMGnsse8ZfgW"),
    ("abcABC123/+321++1GWZVV","39D+oK3AqYK/FwU5eoEvCQNSZqxCmw"),
    ("abcABC123/+321++1GWZVV1","39D+oK3AqYK/FwU5eoEvCQNSZqxCm2"),
    ("lkjsd2093jljsdLJSDl12A","0tn3kouxqIm/UkJgO9RIaGFRXccmjA"),
    ("lkjsd2093jljsdLJSDl123","0tn3kouxqIm/UkJgO9RIaGFRXccm/g"),
    ("lkjsd2093jljsdLJSDl1234","0tn3kouxqIm/UkJgO9RIaGFRXccm/m"),
    ("aaaaaaaaaaaaaaaaaaaaaa","39P8gI7i+dHtWU9rKdFlQ1N0UJd1rA"),
    ("aaaaaaaaaaaaaaaaaaaaaaa","39P8gI7i+dHtWU9rKdFlQ1N0UJd1rD"),
    ("aaaaaaaaaaaaaaaaaaaaaa1","39P8gI7i+dHtWU9rKdFlQ1N0UJd1rG"),
    ("aaaaaaaaaaaaaaaaaaaaaaF","39P8gI7i+dHtWU9rKdFlQ1N0UJd1rB"),
    ("1111111111111111111111","j4Os0N6yqYG9CR87eYE1EwMkAMcl/A"),
    ("11111111111111111111111","j4Os0N6yqYG9CR87eYE1EwMkAMcl/G"),
    ("a","3w=="),
    ("c","3Q=="),
    ("ab","39A="),
    ("abc","39D+"),
    ("abcd","39D+hQ=="),
    ("abcde","39D+hYo="),
    ("abcdef","39D+hYrl"),
    ("abcdefg","39D+hYrl/w=="),
    ("abcdefgh","39D+hYrl/9g="),
    ("123","j4Cu"),
    ("1234","j4Cu1Q=="),
    ("12345","j4Cu1do="),
    ("123456","j4Cu1dq1"),
]
# the xoring "key"
key = [0xbe,0xb2,0x9d,0xe1,0xef,0x83,0x98,0xb0,0x8c,0x38,0x2e,0x0a,0x48,0xb0,0x04,0x22,0x32,0x15,0x31,0xf6,0x14,0xcd,0x51]

def do_xor(a, b):
    res = ""
    for i in range(len(a)):
        res += chr(ord(a[i]) ^ b[i % len(b)])
    return res

for (inp, output) in pairs:
    #adding universal padding for a case of bad length
    try:
        decoded = base64.b64decode(output)
    except:
        decoded = base64.b64decode(output + "===")

    xored = do_xor(decoded, key)
    encoded = base64.b64encode(do_xor(inp, key))[:30]
    print "Input    --> ", inp
    print "Output   --> ", output
    print "Encoded  --> ", encoded
    print "restored --> ", xored
    if xored == inp:
        print "result: exact match"
    elif inp.find(xored) != -1:
        print "result: truncated, but still good"
    else:
        print "result: FAILURE"
    if encoded==output:
        print "encode: result OK"
    else:
        print "encode: FAILURE"
0
0
function crypt(b) {
    var raw = atob(b)
    var key = [0xbe,0xb2,0x9d,0xe1,0xef,0x83,0x98,0xb0,0x8c,0x38,0x2e,0x0a,0x48,0xb0,0x04,0x22,0x32,0x15,0x31,0xf6,0x14,0xcd]
    var ret = []
    for (var i in raw) ret[i] = raw.charCodeAt(i) ^ key[i%key.length]
    return String.fromCharCode.apply(null, ret)
}
// example
console.log(crypt('//DeparF3/jFcmVGBf5LcmNHYqJBmw'))
console.log(crypt('//DeparF3/jFcmVGBf5LcmNHYqJBm2'))
console.log(crypt('j4Cu1dq1r4i1CB84e4QxFAUtCMYl/w'))

result

3
  • Doesn't work on on short strings like "a", "b", "ab"
    – w s
    Jul 20, 2019 at 15:46
  • Sorry, my mistake, it works.
    – w s
    Jul 21, 2019 at 9:36
  • this is a very neat code! it works great, thanks!
    – ksr
    Jul 21, 2019 at 11:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.