FlashGenius Logo FlashGenius
Login Sign Up

Offensive Security Prep Pack for eJPT, OSCP, PNPT, OSWE, and OSEP— Practice Questions: Web Application Exploitation Domain

Test your Offensive Security knowledge (eJPT, OSCP, PNPT, OSWE, and OSEP) with 10 free practice questions from the Web Application Exploitation (OSWE-focused) domain. Includes detailed explanations and answers.

OSPP Practice Questions

Master the Web Application Exploitation (OSWE-focused) Domain

Test your knowledge in the Web Application Exploitation (OSWE-focused) domain with these 10 practice questions. Each question is designed to help you prepare for the OSPP certification exam with detailed explanations to reinforce your learning.

Question 1

You are testing a Node.js/Express application that exposes a JSON-based login API:

// app.js
app.post('/api/login', async (req, res) => {
  const { username, password } = req.body;

  if (!username || !password) {
    return res.status(400).json({ error: 'Missing credentials' });
  }

  const user = await db.users.findOne({ username: username });
  if (!user) {
    return res.status(401).json({ error: 'Invalid credentials' });
  }

  // Passwords are stored as bcrypt hashes
  const ok = await bcrypt.compare(password, user.password);
  if (!ok) {
    return res.status(401).json({ error: 'Invalid credentials' });
  }

  return res.json({ token: signJwt({ id: user._id, role: user.role }) });
});

The db.users.findOne function is a thin wrapper around the MongoDB Node.js driver:

// db.js
exports.users = {
  findOne: async (query) => {
    return await client.db('app').collection('users').findOne(query);
  }
};

You notice that the application uses body-parser with extended: true, and that it does not perform any type checking on username. Which of the following payloads is most likely to allow you to bypass authentication and log in as an arbitrary user without knowing their password?

A) Send a JSON body with {"username":{"$ne":null},"password":"anything"} to exploit a classic MongoDB query injection.
B) Send a JSON body with {"username":"admin","password":{"$gt":""}} to force the password comparison to always succeed.
C) Send a JSON body with {"username":{"$regex":".*"},"password":"anything"} to match any user and skip the password check.
D) Send a JSON body with {"username":{"$ne":"nonexistent"},"password":"anything"} and then abuse the fact that bcrypt will treat a non-string password as always true.
Show Answer & Explanation

Correct Answer: A

Explanation: The core issue is that username is passed into the MongoDB query without type validation. With extended body parsing, an attacker can submit an object containing MongoDB operators (e.g., $ne) and change query semantics.

const user = await db.users.findOne({ username: username });

With payload A, the effective query becomes:

{ "username": { "$ne": null } }

This matches a user record where username is not null (often returning the first user). The other options either do not affect the DB query (B), are less predictable (C), or rely on incorrect bcrypt behavior (D).

Question 2

You are performing a white-box assessment of a PHP-based bug tracking system. The application allows project managers to upload a project logo. You find the following code in upload_logo.php:

<?php
session_start();
require_once 'auth.php';
require_once 'db.php';

if (!isProjectManager($_SESSION['user_id'], $_POST['project_id'])) {
    http_response_code(403);
    die('Forbidden');
}

if (!isset($_FILES['logo']) || $_FILES['logo']['error'] !== UPLOAD_ERR_OK) {
    die('Upload error');
}

$allowed = ['image/png', 'image/jpeg'];
if (!in_array($_FILES['logo']['type'], $allowed, true)) {
    die('Invalid file type');
}

$ext = pathinfo($_FILES['logo']['name'], PATHINFO_EXTENSION);
$filename = 'logo_' . intval($_POST['project_id']) . '.' . $ext;
$target = __DIR__ . '/uploads/' . $filename;

if (!move_uploaded_file($_FILES['logo']['tmp_name'], $target)) {
    die('Failed to move file');
}

$stmt = $db->prepare('UPDATE projects SET logo_path = ? WHERE id = ?');
$stmt->execute(['/uploads/' . $filename, $_POST['project_id']]);

header('Location: project.php?id=' . intval($_POST['project_id']));

In project.php you see:

<?php
$project_id = intval($_GET['id']);
$stmt = $db->prepare('SELECT name, logo_path FROM projects WHERE id = ?');
$stmt->execute([$project_id]);
$project = $stmt->fetch(PDO::FETCH_ASSOC);

if ($project && $project['logo_path']) {
    echo '<img src="' . htmlspecialchars($project['logo_path'], ENT_QUOTES, 'UTF-8') . '" alt="Logo">';
}

Apache configuration includes:

<Directory /var/www/html/uploads>
    Options -Indexes +ExecCGI
    AddHandler application/x-httpd-php .php .phtml
    AllowOverride None
    Require all granted
</Directory>

You have a valid low-privileged account and can create a project where you are the project manager. You want RCE. Which strategy is the most realistic and effective given the code and configuration?

