RedTeam-TG

A blog for the RedTeam-TG CTF Team.

View on GitHub

Ecowas Portal

Is my program secure?
Flag: CTF_*

We’re given a binary, let’s run it

┌──(kali㉿kali)-[~/CTFs/HackerlabCTF/Basic/Ecowas]
└─$ ./ecowas_portal                           
ECOWAS ADMIN PORTAL : 
test
Flag wrong. Try again.  

Strings won’t help us. I like using gdb for static analysis. Let’s look at main.

┌──(kali㉿kali)-[~/CTFs/HackerlabCTF/Basic/Ecowas]
└─$ gdb -q ecowas_portal
Reading symbols from ecowas_portal...
(No debugging symbols found in ecowas_portal)
(gdb) run
Starting program: /home/kali/CTFs/HackerlabCTF/Basic/Ecowas/ecowas_portal 
ECOWAS ADMIN PORTAL : 
test
Flag wrong. Try again.[Inferior 1 (process 19161) exited with code 01]
(gdb) disass main
Dump of assembler code for function main:
   0x000055555555517e <+0>:     push   rbp
   0x000055555555517f <+1>:     mov    rbp,rsp
   0x0000555555555182 <+4>:     push   rbx
   0x0000555555555183 <+5>:     sub    rsp,0x118
   0x000055555555518a <+12>:    lea    rax,[rip+0xe73]        # 0x555555556004
   0x0000555555555191 <+19>:    mov    rdi,rax
   0x0000555555555194 <+22>:    call   0x555555555030 <puts@plt>
   0x0000555555555199 <+27>:    mov    DWORD PTR [rbp-0x120],0x2f
   0x00005555555551a3 <+37>:    mov    DWORD PTR [rbp-0x11c],0x41
   0x00005555555551ad <+47>:    mov    DWORD PTR [rbp-0x118],0x30
   0x00005555555551b7 <+57>:    mov    DWORD PTR [rbp-0x114],0x48
   0x00005555555551c1 <+67>:    mov    DWORD PTR [rbp-0x110],0x4a
   0x00005555555551cb <+77>:    mov    DWORD PTR [rbp-0x10c],0x25
   0x00005555555551d5 <+87>:    mov    DWORD PTR [rbp-0x108],0x27
   0x00005555555551df <+97>:    mov    DWORD PTR [rbp-0x104],0x1a
   0x00005555555551e9 <+107>:   mov    DWORD PTR [rbp-0x100],0x27
   0x00005555555551f3 <+117>:   mov    DWORD PTR [rbp-0xfc],0x57
   0x00005555555551fd <+127>:   mov    DWORD PTR [rbp-0xf8],0x15
   0x0000555555555207 <+137>:   mov    DWORD PTR [rbp-0xf4],0x49
   0x0000555555555211 <+147>:   mov    DWORD PTR [rbp-0xf0],0x10
   0x000055555555521b <+157>:   mov    DWORD PTR [rbp-0xec],0x2d
   0x0000555555555225 <+167>:   mov    DWORD PTR [rbp-0xe8],0x11
   0x000055555555522f <+177>:   mov    DWORD PTR [rbp-0xe4],0x2b
   0x0000555555555239 <+187>:   mov    DWORD PTR [rbp-0xe0],0xc
   0x0000555555555243 <+197>:   mov    DWORD PTR [rbp-0xdc],0xe
   0x000055555555524d <+207>:   mov    DWORD PTR [rbp-0xd8],0xc
   0x0000555555555257 <+217>:   mov    DWORD PTR [rbp-0xd4],0x37
   0x0000555555555261 <+227>:   mov    DWORD PTR [rbp-0xd0],0xb
   0x000055555555526b <+237>:   mov    DWORD PTR [rbp-0xcc],0xb
--Type <RET> for more, q to quit, c to continue without paging--
   0x0000555555555275 <+247>:   mov    DWORD PTR [rbp-0xc8],0xa
   0x000055555555527f <+257>:   mov    DWORD PTR [rbp-0xc4],0xa
   0x0000555555555289 <+267>:   mov    DWORD PTR [rbp-0xc0],0x6
   0x0000555555555293 <+277>:   mov    QWORD PTR [rbp-0x20],0x19
   0x000055555555529b <+285>:   mov    rdx,QWORD PTR [rip+0x2d8e]        # 0x555555558030 <stdin@GLIBC_2.2.5>
   0x00005555555552a2 <+292>:   lea    rax,[rbp-0xb0]
   0x00005555555552a9 <+299>:   mov    esi,0x80
   0x00005555555552ae <+304>:   mov    rdi,rax
   0x00005555555552b1 <+307>:   call   0x555555555060 <fgets@plt>
   0x00005555555552b6 <+312>:   lea    rax,[rbp-0xb0]

