TU CTF is an introductory CTF for teams that want to build their experience. We will have the standard categories of Web, Forensics, Crypto, RE, and Exploit, as well as some other categories we don't want to reveal just yet. If you have any questions, our contact is at the bottom of each page, but please read the official rules before sending us any emails.
This is a write-up for the Reversing challenges in
TU CTF 2017.
Funmail [25]
|
Figure 1: Challenge description |
This is straightforward. The challenge requires a password which is hardcoded within the binary as shown in the Figure 2.
|
Figure 2: Hardcoded password |
Provide the password and get the flag TUCTF{d0n7_h4rdc0d3_p455w0rd5}.
|
Figure 3: Flag for #1 |
Funmail 2.0 [50]
|
Figure 4: Challenge description |
Same drill as before. The password is hardcoded but the program is intentionally crippled and does not show the flag.
|
Figure 5: Deliberately crippled |
The binary contains a function
printFlag but it is not called from anywhere. We can just patch any call instruction such as the
call puts shown in Figure 6.
|
Figure 6: The instruction to patching |
to
call printFlag as shown below.
|
Figure 7: After applying the patch |
Running the patched binary we get the flag TUCTF{l0c4l_<_r3m073_3x3cu710n}.
|
Figure 8: Flag for #2 |
Unknown [200]
|
Figure 9: Challenge description |
The binary takes the flag as a command line argument. The length of the correct flag is 56 as evident in the disassembly listing below where it compares the result of strlen to 56.
|
Figure 10: Length of flag must be 56 |
Navigating down in the disassembly listing we have a function
check_letter which takes in the provided flag and an index. The function checks whether the character at the specified index within the flag is correct and returns 0 if so.
|
Figure 11: Checking the flag letter by letter |
check_letter is called from a loop, once for each of the 56 characters. If any check fail, the function returns one which is stored in the variable named
fail. Later, on the contents of this variable decide whether to print the success or the failure message.
|
Figure 12: To fail or not to fail |
The flag checking algorithm can be attacked using a brute-force approach. Each of the characters are checked individually, letter by letter without regards to the other characters.
To develop a bruteforce tool. our approach would be to set a breakpoint on 401c7d - the place where
check_letter is called. We would modify the string and the index that is passed. If the function returns 0, we know the character is correct. Using this approach we can try out various letters at each of the 56 positions. The code for the brute forcer tool developed in python using r2pipe is shown below.
import r2pipe
"""
| .----> 0x00401c71 8b55f4 mov edx, dword [local_ch]
| :||| 0x00401c74 488b45f8 mov rax, qword [local_8h]
| :||| 0x00401c78 89d6 mov esi, edx
| :||| 0x00401c7a 4889c7 mov rdi, rax
| :||| 0x00401c7d e80e020000 call check_letter
"""
flag = ""
r2 = r2pipe.open('unknown')
# Run with a dummy string
r2.cmd('doo {}'.format('a'*56))
# Set breakpoint
r2.cmd('db 0x401c7d')
for pos in xrange(56):
for ch in '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_+!{}':
# Run
r2.cmd('dc')
# Breakpoint at 0x00401c7d hits
# Write the character index
r2.cmd('dr esi={}'.format(pos))
# Write the flag obtained so far
r2.cmd('wz %s @ rdi' %format(flag+ch))
# Step over call
r2.cmd('dso')
# Check function result
rax = r2.cmdj('drj')['rax']
# Set rip back to start
r2.cmd('dr rip=0x401c71')
# Success
if rax == 0:
flag += ch
print '****************************************', flag
break
To speed up execution, after the function returns we set rip back to
401c71. This way we do not need to re-execute the binary each time. Running the script we get the flag TUCTF{w3lc0m3_70_7uc7f_4nd_7h4nk_y0u_f0r_p4r71c1p471n6!}. Below is a demo of the bruteforce tool in action.
Future [250]
|
Figure 13: Challenge description |
The last challenge of the RE track is a bit different from the rest. Quite unexpectedly we have been provided the C source code of the challenge. Hence there is no need to inspect the binary. The source looks like the image below.
|
Figure 14: Challenge source code |
The program takes the flag as input, performs some calculations on it and compares the result to a hardcoded string. If it matches our flag is correct. Navigating up within the source code we can see two functions
genMatrix and
genAuthString which perform these calculations.
|
Figure 15: The calculations |
We can employ a black box approach to solve this challenge. The entire system can be modelled in z3. To retrieve the flag, we can then query z3 if there is a possible input such that output matches the hardcoded string. The script is shown below.
from z3 import *
flag = [BitVec('ch'+str(i), 8) for i in xrange(25)]
mat = [[0 for i in xrange(5)] for i in xrange(5)]
# genMatrix
for i in xrange(25):
m = (i * 2) % 25
f = (i * 7) % 25
mat[m/5][m%5] = flag[f]
#genAuthString
auth = [0 for i in xrange(18)]
auth[0] = mat[0][0] + mat[4][4]
auth[1] = mat[2][1] + mat[0][2]
auth[2] = mat[4][2] + mat[4][1]
auth[3] = mat[1][3] + mat[3][1]
auth[4] = mat[3][4] + mat[1][2]
auth[5] = mat[1][0] + mat[2][3]
auth[6] = mat[2][4] + mat[2][0]
auth[7] = mat[3][3] + mat[3][2] + mat[0][3]
auth[8] = mat[0][4] + mat[4][0] + mat[0][1]
auth[9] = mat[3][3] + mat[2][0]
auth[10] = mat[4][0] + mat[1][2]
auth[11] = mat[0][4] + mat[4][1]
auth[12] = mat[0][3] + mat[0][2]
auth[13] = mat[3][0] + mat[2][0]
auth[14] = mat[1][4] + mat[1][2]
auth[15] = mat[4][3] + mat[2][3]
auth[16] = mat[2][2] + mat[0][2]
auth[17] = mat[1][1] + mat[4][1]
correct_output = "\x8b\xce\xb0\x89\x7b\xb0\xb0\xee\xbf\x92\x65\x9d\x9a\x99\x99\x94\xad\xe4"
s = Solver()
s.add(flag[0] == ord('T'))
s.add(flag[1] == ord('U'))
s.add(flag[2] == ord('C'))
s.add(flag[3] == ord('T'))
s.add(flag[4] == ord('F'))
s.add(flag[5] == ord('{'))
s.add(flag[24] == ord('}'))
for pos, ch in enumerate(correct_output):
s.add(auth[pos] == ord(ch))
if s.check() == sat:
m = s.model()
print ''.join([chr(m[e].as_long()) for e in flag])
Running the script we get the flag TUCTF{5y573m5_0f_4_d0wn!}
|
Figure 16: The flag, finally! |
No comments:
Post a Comment