A) Upload a PHP file by spoofing MIME type and naming it logo_1.php, then access it.
B) Upload a polyglot PNG containing PHP and rely on Apache to execute .png as PHP.
C) Upload a .phtml file containing PHP code while spoofing MIME type to image/jpeg, then access it via /uploads/logo_<id>.phtml.
D) Inject javascript: into logo_path and pivot from XSS to RCE.
Show Answer & Explanation

Correct Answer: C

Explanation: The server trusts the client-supplied MIME type and lets you control the stored extension via the original filename. Apache executes .phtml as PHP in /uploads, enabling a direct upload-to-exec chain.

Question 3

You are reviewing a Java DAO method:

public Product getProductById(String id) throws SQLException {
    String sql = "SELECT id, name, price, description FROM products WHERE id = " + id;
    Statement stmt = conn.createStatement();
    ResultSet rs = stmt.executeQuery(sql);

    if (rs.next()) {
        Product p = new Product();
        p.setId(rs.getInt("id"));
        p.setName(rs.getString("name"));
        p.setPrice(rs.getBigDecimal("price"));
        p.setDescription(rs.getString("description"));
        return p;
    }
    return null;
}

Controller:

@GetMapping("/product")
public String product(@RequestParam("id") String id, Model model) {
    Product p = productDao.getProductById(id);
    model.addAttribute("product", p);
    return "product";
}

DB user has SELECT/INSERT/UPDATE/DELETE on products only; no privileges on users. Which attack path is most realistic to reach an auth bypass given constraints?

A) UNION select from users to dump admin creds.
B) Stacked query to update admin password in users.
C) Modify products data and chain into business logic for escalation.
D) Write a webshell via SELECT ... INTO OUTFILE and then read users.
Show Answer & Explanation

Correct Answer: C

Explanation: Direct access to users is blocked by DB privileges. The realistic OSWE chain is to abuse what you can control (products) and pivot into an application logic weakness (e.g., pricing/checkout/role logic).

Question 4

Flask admin endpoint:

from flask import Flask, request, render_template_string, session, abort

app = Flask(__name__)
app.secret_key = 'super-secret-key'

@app.route('/admin/panel')
def admin_panel():
    if not session.get('is_admin'):
        abort(403)

    widget = request.args.get('widget', 'stats.html')

    template = '''
    {% include 'base.html' %}
    {% include 'widgets/' + widget %}
    '''

    return render_template_string(template, user=session.get('user'))

You can set session['is_admin'] = True via a weak cookie. You cannot upload files. Which payload in widget is most likely to yield RCE?

A) {{ config.__class__.__init__.__globals__['os'].popen('id').read() }}
B) {{ ''.__class__.__mro__[1].__subclasses__()[40]('id', shell=True, stdout=-1).communicate()[0].decode() }}
C) stats.html' %}{% for k,v in config.items() %}{{k}}={{v}}{% endfor %}{% include 'stats.html
D) stats.html' %}{% set os = config.__class__.__init__.__globals__['os'] %}{{ os.popen('id').read() }}{% include 'stats.html
Show Answer & Explanation

Correct Answer: D

Explanation: You must break out of the Jinja {% include %} context before executing Jinja expressions. D performs the correct block escape and then executes a known Jinja gadget chain.

Question 5

Login + JWT code:

// LoginServlet.java (excerpt)
String token = JwtUtil.sign(user.getId(), user.getRole());
Cookie c = new Cookie("auth", token);
c.setHttpOnly(true);
c.setPath("/");
resp.addCookie(c);
// JwtUtil.java (excerpt)
private static final String SECRET = "dev-secret";

public static String sign(int userId, String role) {
    Algorithm alg = Algorithm.HMAC256(SECRET);
    return JWT.create()
            .withClaim("uid", userId)
            .withClaim("role", role)
            .sign(alg);
}

A unit test exists and is deployed with the app:

// JwtUtilTest.java (excerpt)
@Test
public void testSign() {
    String token = JwtUtil.sign(1, "admin");
    System.out.println(token);
}

Which is the most realistic way to gain admin access to /account without valid credentials?

A) Use alg=none and remove signature.
B) Reproduce or reuse the test-generated admin token and set it as the auth cookie.
C) Brute-force the secret dev-secret with a wordlist, then forge token.
D) Flip role claim in transit before verification.
Show Answer & Explanation

Correct Answer: B

Explanation: The repository includes deterministic signing logic and a test that generates an admin token using the same hard-coded secret. That is the most direct, reliable path compared to speculative bypass techniques.

Question 6

Search helper concatenates ORDER BY:

async function searchProducts(term, orderBy) {
  const sql = `
    SELECT id, name, price
    FROM products
    WHERE name LIKE ?
    ORDER BY ${orderBy} ASC
    LIMIT 20
  `;
  return pool.query(sql, [`%${term}%`]);
}

Caller whitelists sort:

const orderBy = req.query.sort || 'name';

if (!['name', 'price'].includes(orderBy)) {
  orderBy = 'name';
}

Which approach is most accurate and realistic from an OSWE perspective?