At this point the program initializes a bunch of local variables and print ECOWAS ADMIN PORTAL : and wait for our input. Then compare the length of our input with the value 25 at [rbp-0x20] and display Flag wrong. try again if the length is not 0x19, 25 in decimal

┌──(kali㉿kali)-[~/CTFs/HackerlabCTF/Basic/Ecowas]
└─$ ./ecowas_portal                           
ECOWAS ADMIN PORTAL : 
test
Flag wrong. Try again. 

If the lenght is 25 the program and the flag is wrong the program display Check failed

┌──(kali㉿kali)-[~/CTFs/HackerlabCTF/Basic/Ecowas]
└─$./ecowas_portal                                                                                                      ECOWAS ADMIN PORTAL : 
AAAAAAAAAAAAAAAAAAAAAAAAA        
Check failed   

Let’s find out what this check is

We know that our flag starts with CTF_, let’s do some test in gdb with CTF_AAAAAAAAAAAAAAAAAAAAA. we will go through our program instruction by instruction and understand how it works.

Let’s execute the program in gdb with a breakpoint on main and browse the flow with the instruction ni, shortcut for next instruction. When the program asks for it, we put our crafted flag.

(gdb) 
0x00005555555552b1 in main ()
(gdb) 
CTF_AAAAAAAAAAAAAAAAAAAAA
0x00005555555552b6 in main ()
(gdb) 

Let’s continue with our next instruction command At this point the program subtracts 1 from the value of rax(the result of the strlen() function), compares rax to rbx 0 then jump if below. it is clearly a loop that will iterate until 25 (the length of our flag)

(gdb) info registers 
rax            0x1a                26
rbx            0x0                 0
rcx            0x0                 0
rdx            0xfffbfefefc000000  -1127003780546560
rsi            0x5555555596b1      93824992253617
rdi            0x7fffffffde30      140737488346672
rbp            0x7fffffffdee0      0x7fffffffdee0
rsp            0x7fffffffddc0      0x7fffffffddc0
r8             0xfefe              65278
r9             0x7ffff7fa1c00      140737353751552
r10            0xfffffffffffffb87  -1145
r11            0x7ffff7e70d80      140737352502656
r12            0x555555555080      93824992235648
r13            0x0                 0
r14            0x0                 0
r15            0x0                 0
rip            0x555555555361      0x555555555361 <main+483>
eflags         0x202               [ IF ]
cs             0x33                51
ss             0x2b                43
ds             0x0                 0
es             0x0                 0
fs             0x0                 0
gs             0x0                 0
(gdb) x/3i $rip
=> 0x555555555361 <main+483>:   sub    rax,0x1
   0x555555555365 <main+487>:   cmp    rbx,rax
   0x555555555368 <main+490>:   jb     0x5555555552f6 <main+376>
(gdb) 

Let’s continue with our next instruction command. then the program calls an encrypt function to which it passes two parameters esi and edi

0x0000555555555312 <+404>:   movsx  eax,al
0x0000555555555315 <+407>:   mov    edx,DWORD PTR [rbp-0x14]
0x0000555555555318 <+410>:   mov    esi,edx
0x000055555555531a <+412>:   mov    edi,eax
0x000055555555531c <+414>:   call   0x555555555169 <encrypt>
0x0000555555555321 <+419>:   mov    BYTE PTR [rbp-0x22],al
0x0000555555555324 <+422>:   movzx  eax,BYTE PTR [rbp-0x22]
0x0000555555555328 <+426>:   cmp    al,BYTE PTR [rbp-0x21]
0x000055555555532b <+429>:   je     0x555555555348 <main+458>

let’s look at these parameters.

(gdb) p $esi
$5 = 0
(gdb) p $edi
$6 = 67
(gdb) x/2i $rip
=> 0x55555555531c <main+414>:   call   0x555555555169 <encrypt>
   0x555555555321 <main+419>:   mov    BYTE PTR [rbp-0x22],al
(gdb) 

└─$ python               
>>> chr(67)
'C'
>>> 

The program passes $edi=67 and $esi=0 to the encrypt function. Let’s take a closer look at this function.

The function encrypt subtracts 0x14, 20 in decimal from the value of edi and xor the result with the value of esi

(gdb) disass encrypt
Dump of assembler code for function encrypt:
   0x0000555555555169 <+0>:     push   rbp
   0x000055555555516a <+1>:     mov    rbp,rsp
   0x000055555555516d <+4>:     mov    DWORD PTR [rbp-0x4],edi
   0x0000555555555170 <+7>:     mov    DWORD PTR [rbp-0x8],esi
   0x0000555555555173 <+10>:    mov    eax,DWORD PTR [rbp-0x4]
   0x0000555555555176 <+13>:    sub    eax,0x14
   0x0000555555555179 <+16>:    xor    eax,DWORD PTR [rbp-0x8]
   0x000055555555517c <+19>:    pop    rbp
   0x000055555555517d <+20>:    ret    
