NoshRun
Range (Easy) - by Leighlin Gunner Ramsay
The following post by 0xb0b is licensed under CC BY 4.0
Scenario
NoshRun's investors want a clean external pentest before the next round. You're handed the public surface and a single customer account — nothing else. Work the ordering app, the partner dashboard, the API, the promotions engine, and the internal ops panel, and see how far a startup that "shipped fast and locked it down later" really lets you get.
Summary
Summary
In NoshRun, we begin with external enumeration and discover a single web server on port 80. Virtual host fuzzing with FFuF uncovers several subdomains, including order, api, kitchen, ops, drivers, and promo.
On order.noshrun.local, we identify a SQL injection vulnerability in the search parameter. Using SQLMap with a UNION-based technique against the PostgreSQL backend, we enumerate the public schema, dump the orders table to recover the first flag, and extract owner email addresses from the restaurants table, noting mike@trestacos.lab for later use.
On api.noshrun.local, browsing the versioned driver endpoints directly reveals sensitive driver data exposed without authentication on the v1 endpoint, yielding the third flag.
Returning to order.noshrun.local, we authenticate with a self-registered account and place an order. Observing that promo codes are applied via a POST request to promo.noshrun.local, we capture the order request and multiple promo apply requests in Burp Suite, group them, and send them in parallel using last-byte sync to exploit a race condition. The server returns a 500 and leaks the fourth flag.
On kitchen.noshrun.local, we leverage the recovered email address to trigger a password reset for mike. By injecting a malicious X-Forwarded-Host header into the reset request and listening with a Python web server, we poison the reset link and redirect it to our machine. Using the captured token, we reset Mike's password, authenticate to the kitchen dashboard, and retrieve the second flag.
On ops.noshrun.local, directory brute-forcing with Feroxbuster reveals an exposed .git directory. We dump the repository with gitdumper and inspect server.js, which discloses the JWT signing logic, cookie name nosh_ops, and the payload structure. We forge an unsigned JWT with role: admin and set it as a cookie, gaining access to the admin panel. The /admin/system endpoint yields the fifth flag.
The /admin/export/download endpoint strips only literal ../ sequences, allowing path traversal via ....// payloads. At /admin/diag, we identify a command injection vector and catch a reverse shell as jerry using Penelope, recovering the seventh flag. Revisiting the export endpoint with a traversal payload, we read flag.txt from the root directory via the LFI, capturing the sixth flag and completing the scenario.
Recon
We start with a port scan and find only one open port, port 80.

Our scan shows that we are being redirected to http://noshrun.local/.

We add the following entry to our /etc/hosts file.
We try to identify the other virtual hosts using FFuF and find what we're looking for.

We will edit our /etc/hosts file as follows.
We visit the initial vhost. There we can order for a delivery.

www.noshrun.local
The www vhost serves the same page as the original one. If we scroll down, we find a discount code. Interesting. This could come in handy later - perhaps by exploiting a race condition by applying multiple discount codes to a single order.

order.noshrun.local
Let's take a look at the order vhost. Here, you can search for different options to order. Let's see if this is vulnerable to SQL injection using the payload ' and get an error. This is a strong indication that we can exploit it.

We try a UNION-based SQL injection to leak the database and use an ORDER BY clause to determine how many columns the query retrieves. It stops at 5, so we're dealing with four columns. Further testing has revealed that we're dealing with a PostgreSQL database. We'll make things easy on ourselves and continue with SQLMap.

We are trying to identify the database and have one in front of us named public.

We'll list the tables. There are some interesting ones among them. But as it turns out, we won't be able to read them all.

In the orders table, we then find the first flag!

We can list the columns in the password reset table, but we cannot retrieve their contents. This would have been useful for other endpoints that we haven't covered yet but that offer a password reset feature.

Let's take a look at our permissions for the tables and see that we can save ourselves the trouble of trying to query the sensitive tables for now.

The restaurant table is also of interest, as it lists the owners along with their email addresses. We can also retrieve the password hashes for each owner, but these cannot be cracked.

For now, we note down the following email.
We visit the orders page again. This time we sign in with a created account by us and make an order. We can see that we can apply the discount code to each order made, but only one for each order as it seems. With a closer look using chromium and preserving the logs we can see that a POST request is being made to the promo vhost.

The payload contains the order_id, the customer_id and the promo code. So this might enable us to apply multiple promos to a single order.

We catch each request of making an order and applying the promocode in Burp Suite and repeatly sent the /api/apply-promo to our repeater. We have now one order request and multiple promo request. We add them all to a single group and send them in parallel via last byte snyc so each request hit exactly by the last byte the server at the same time.



The server throws a 500 and we get the fourth flag.

api.noshrun.local
We move to the api vhost. This reveals us two path.

We visit the older one, revealing sensitive data of each driver and the third flag.


kitchen.noshrun.local
Next, we visit the kitchen vhost. This requires a login by an owner. Fortunatley we got them already thorugh our SQLinjection. The site offers a password reset functionality.

We make a password reset request for mike's account.

What we're trying now is called host header injection. We attempt to poison the host header using the X-Forwarded-Host header, hoping that the password reset link is crafted based on the host header value - so that the resulting reset link points to our server instead of the legitimate one.

We run a python web server and after a short duration we get the reset link.

Next, we use the link to reset the password of mike's account.

We now have access to the dashboard and the second flag.

drivers.noshrun.local
We visit the drivers vhost but there seems nothing interesting so far.

ops.noshrun.local
Next, we move on to the ops vhost. This requires a login. Unfortuntely we do not know yet, what authentication mechnisms are used. Neither do we know any admins or email adresses related to them. The login also is not vulnerable to username enumeration.

We run a directory scan using Feroxbuster with the common.txt file and find a .git folder. This might hold some sensitive information.

We use gitdumper to retrieve the data from the repository.

And we see a server.js file. We restore the files via a checkout.

We inspect the file.. and among other we see how the authentication is being handled. A JWT is used witht he cookie name nosh_ops and only a mail and role is set.

In my initial attempt I missed the git repostory and deduced the cookie name by the other vhosts and the structure from them and assumed a JWT was used.

It turns out only role is required, in this example the user diego is being used as that user is decribed as the admin in the scenario.
We try to craft our own JWT without a signature chosing the algorithm none.

We set the cookie and now have access to the admin dashboard.

There we can export files.


It turns out, it is vulnerable to Local File Inclusion, but no other files to export can be found yet.

At the endpoint diag we can query for other services internally and externally.

The systems endpoint reveals the fifth flag.

We move back to the diag endpoint and try some command injection via command substituion and try to catch a reverse shell. We set up a listener using Penelope.

After we run the check we successfully get a stable reverse shell as jerry.

From there we find the seventh flag.

In the root directory we find another flag owned by node. We might be able to read it via the LFI.

We head back to the export endpoint.

We catch the request.

Recalling the source code we see why we were able to perform a path traversal. Only ../ is being stripped.

We include the flag and retrieve the sixth flag and finish the scenario.

Last updated