A) UNION SQLi through term because template literals concatenate it.
B) Bypass whitelist via case change and inject ; SLEEP(5) in orderBy.
C) Conclude the whole app is not injectable.
D) Recognize term is parameterized and orderBy is whitelisted; pivot to find a different unsafe concatenation path elsewhere.
Show Answer & Explanation

Correct Answer: D

Explanation: term is a bound parameter; injected SQL remains literal string data. orderBy is constrained to fixed column names. The OSWE-accurate move is to validate this path as safe and pivot to other code paths for real injection.

Question 7

Password reset route:

const payload = jwt.verify(token, process.env.JWT_SECRET || 'dev-secret');

Staging leaks: [DEBUG] Using default JWT secret. You want to escalate to admin@example.com. Which approach is most reliable?

A) Brute-force secret with a wordlist.
B) Use alg=none unsigned token.
C) Forge a JWT signed with dev-secret and payload {"email":"admin@example.com"}.
D) Timing side-channel against HMAC validation.
Show Answer & Explanation

Correct Answer: C

Explanation: The default secret is confirmed in staging. Forging a valid token with the known secret is deterministic, fastest, and most reliable.

Question 8

Coupon controller:

String sql = "SELECT * FROM coupons WHERE code = '" + code + "'";
Coupon coupon = jdbcTemplate.queryForObject(sql, new CouponRowMapper());

Errors not shown to user; you can measure timing and see logs. Most reliable exfil approach?

A) Time-based blind SQLi and infer data by response time.
B) UNION-based SQLi and read leaked data in HTML response.
C) Stacked queries to write to disk then download file via HTTP.
D) Error-based SQLi and read data from stack traces.
Show Answer & Explanation

Correct Answer: A

Explanation: When output is not reflected and error detail is uncertain, time-based blind extraction is the most consistently reliable OSWE-style primitive.

Question 9

Download handler:

$baseDir = __DIR__ . '/uploads/' . $ticketId . '/';
$path = realpath($baseDir . $file);

if ($path === false || strpos($path, $baseDir) !== 0) {
    http_response_code(400);
    exit('Invalid file');
}

You want to read /var/log/nginx/access.log without uploading new files. Which approach is most likely?

A) Simple ../ traversal.
B) Overflow realpath/strpos using an extremely long string.
C) Leverage a symlink inside the allowed directory pointing to the target file and request it.
D) Race condition while another process modifies filesystem.
Show Answer & Explanation

Correct Answer: C

Explanation: In real-world patterns, symlink tricks are the primary bypass class when traversal is blocked and you cannot upload. (If no symlink exists and you cannot create one, the practical exploitability depends on other filesystem primitives.)

Question 10

DAO uses PreparedStatement for status and whitelists sort:

// Whitelist for sort column
if (!sort.matches("^[a-z_]+$") ||
    !(sort.equals("created_at") || sort.equals("priority") || sort.equals("id"))) {
    sort = "created_at";
}

String sql = "SELECT id, title, status, owner_id FROM tickets WHERE status = ? ORDER BY " + sort;

PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, status);

Which is the most realistic and effective way to achieve time-based blind SQLi under default PostgreSQL behavior?

A) Inject into status with open' OR 1=1--.
B) Inject ; SELECT pg_sleep(5) into sort.
C) Inject a complex ORDER BY expression with CASE + pg_sleep.
D) No SQLi possible due to parameterization + strict whitelist/regex.
Show Answer & Explanation

Correct Answer: D

Explanation: status is bound via PreparedStatement, and sort only allows three literal column names with a regex that disallows spaces, commas, parentheses, operators, or functions. Payloads that attempt expressions or stacking will not pass validation.

Ready to Accelerate Your Offensive Security Preparation?

Join thousands of professionals who are advancing their careers through expert certification preparation with FlashGenius.

  • ✅ Unlimited practice questions across all OSPP domains
  • ✅ Full-length exam simulations with real-time scoring
  • ✅ AI-powered performance tracking and weak area identification
  • ✅ Personalized study plans with adaptive learning
  • ✅ Mobile-friendly platform for studying anywhere, anytime
  • ✅ Expert explanations and study resources
Start Free Practice Now

Already have an account? Sign in here

About Offensive Certification Pack

The Offensive Security Prep Pack for eJPT, OSCP, PNPT, OSWE, and OSEP validates your expertise in web application exploitation and other critical domains. Our practice questions are crafted to mirror exam-style reasoning and help you identify knowledge gaps before test day.

FlashGenius Premium

Offensive Security Prep Pack: EJPT, OSCP, PNPT, OSWE & OSEP


Train for multiple offensive security certifications in one place. Get domain-based drills, mixed-mode practice tests, and realistic red-team scenarios that mirror how EJPT, OSCP, PNPT, OSWE & OSEP actually feel on exam day.

  • 10+ focused domains covering networking, web, AD, privilege escalation & more
  • Exam-style MCQs, methodology drills, and chained attack paths
  • Mixed practice sets to simulate end-to-end engagements
  • Detailed explanations to turn every miss into a lesson
Be exam-ready faster

Ideal if you're targeting 2–3 OffSec-style certifications and want one unified prep pack.

Try the Offensive Security Prep Pack