DefCamp-CTF-2015/entry-language
You are here | entry-language
|
Description
The challenge(r100) is worth 100 points. We have to deal with a 64-bit ELF.
MD5 | 7f24336a9475b4a6a79086f29ec0949a |
---|---|
SHA1 | 6a9331b8d452459a481c5946d727939945cc83a9 |
SHA256 | 8c481c589e9f95acbfdc20b54f5965017604a4c149dd72ec6bde55a5ea2a11bc |
File | ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=0f464824cc8ee321ef9a80a799c70b1b6aec8168, stripped |
Running the binary
The binary doesn't expect any parameter. The password is prompted at runtime:
$ ./r100 Enter the password: 123456 Incorrect password!
Analysis
Main
The main function is straightforward to analyze.
.text:00000000004007E8 ; int __cdecl main(int, char **, char **)
.text:00000000004007E8 main proc near
.text:00000000004007E8
.text:00000000004007E8 my_password = byte ptr -110h
.text:00000000004007E8 var_8 = qword ptr -8
.text:00000000004007E8
.text:00000000004007E8 push rbp
.text:00000000004007E9 mov rbp, rsp
.text:00000000004007EC sub rsp, 110h
.text:00000000004007F3 mov rax, fs:28h
.text:00000000004007FC mov [rbp+var_8], rax
.text:0000000000400800 xor eax, eax
.text:0000000000400802 mov edi, offset format ; "Enter the password: "
.text:0000000000400807 mov eax, 0
.text:000000000040080C call _printf
.text:0000000000400811 mov rdx, cs:stdin ; stream
.text:0000000000400818 lea rax, [rbp+my_password]
.text:000000000040081F mov esi, 0FFh ; n
.text:0000000000400824 mov rdi, rax ; s
.text:0000000000400827 call _fgets
.text:000000000040082C test rax, rax
.text:000000000040082F jz short loc_400866
.text:0000000000400831 lea rax, [rbp+my_password]
.text:0000000000400838 mov rdi, rax
.text:000000000040083B call check_password
.text:0000000000400840 test eax, eax
.text:0000000000400842 jnz short fail
.text:0000000000400844 mov edi, offset s ; "Nice!"
.text:0000000000400849 call _puts
.text:000000000040084E mov eax, 0
.text:0000000000400853 jmp short loc_40086B
.text:0000000000400855 ; ---------------------------------------------------------------------------
.text:0000000000400855
.text:0000000000400855 fail:
.text:0000000000400855 mov edi, offset aIncorrectPassw ; "Incorrect password!"
.text:000000000040085A call _puts
.text:000000000040085F mov eax, 1
.text:0000000000400864 jmp short loc_40086B
.text:0000000000400866 ; ---------------------------------------------------------------------------
At offset 0x400827, the user password is asked (call to _fgets) and passed to the sub_4006FD function (renamed check_password) at offset 0x40083B. If this function returns a non zero value, the password is invalid and the program will output the string Incorrect password!. Otherwise, it will display Nice!.
check_password
This function is manipulating the user input (password) and compares it to the result of a rather complex computation based on 3 strings (Dufhbmf, pG`imos, ewUglpt):
.text:00000000004006FD check_password proc near
.text:00000000004006FD
.text:00000000004006FD my_password = qword ptr -38h
.text:00000000004006FD count_i = dword ptr -24h
.text:00000000004006FD var_20 = qword ptr -20h
.text:00000000004006FD var_18 = qword ptr -18h
.text:00000000004006FD var_10 = qword ptr -10h
.text:00000000004006FD
.text:00000000004006FD push rbp
.text:00000000004006FE mov rbp, rsp
.text:0000000000400701 mov [rbp+my_password], rdi
.text:0000000000400705 mov [rbp+count_i], 0
.text:000000000040070C mov [rbp+var_20], offset aDufhbmf ; "Dufhbmf"
.text:0000000000400714 mov [rbp+var_18], offset aPgImos ; "pG`imos"
.text:000000000040071C mov [rbp+var_10], offset aEwuglpt ; "ewUglpt"
.text:0000000000400724 mov [rbp+count_i], 0 ; i = 0
.text:000000000040072B jmp short loc_40079B
.text:000000000040072D ; ---------------------------------------------------------------------------
.text:000000000040072D
.text:000000000040072D loc_40072D:
.text:000000000040072D mov ecx, [rbp+count_i]
.text:0000000000400730 mov edx, 55555556h
.text:0000000000400735 mov eax, ecx
.text:0000000000400737 imul edx
.text:0000000000400739 mov eax, ecx
.text:000000000040073B sar eax, 1Fh
.text:000000000040073E sub edx, eax
.text:0000000000400740 mov eax, edx
.text:0000000000400742 add eax, eax
.text:0000000000400744 add eax, edx
.text:0000000000400746 sub ecx, eax
.text:0000000000400748 mov edx, ecx
.text:000000000040074A movsxd rax, edx
.text:000000000040074D mov rsi, [rbp+rax*8+var_20] ; var_20 = 'Dufhbmf'
.text:0000000000400752 mov ecx, [rbp+count_i] ; ecx = i
.text:0000000000400755 mov edx, 55555556h
.text:000000000040075A mov eax, ecx
.text:000000000040075C imul edx
.text:000000000040075E mov eax, ecx
.text:0000000000400760 sar eax, 1Fh
.text:0000000000400763 sub edx, eax
.text:0000000000400765 mov eax, edx
.text:0000000000400767 add eax, eax
.text:0000000000400769 cdqe
.text:000000000040076B add rax, rsi
.text:000000000040076E movzx eax, byte ptr [rax]
.text:0000000000400771 movsx edx, al
.text:0000000000400774 mov eax, [rbp+count_i]
.text:0000000000400777 movsxd rcx, eax
.text:000000000040077A mov rax, [rbp+my_password]
.text:000000000040077E add rax, rcx
.text:0000000000400781 movzx eax, byte ptr [rax]
.text:0000000000400784 movsx eax, al
.text:0000000000400787 sub edx, eax
.text:0000000000400789 mov eax, edx
.text:000000000040078B cmp eax, 1
.text:000000000040078E jz short loop_next
.text:0000000000400790 mov eax, 1
.text:0000000000400795 jmp short loc_4007A6
.text:0000000000400797 ; ---------------------------------------------------------------------------
.text:0000000000400797
.text:0000000000400797 loop_next:
.text:0000000000400797 add [rbp+count_i], 1
.text:000000000040079B
.text:000000000040079B loc_40079B:
.text:000000000040079B cmp [rbp+count_i], 0Bh ; while i<11
.text:000000000040079F jle short loc_40072D
.text:00000000004007A1 mov eax, 0
.text:00000000004007A6
.text:00000000004007A6 loc_4007A6:
.text:00000000004007A6 pop rbp
.text:00000000004007A7 retn
.text:00000000004007A7 check_password endp
Using the pseudo-code plugin (press F5) really helps. It produces the following code:
signed __int64 __fastcall check_password(__int64 a1)
{
signed int count_i; // [sp+14h] [bp-24h]@1
const char *v3; // [sp+18h] [bp-20h]@1
const char *v4; // [sp+20h] [bp-18h]@1
const char *v5; // [sp+28h] [bp-10h]@1
v3 = "Dufhbmf";
v4 = "pG`imos";
v5 = "ewUglpt";
for ( count_i = 0; count_i <= 11; ++count_i )
{
if ( (&v3)[8 * (count_i % 3)][2 * (count_i / 3)] - *(_BYTE *)(count_i + a1) != 1 )
return 1LL;
}
return 0LL;
}
It is now trivial to understand what the function does. It actually rotates between characters of the 3 strings (modulo the counter) to compute the expected characters of the password. We can write a script to display the expected password (notice that the 3 strings have been converted to hex values).
Solution
The following script:
#!/usr/bin/env python3
s = [[0x44, 0x75, 0x66, 0x68, 0x62, 0x6D, 0x66],
[0x70, 0x47, 0x60, 0x69, 0x6D, 0x6F, 0x73],
[0x65, 0x77, 0x55, 0x67, 0x6C, 0x70, 0x74]]
flag = []
for i in range(12):
flag.append(s[i%3][2*(i//3)]-1)
print(''.join([chr(i) for i in flag]))
will output the following flag:
Code_Talkers
We can validate this flag:
$ ./r100 Enter the password: Code_Talkers Nice!
Comments
Keywords: defcamp dctf-2015 ctf challenge reversing