DefCamp-CTF-2015/entry-language

From aldeid
Jump to navigation Jump to search
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