End of assembler dump.

Let’s take a closer look at what happens after the encrypt function is executed. The return value of the encrypt function is stored in the $rax register, a byte (value at al register) of this value is copied to the memory address [rbp-0x22] and is compared to a byte of the value at the memory address [rbp-0x21]. if the values are equal then the program flow continues otherwise we get the message check failed.

0x000055555555531c <+414>:   call   0x555555555169 <encrypt>
0x0000555555555321 <+419>:   mov    BYTE PTR [rbp-0x22],al
0x0000555555555324 <+422>:   movzx  eax,BYTE PTR [rbp-0x22]
0x0000555555555328 <+426>:   cmp    al,BYTE PTR [rbp-0x21]
0x000055555555532b <+429>:   je     0x555555555348 <main+458>

At this stage the values are equal and our program can continue its execution.

(gdb) p/x $al
$12 = 0x2f

(gdb) x/b $rbp-0x21
0x7fffffffdebf: 0x2f

Let’s start again from the beginning but this time with breakpoints Let’s place the first breakpoint at the input of the encrypt function and the second break before the jump if equal at the exit of the encrypt function.

let’s launch the execution of our program with our crafted flag and at each loop let’s eximinate the values of the esi and edi registers at the first break, then the values of the al register and the memory address [rbp-0x21] at the second break

(gdb) break *0x000055555555531c
Breakpoint 3 at 0x55555555531c
(gdb) break *0x000055555555532b
Breakpoint 4 at 0x55555555532b
(gdb) run
Starting program: /home/kali/CTFs/HackerlabCTF/Basic/Ecowas/ecowas_portal 
ECOWAS ADMIN PORTAL : 
CTF_AAAAAAAAAAAAAAAAAAAAA

gdb) c
Continuing.
Breakpoint 3, 0x000055555555531c in main ()
(gdb) p $esi
$2 = 0
(gdb) p $edi
$3 = 67
(gdb) c
Continuing.

Breakpoint 4, 0x000055555555532b in main ()
(gdb) x/b $rbp-0x21
0x7fffffffdebf: 0x2f
(gdb) p/x $al
$4 = 0x2f
(gdb) c
Continuing.

Breakpoint 3, 0x000055555555531c in main ()
(gdb) p $esi
$5 = 1
(gdb) p $edi
$6 = 84
(gdb) c
Continuing.

Breakpoint 4, 0x000055555555532b in main ()
(gdb) x/b $rbp-0x21
0x7fffffffdebf: 0x41
(gdb) p/x $al
$7 = 0x41
(gdb) c
Continuing.

Breakpoint 3, 0x000055555555531c in main ()
(gdb) p $esi
$8 = 2
(gdb) p $edi
$9 = 70
(gdb) c
Continuing.

Breakpoint 4, 0x000055555555532b in main ()
(gdb) p/x $al
$10 = 0x30
(gdb) x/b $rbp-0x21
0x7fffffffdebf: 0x30

Breakpoint 3, 0x000055555555531c in main ()
(gdb) p $esi
$11 = 3
(gdb) p $edi
$12 = 95
(gdb) c
Continuing.

Breakpoint 4, 0x000055555555532b in main ()
(gdb) p/x $al
$13 = 0x48
(gdb) x/b $rbp-0x21
0x7fffffffdebf: 0x48
(gdb) 


Have you noticed anything? Yes, that’s exactly it. At each iteration, one character of our flag and the loop counter are passed to the encrypt function. It’s not over yet. Look at the values in the al register and the bunch of variables I mentioned at the beginning. Yes, the byte of the value at memory address [rbp-0x21] to which the value of the al register is compared at each iteration is indeed one of these values.

└─$ python 
>>>
>>> chr(67)
'C'
>>> chr(84)
'T'
>>> chr(70)
'F'
>>> chr(95)
'_'
>>> 

let’s summarize

The program declares and initializes a bunch of variables, then it asks for a user input (the 25 characters flag), then it passes character by character the flag to a function encrypt which subtracts 0x14 from each character of the flag and xor the result with the counter value of the loop. The result of this function is then compared in turn to the variables previously initialized by the program. If there is a match the program continues to the second character otherwise the program stops with the message check failed.

Now that we know how the program works, we can reverse it. I have written a python program to do this.

I converted to decimal the first 25 variables that our program initializes that I stored in a list then I browse the list by doing the reverse of what the encrypt function does.

#!/usr/bin/env python3
from pwn import xor

variables = [47,65,48,72,74,37,39,26,39,87,21,73,16,45,17,43,12,14,12,55,11,11,10,10,6]
flag=''

for i in range(len(variables)):
   flag=flag+chr(ord(xor(variables[i],i))+20)
   i = i+1
print(flag)
└─$ python reverse.py       
CTF_b451Cr3V0438032832012

Done!!!