Fall 2015
CODEBREAKER CHALLENGE 3.0
1
Challenge Scenario
NSA has discovered that the leadership of a
terrorist organization is using a new method of
communicating secret messages to its
operatives in the field and has provided each
individual with a unique program for decoding
messages. Your mission is to reverse-engineer
this software and develop capabilities to exploit
the secret messaging component.
2
The Challenge
There are 4 different levels or "tasks" to this
challenge problem
Task 1: Determine how to execute the hidden
functionality
Task 2: Bypass an authentication check
Task 3: Create an encoder program
Task 4: Spoof a message to a high-value target
Each task gets progressively harder and
builds off previous ones
3
The Challenge (cont.)
Challenge materials and instructions can be
found at [Link]
Register for an account with your .edu email
address
4
Reverse Engineering Tips
Examine strings in the binary using IDA
Look for clues that relate to the functionality you are trying
to find / reverse
Utilize IDA xrefs to find code that references the string(s) of
interest
Utilize symbols (e.g., function names) to help determine
what a section of code does
Try setting debugger breakpoints to help RE code
Single-step after hitting a breakpoint and see how the
values in registers/memory change
Look for the result of interesting computations. You can
sometimes get the data you need from memory
Leverage online resources, e.g.,Intel manuals, RE
lectures, etc. for help on reverse-engineering
5
Technical Walkthrough
2014 Codebreaker Challenge on Windows
using IDA Pro Demo
This binary can be downloaded from
[Link]
6
Running the program
7
Running the program (2)
8
Disassemble
Disassemble the Codebreaker2 binary
If asked whether you want to use Proximity View
Click no
Use graph view
9
Disassemble (2)
10
Disassemble (3)
11
Observe Strings
Observe the strings that show up in IDA
Click Views->Open Subviews->Strings
You should see the strings that are displayed when
you run the program
Yahoo! Weather forecast for
Full forecast available at:
Weatherman version 6.8.1
12
Observe Strings (2)
13
Observe Strings (3)
14
Observe Strings (4)
15
C:\\tmp\\secrets
Double click on the “C:\\tmp\\secrets” string
This takes you to the data section of the binary
where the string is stored
To the right of the string are cross references
to this address (show up as DATA XREF in
IDA)
Press ctrl-x to pull up a cross-references
window; you will see two different references
16
C:\\tmp\\secrets (2)
17
C:\\tmp\\secrets (3)
18
Double-click Reference
You should now be looking at disassembled
x86 code
We just leveraged the fact that in order to use
“C:\\tmp\\secrets” in the program, the code had to
reference the address in the data section of the
program where the string was stored.
Using xrefs in IDA is a quick and easy way to
find interesting code sections
19
Double-click Reference (2)
20
Explore Code Block
21
Explore Code Block (2)
22
Explore Code Block (3)
23
Running the program (for real)
24
Explore Code Block (4)
25
Explore Code Block (5)
26
Running the program (for real)(2)
27
Tier 1 Complete!
Pretty straight forward
Just looking at the strings may have been
enough to get you through this
… on to Tier 2!
28
Running the program (for real)(2)
29
Explore Code Block (6)
30
Explore Code Block (7)
31
Explore Code Block (8)
32
Explore Code Block (9)
33
getPasswordFromUsername
34
What does this code do?
mov edx, 0xAC769185 // edx = 0xAC769185
mov eax, ecx // ecx = input value
imul edx // edx:eax = eax * edx
lea eax, [edx + ecx*0x1] // eax = edx + ecx
mov edx, eax // edx = eax
sar edx, 0x6 // arith right shift; edx = edx >> 0x6
mov eax, ecx // eax = ecx
sar eax, 0x1f // eax = eax >> 0x1f (31)
mov ebx, edx // ebx = edx
sub ebx, eax // ebx = ebx - eax
mov eax, ebx // eax = ebx
imul eax, eax, 0x5f // edx:eax = eax * 0x5f (95)
mov edx, ecx // edx = ecx
sub edx, eax // edx = edx – eax
// edx is the final result
35
Signed Division and Remainder
The code computes: edx = ecx % 95
Why multiply by 0xAC769185 and where did that
number come from?
Division is a time consuming operation
When the divisor is a constant, the compiler can
optimize the computation
The basic trick is to multiply by a “magic value”
(~ 232/d) and extract the leftmost 32 bits of the
product
The following site computes these numbers for
you: [Link]
36
getPasswordFromUsername (2)
37
Running the program (3)
38
Running the program (4)
39
Tier 2 Complete!
Required either reverse engineering the
password derivation function or just using a
debugger to see the computed value
… on to Tier 3!
40
Running the program (4)
41
Running the program (5)
42
Running the program (6)
43
Explore Code Block (10)
44
Inside encrypt (1)
45
Inside encrypt (2)
46
SHA256 Functions
47
Inside encrypt (3)
48
Initial computation
49
Subsequent computations
50
So…
byte buffer[] = SHA256(secret key)
byte result[0-7] = (A * buffer[0-7]) + C
byte result[8-15] = (A * result[0-7]) + C
byte result[16-23] = (A * result[8-15]) + C
… and same for the derived password
From now on we will refer to:
‘result’ from secret key as X
‘result’ from derived password as Y
51
Inside encrypt (4)
52
Inside encrypt (5)
53
What’s happening
54
Tier 3 Solution
Write a program to:
Compute the password given a username*
Base64 decode the string**
Run the encryption algorithm in reverse to decrypt a
given input
plaintext[i] = ciphertext[i] ^ (X[i] ^ Y[i])
where you compute X and Y from the given secret key
and derived password
*Or just get it by running the Codebreaker binary in a
debugger
** Or do it online, use existing programs, etc.
55
Tier 3 Solution (2)
… on to Tier 4!
56
Tier 4 Solution
You’d get the following message via email:
To:Tier3_Codebreakers
Msg:z/W4uhaRU+8N7/qKSzuwXfNPZ8Tf867ajNJ33tU85wTtgXywSTefsB86
3g26B5rR2Q9/oqFztnrT6nTUq8JMuJbWTUD5YIsN7uTbw6F9/GzsgdBG567
A303kSOTEM+Fsp7QialTheU9/W/02jiGZUeW6yYdhaMrDP6vDJlq+MNRMX
Zg8ereNKyBQDvGPR4iHUNBH0CP2oSb+/9WkeupRs2mkkoBAo8rdirZu0J
NOwnugF9T/Kw0R9EHVxNneIdDiG0m8O2UilAUaR6pKHTu1xS6MfkVh5C
KArmVTY6MAC6Vi8CnZJvM/WZT6cg6dLesgFrtXX8uwhzcTYwLe+t2m5Mv
vDtiZy0t9pLdBNAr6N3+znHCDInAIGlJe3shipbBQoqKxbb8VNY9DR4fJMG9
YIhnMyYn1g+mLGC41niWUqTbbBrnwSJgZ+u5AwLcpHXkA649O4IoHEyV+
bgWL/bKFVWL7KDAzEx4FdhwnYfe25SHirjFxVTrNiyR/FPPa/MgfixkrlVrZkY
GsZNlvDZjG8sxrH9tQ0kkOO7yaplHsBaYiwqCGVKum55iRyKgG1q2RuDAY
yzs1uvA2JnHnBZW1gEOpyy6RPiPuV7/z5DyQiMYhEzDA1Y9Dne92BagY0a
FTsCNMRX+W+L1XepcN49BEUDEMUKuUnLT6G+QuLw==
57
Tier 4 Solution (2)
Maybe there’s a problem with the encryption
scheme…
58
Tier 4 Solution (3)
plaintext[i] = ciphertext[i] ^ (X[i] ^ Y[i])
byte buffer[] = SHA256(secret key)
byte X[0-7] = (A * buffer[0-7]) + C
byte X[8-15] = (A * X[0-7]) + C
…
byte buffer[] = SHA256(derived password)
byte Y[0-7] = (A * buffer[0-7]) + C
…
We have the ciphertext, and the constants
59
Tier 4 Solution (4)
plaintext[0-7] = ciphertext[0-7] ^ (X[0-7] ^ Y[0-7])
byte buffer[] = SHA256(secret key)
byte X[0-7] = (A * buffer[0-7]) + C
byte X[8-15] = (A * X[0-7]) + C
…
byte buffer[] = SHA256(derived password)
byte Y[0-7] = (A * buffer[0-7]) + C
…
All messages include the username too, from which we can
derive the password, and then the SHA256 hash
60
Tier 4 Solution (4)
plaintext[0-7] = ciphertext[0-7] ^ (X[0-7] ^ Y[0-7])
byte buffer[] = SHA256(secret key)
byte X[0-7] = (A * buffer[0-7]) + C
byte X[8-15] = (A * X[0-7]) + C
…
byte buffer[] = SHA256(derived password)
byte Y[0-7] = (A * buffer[0-7]) + C
…
We know the plaintext at the beginning too, since the
program always adds ‘---MESSAGE BEGIN---’
61
Tier 4 Solution (5)
plaintext[0-7] = ciphertext[0-7] ^ (X[0-7] ^ Y[0-7])
byte buffer[] = SHA256(secret key)
byte X[0-7] = (A * buffer[0-7]) + C
byte X[8-15] = (A * X[0-7]) + C
…
byte buffer[] = SHA256(derived password)
byte Y[0-7] = (A * buffer[0-7]) + C
…
Reversing the encryption equation, we get:
X[0-7] = plaintext[0-7] ^ ciphertext[0-7] ^ Y[0-7]
62
Tier 4 Solution (6)
plaintext[0-7] = ciphertext[0-7] ^ (X[0-7] ^ Y[0-7])
byte buffer[] = SHA256(secret key)
byte X[0-7] = (A * buffer[0-7]) + C
byte X[8-15] = (A * X[0-7]) + C
…
byte buffer[] = SHA256(derived password)
byte Y[0-7] = (A * buffer[0-7]) + C
…
X[8-15] and Y[8-15] (and on) are computed from the
SHA256 bytes, so we have the rest of those too.
63
Tier 4 Solution (7)
Decrypted message:
Congratulations!! You have solved the final tier of
the Codebreaker Challenge! Please send us an
email at senior_project@[Link] and let us
know how you solved it. We hope you have
enjoyed working on this problem. If you are
interested in solving even more challenging and
exciting problems on a daily basis that directly
impact our national security posture and military
forces around the world, consider applying for a
career at NSA -- [Link]
64
Questions
?
… if this work interests you, consider applying for an internship or full-time
position at [Link]
Use event code 483-1 to associate yourself with the Codebreaker Challenge
65
Extra Slides
66
64-bit Data Types
Consider the following program:
int main(){
char one = 0x11; // sizeof(char) == 1
char two = 0x22;
int three = 0x33333333; // sizeof(int) == 4
int four = 0x44444444;
long long five = 0x5555555555555555; // sizeof(long long) == 8
long long six = 0x6666666666666666;
printf("8b: %hu 32b: %u 64b: %llu\n", one + two, three + four, five + six);
return 0;
}
67
64-bit Data Types – x86_64
Part 1: Move values onto the stack
mov BYTE PTR [rbp-0x2],0x11
mov BYTE PTR [rbp-0x1],0x22
mov DWORD PTR [rbp-0xc],0x33333333
mov DWORD PTR [rbp-0x8],0x44444444
mov DWORD PTR [rbp-0x20],0x55555555
mov DWORD PTR [rbp-0x1c],0x55555555
mov DWORD PTR [rbp-0x18],0x66666666
mov DWORD PTR [rbp-0x14],0x66666666
68
64-bit Data Types – x86_64
Part 2: Load into registers and compute
mov rax,QWORD PTR [rbp-0x18] // 0x6666666666666666 in rax
mov rdx,QWORD PTR [rbp-0x20] // 0x7777777777777777 in rdx
lea rcx,[rdx+rax*1] // rcx = rax + rdx*1
mov eax,DWORD PTR [rbp-0x8] // 0x44444444 in eax
mov edx,DWORD PTR [rbp-0xc] // 0x33333333 in edx
add edx,eax // edx = edx + eax
movsx esi,BYTE PTR [rbp-0x2] // 0x11 in esi
movsx eax,BYTE PTR [rbp-0x1] // 0x22 in eax
add esi,eax // esi = esi + eax
69
64-bit Data Types – x86
No 64-bit registers
long long five = 0x5555555555555555; // sizeof(long long) == 8
long long six = 0x6666666666666666;
Let’s make it work with 32-bit ones!
70
64-bit Data Types – x86
Part 1: Move values onto the stack (same as x86_64)
mov BYTE PTR [ebp-1],0x11
mov BYTE PTR [ebp-2],0x22
mov DWORD PTR [ebp-8],0x33333333
mov DWORD PTR [ebp-12],0x44444444
mov DWORD PTR [ebp-24],0x55555555
mov DWORD PTR [ebp-20],0x55555555
mov DWORD PTR [ebp-32],0x66666666
mov DWORD PTR [ebp-28],0x66666666
71
64-bit Data Types – x86
Part 2: Load into registers and compute
mov eax,DWORD PTR [ebp-32] // 0x66666666 in eax
mov edx,DWORD PTR [ebp-28] // 0x66666666 in edx
add eax,DWORD PTR [ebp-24] // eax = eax + 0x55555555
adc edx,DWORD PTR [ebp-20] // edx = edx + 0x55555555 + CF
…
mov eax,DWORD PTR [ebp-12] // 0x444444 in eax
add eax,DWORD PTR [ebp-8] // eax = eax + 0x33333333
…
movsx edx,BYTE PTR [ebp-1] // 0x11 in edx
movsx eax,BYTE PTR [ebp-2] // 0x22 in eax
lea eax,[edx+eax] // eax = edx + eax*
72
Strings?
The strings that are used here don’t appear in
the list of strings
… or do they? Scroll down in the code to try
and spot them.
73
Explore Code Block (10)
74
Explore Code Block (11)
75
Inside special_printf
76
Inside special_printf (2)
77
Mystery solved!
78