The data given was a block of hexadecimal numbers, shown belown:
7b 0a 20 a0 22 65 76 e5
6e 74 22 ba 20 22 70 e1
73 73 77 ef 72 64 5f e3
68 61 6e e7 65 22 2c 8a
20 20 22 f5 73 65 72 ee
61 6d 65 a2 3a 20 22 e2
63 6f 6c ec 69 6e 22 ac
0a 20 20 a2 6f 6c 64 df
70 61 73 f3 77 6f 72 e4
22 3a 20 a2 3a 5c 78 c3
37 5c 78 c6 34 5c 6e dc
78 41 46 a9 29 37 43 dc
78 31 35 dc 78 44 30 dc
78 46 33 dc 78 44 45 e9
55 3b 22 ac 0a 20 20 a2
6e 65 77 df 70 61 73 f3
77 6f 72 e4 22 3a 20 a2
39 5c 78 c6 41 5c 78 b9
39 5c 78 c3 41 5c 78 c5
44 5c 78 c6 32 58 53 c7
5c 78 44 c4 2d 5c 78 c3
32 5c 78 b8 45 7a 48 eb
22 2c 0a a0 20 22 74 e9
6d 65 73 f4 61 6d 70 a2
3a 20 31 b5 30 31 38 b5
38 38 36 b0 30 30 30 8a
7d 0a
Using Python, I decoded this data:
import binascii
data = '''
7b 0a 20 a0 22 65 76 e5
6e 74 22 ba 20 22 70 e1
73 73 77 ef 72 64 5f e3
68 61 6e e7 65 22 2c 8a
20 20 22 f5 73 65 72 ee
61 6d 65 a2 3a 20 22 e2
63 6f 6c ec 69 6e 22 ac
0a 20 20 a2 6f 6c 64 df
70 61 73 f3 77 6f 72 e4
22 3a 20 a2 3a 5c 78 c3
37 5c 78 c6 34 5c 6e dc
78 41 46 a9 29 37 43 dc
78 31 35 dc 78 44 30 dc
78 46 33 dc 78 44 45 e9
55 3b 22 ac 0a 20 20 a2
6e 65 77 df 70 61 73 f3
77 6f 72 e4 22 3a 20 a2
39 5c 78 c6 41 5c 78 b9
39 5c 78 c3 41 5c 78 c5
44 5c 78 c6 32 58 53 c7
5c 78 44 c4 2d 5c 78 c3
32 5c 78 b8 45 7a 48 eb
22 2c 0a a0 20 22 74 e9
6d 65 73 f4 61 6d 70 a2
3a 20 31 b5 30 31 38 b5
38 38 36 b0 30 30 30 8a
7d 0a
'''.replace('\n', '').replace(' ', '')
with open('data_raw', 'wb') as f:
f.write(binascii.unhexlify(data))
Now I had a file, data_raw
that had the raw data given inside of it:
# teknogeek at teknogeek-mbp in ~/Documents/CTF/Other/HackerOneHoodie [14:35:18]
→ cat data_raw
1 {
2 �"ev�nt"� "p�ssw�rd_�han�e",� "�ser�ame�: "�col�in"�
3 �old�pas�wor�": �:\x�7\x�4\n�xAF�)7C�x15�xD0�xF3�xDE�U;"�
4 �new�pas�wor�": �9\x�A\x�9\x�A\x�D\x�2XS�\xD�-\x�2\x�EzH�",
5 � "t�mes�amp�: 1�018�886�000�}
There seems to be something very wrong with this, but consistently -- every 4th character is a broken one. So I looked at the hex data for it using xxd
:
# teknogeek at teknogeek-mbp in ~/Documents/CTF/Other/HackerOneHoodie [14:35:22]
→ xxd data_raw
00000000: 7b0a 20a0 2265 76e5 6e74 22ba 2022 70e1 {. ."ev.nt". "p.
00000010: 7373 77ef 7264 5fe3 6861 6ee7 6522 2c8a ssw.rd_.han.e",.
00000020: 2020 22f5 7365 72ee 616d 65a2 3a20 22e2 ".ser.ame.: ".
00000030: 636f 6cec 696e 22ac 0a20 20a2 6f6c 64df col.in".. .old.
00000040: 7061 73f3 776f 72e4 223a 20a2 3a5c 78c3 pas.wor.": .:\x.
00000050: 375c 78c6 345c 6edc 7841 46a9 2937 43dc 7\x.4\n.xAF.)7C.
00000060: 7831 35dc 7844 30dc 7846 33dc 7844 45e9 x15.xD0.xF3.xDE.
00000070: 553b 22ac 0a20 20a2 6e65 77df 7061 73f3 U;".. .new.pas.
00000080: 776f 72e4 223a 20a2 395c 78c6 415c 78b9 wor.": .9\x.A\x.
00000090: 395c 78c3 415c 78c5 445c 78c6 3258 53c7 9\x.A\x.D\x.2XS.
000000a0: 5c78 44c4 2d5c 78c3 325c 78b8 457a 48eb \xD.-\x.2\x.EzH.
000000b0: 222c 0aa0 2022 74e9 6d65 73f4 616d 70a2 ",.. "t.mes.amp.
000000c0: 3a20 31b5 3031 38b5 3838 36b0 3030 308a : 1.018.886.000.
000000d0: 7d0a }.
Looking at this data, I could see that this was JSON and was able to make some conclusions based on the length of words, which character was missing, and the context of the situation.
String | Hex |
---|---|
"ev.nt" |
22 65 76 e5 6e 74 22 |
"p.ssw.rd_.han.e" |
22 70 e1 73 73 77 ef 72 64 5f e3 68 61 6e e7 65 22 |
These should be "event"
and "password_change"
. Comparing the wrong character codes with the correct ones, I could see that the e
in event
was a 0xe5
instead of a 0x65
. The a
, o
, c
, and g
in password_change
were 0xe1
instead of 0x61
, 0xef
instead of 0x6f
, 0xe3
instead of 0x63
, and 0xe7
instead of 0x67
. All of these were a difference of 0x80
(128
).
Combining the fact that every 4th character needed to be fixed, and the difference for all the broken characters was 0x80
, I wrote a python script to fix the data, output it to a file, and print it:
data = open('data_raw', 'rb').read()
fixed_data = ''.join([chr(c - 0x80 if (idx + 1) % 4 == 0 else c) for idx, c in enumerate(data)])
print(fixed_data)
with open('fixed_data.json', 'w') as f:
f.write(fixed_data)
Sorry about the list comprehension...let me make that easier to read.
data = open('data_raw', 'rb').read()
fixed_data = ''
# keep the current index handy to know if a 4th character
# (zero-indexed 3)
for idx, c in enumerate(data):
# it is a 4th character
if (idx + 1) % 4 == 0:
c -= 0x80
# convert the character code to a string and add it
# to the end of the result
fixed_data += chr(c)
print(fixed_data)
with open('fixed_data.json', 'w') as f:
f.write(fixed_data)
Now I have this cleaned up data:
{
"event": "password_change",
"username": "bcollin",
"old_password": ":\xC7\xF4\n\xAF))7C\x15\xD0\xF3\xDEiU;",
"new_password": "9\xFA\x99\xCA\xED\xF2XSG\xDD-\xC2\x8EzHk",
"timestamp": 1501858860000
}
This seems very accurate now because the sysadmin's name was Barry Collin and the username is bcollin
. Also, the timestamp is 1501858860000
which is Friday August 04, 2017 08:01:00am (-0700 DST)
and lines up with the scenario.
Now I needed to figure out what the passwords were.
The passwords were both an old and new password, and the scenario stated that the passwords were hashed. Additionally, the solution said to "reverse" the passwords...but hash functions can't be reversed. This took me MANY hours to figure out but I was able to understand it finally when I converted the strings to hexadecimal representation, and THEN reversed the strings.
:\xC7\xF4\n\xAF))7C\x15\xD0\xF3\xDEiU;
->3ac7f40aaf2929374315d0f3de69553b
9\xFA\x99\xCA\xED\xF2XSG\xDD-\xC2\x8EzHk
->39fa99caedf2585347dd2dc28e7a486b
Then, REVERSING these hex strings, I got b35596ed3f0d5134739292faa04f7ca3
and b684a7e82cd2dd7435852fdeac99af93
. These are double MD5 hashes of p4ssw0rd
and thisiscrazy
, the two passwords!