NSec 2014 – Authenticator.exe

This weekend was the NSec CTF competition. I couldn’t fly up to Canada, but the challenges sounded fun and I decided to contribute to an anonymous team.

The NSSA has leaked their authenticator! Our job is to find a password for the agent “Shikishima.” Popping open auth.exe we get a login prompt. Opening it in IDA we immediately see some interesting strings…

Following these strings we see a decision made on what to print based on a single function. Checking this function out, we can see it iterates over an array, calls two other functions with this array and another array, and compares the results of the two. Checking out what it’s iterating over reveals it is checking to make sure the password only contains characters ’0′ to ’9′. That’s our charset.

Function that determines validity of username/password combo

The comparisons are just doing a direct comparison to ensure that the second array(of five bytes) is equivalent to the following indices of the first array: 1, 5, 8, 14, 17.

Looking into the first array’s creation function we see some magic numbers thrown around. Googling these tells us it’s part of some hashing library. Breakpointing after it’s created, I went ahead and compared the result for username “A” to the result for “A” on http://www.fileformat.info/tool/hash.htm and found that the resultant first array is a standard RipeMD160 hash.

Now all that’s left is to determine how the second array of five bytes is generated. We can see that the password is passed to this function(with a strange calling convention because it looks like this was built with Cygwin).

It’s looping four times, and working out the math on paper I noticed it was operating on every three bytes in the password. There’s only one block of transformations done, so it’s not too tedious to work it out on paper. Doing so and passing it to Wolframalpha for simplification gave us the following formula:

1
(70*s[0]-s[1]+12*s[2]-3892)&0xFF

where s is the current set of three bytes in the password. At the end of each loop this value is assigned to the next index of the resultant five byte CRC thing.

Since our keyspace is very small(digits), we can do a simple brute force on every repeated permutation of 0-9 with a length of 3 to retrieve the series triplets of digits that would go with a specific hash. A ruby script is below. Some junk testing code with the original formula is included above for posterity, that’s what the formula looked like before wolframalpha.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
require 'digest'
# str must be 15 long
def crc_password(str)
ret = []
i = 0
str.split("").each_slice(3) do |byteSet|
ret[i] =( (2 * (((byteSet[0].ord-0x30) + 4 * (byteSet[0].ord-0x30)) * 7) - (byteSet[1].ord-0x30)) + 4 * ((byteSet[2].ord-0x30) + 2 * (byteSet[2].ord-0x30)) - 4
) #& 0xFF # bitmask, ignore single-byte overflow after math is done just like the asm does.
i += 1
end
return ret
end
crc_password("AAAAAAAAAAAAAAA").each { |x|
print "0x#{x.to_s(16)}, "
}
puts "\n"
# let's try brute forcing possible passwords for the hash
username = "Shikishima"
userhash = Digest::RMD160.hexdigest(username)
explodeArr = userhash.scan(/../).map { |x| x.hex }
.each { |x|
print "0x#{x.to_s(16)}, "
}
puts "\n"
targetArr = [explodeArr[1], explodeArr[5], explodeArr[8], explodeArr[14], explodeArr[17]]
outPassword = ""
targetArr.each { |target|
possiblePer = (48..57).to_a.repeated_permutation(3).to_a
possiblePer.each { |s|
if target == (70*s[0]-s[1]+12*s[2]-3892)&0xFF
puts "Got target 0x#{target.to_s(16)} from #{s.map { |e| e.chr }.join("")}"
outPassword = "#{outPassword}#{s.map { |e| e.chr }.join("")}"
break
end
}
}
puts "User: #{username} Password: #{outPassword}"