> ## Documentation Index
> Fetch the complete documentation index at: https://hackwithmike.com/llms.txt
> Use this file to discover all available pages before exploring further.

# OSWE Methodology

## Introduction

***

I passed the OffSec Web Expert (OSWE) exam a while back, and I promised to write something on it - so here it is.

OSWE is a 48-hour proctored exam with 2 targets. Each target has 1 user flag for authentication bypass, and 1 root flag for remote code execution (RCE). Obtaining 3 out of 4 flags is required to pass. Each target also comes with 2 machines: a target machine with the actual flags, and a debug machine that holds an exact copy of the source code and the application. Do note that you **cannot download the source code off** the debugging box to your own machine.

Most analysis and review will be performed on the debugging machine. Per target, you are expected to produce a single Python exploit script that chains together both the auth-bypass and RCE vulnerabilities.

Since there are already a lot of great OSWE write-ups on the course structure and exam-day experience, the rest of this blog will focus more on my methodology and preparation. Here are a few references that helped me along the way:

* [an00b / nayyyr's Obligatory OSWE Retrospective](https://notateamserver.xyz/blog/oswe-review/)
* [Stefano Lanaro's OSWE review ](https://steflan-security.com/offsec-web-expert-oswe-review/)
* [saunders-jake's oswe-resource](https://github.com/saunders-jake/oswe-resources)
* [sidd.sh's OSWE review](https://sidd.sh/posts/OSWE_review/)

One more tip for you: pay close attention to the comments on the **slow exam environment** - I experienced the same and ended up using SSH, grep commands, and eyeballing for most of my code review.

## The Methodology

***

The OSWE / AWAE course focuses heavily on exploitation techniques for the introduced vulnerabilities. By the time you sit the exam, you should already have a bunch of scripts and helpers ready, such as blind SQLi extractors, XSS token-theft payloads, reverse shell handlers, etc.

The actual challenge of the exam is to find bugs in an unfamiliar codebase under time pressure, and to triage exploitable vulnerabilities from rabbitholes. A concatenated SQL query is a good lead, but it can also be a dead end if the input was cast to an integer upstream, or if it is being properly sanitised by a strong filter. On the other hand, custom sanitisers are always worth investigating, as the implementation can be incomplete or misconfigured.

From a pure exam perspective, two ideas drive my whole approach: **splitting your work into two tracks** - Auth-bypass & RCE, and **hunting by features & endpoints**.

### Two Tracks: Auth-bypass and RCE

As mentioned, each target requires an authentication bypass to get to the app admin account for the user flag, and an RCE for the root flag. Treat them as separate searches - each is usually abused through different features and vuln classes.

#### Authentication Bypass

The Auth-bypass track is always about attacking the authentication logic. You can trick the server into believing you are someone else, or have a higher privilege (e.g., `admin=true`), or break the authentication flow to skip the checks, or simply get your hands into the valid credentials. Start by understanding how the application authenticates users and assigns roles. Here are some example questions worth asking:

* Does the app use **session tokens** to authenticate & authorize its users? If so, how are they implemented? (e.g., Cookies? Authentication Header? Custom headers?)
* Say if the app uses cookies for authentication, are the **cookies properly protected**? Is `httponly` set to prevent XSS from extracting the cookie?
* Is **role assignment** exposed in the client-side? (e.g., something like `role=admin` in cookies / headers). If so, are they properly validated?
* How are the session tokens (and other related tokens like password reset, temporary login codes) **generated, stored, and checked**? Are they vulnerable to extraction from database (SQLi), or are they predictable?
* etc.

#### Remote-Code Execution

On the other hand, the RCE track is about identifying where the server may parse or process our input as server-side code. The usual vectors are deserialization, server-side template injection (SSTI), custom command wrappers, file upload to an executable path, SQL injection, etc. From an exam perspective, this is easier to scope: since the RCE is expected after Auth-bypass, focus on functions and endpoints that are exposed to the admin user only. It can be something like:

* An admin-only dashboard that contains a **command wrapper**;
* An **editing function** that normal users are restricted from using;
* A **file upload** function for administrative tasks;
* An SQL query that only the admin user is accessible;
* etc.

Using the "**admin-only**" logic, if an upload function behaves exactly the same for both regular users and admin users, then it is less likely to be the intended RCE vector for OSWE.

Keeping the two tracks separate stops you from getting distracted by the amount of code. Start with Auth-bypass, and keep the RCE hunt limited to admin-accessible features only.

<Note>
  Vulnerability chaining is a must for the OSWE exam (and also in real-world hunting), so always ask - "what does this vulnerability unlock next?". A file read vuln can lead to leaking of environment variables, which give further access to other services that expose another vulnerablity, etc.
</Note>

### Hunt by Features and Endpoints

Since we have both the source code and a working application, treat it like a grey-box test: interact with the application like a regular user, list every accessible feature and endpoint, then map them back to the source code.

Using the two-track methodology, here are some features & endpoints worth looking at:

#### Pre-admin (Auth-bypass)

* Login and registration
* Password reset
* Profile and account update
* Any user-input areas (e.g., blog post, comments, feedback forms, URL parameters, etc.)
* File upload and download (e.g., profile picture, blog post images, attachments, etc.)
* Any functions that likely pull data from the database (e.g., search function, blog post filtering, etc.)
* etc.

#### Post-admin (RCE)

* Admin dashboard
* File import and export
* File upload and download
* Content Templates
* User management panel
* Database management panel
* Background and cron jobs
* System command / function runners
* etc.

Most importantly, trace the user input from source to sink. Ask yourself - where does it enter (source)? What transformations happen? Which security checks run before the dangerous operation? Where does it reach eventually (sink)?

## The Preparation

***

With the methodology, we can then prepare for the exam accordingly. Know the impact of each vuln class, have search patterns ready, and prepare helper scripts that were battle-tested in labs.

### Vulnerability Classes by Tracks

Following the two-track approach, I classified each vulnerability class by their impacts. If it helps with becoming admin, it goes into the Auth-bypass track. If it helps turn admin access into code execution, it goes into the RCE track.

#### Pre-admin (Auth-bypass)

For the pre-admin track, the goal is to reach the administrator context, steal or forge a privileged session, or otherwise make the application treat you as the admin user.

Here is how to think about the pre-admin bugs:

<div className="tab-frame">
  <Tabs>
    <Tab title="SQL Injection">
      **Impact:** Data extraction, auth bypass, token theft

      * It may leak admin password hashes, active sessions, reset tokens, signing secrets, etc. Sometimes the login query itself can also be bypassed directly.
    </Tab>

    <Tab title="XSS">
      **Impact:** Session hijacking, forced admin actions, privilege escalation

      * It is only useful when the payload runs in an admin-viewed page. Self-XSS almost always have no impact.
      * Pro-tip: Think about what the XSS can do - if the cookie is protected and there is no sensitive secrets hardcoded in the HTML pages / JS scripts, there may not be much impact, and vice versa.
    </Tab>

    <Tab title="IDOR / Access Control">
      **Impact:** Unauthorized read/write, account takeover path

      * It can let you read or modify objects you should not control. Much higher impact when the object belongs to admin or high priv roles.
    </Tab>

    <Tab title="Auth and Session Logic">
      **Impact:** Session forgery, replay, role tampering

      * Weak role validation, broken signature checks, remember-me tokens, or custom session checks can sometimes let you become admin without knowing the password.
    </Tab>
  </Tabs>
</div>

<br />

<div className="tab-frame">
  <Tabs>
    <Tab title="Password Reset Tokens">
      **Impact:** Account takeover

      * Check the reset token is predictable, reusable, leaked, or not properly tied to a user.
    </Tab>

    <Tab title="CSRF / CORS">
      **Impact:** Admin action abuse, privileged data theft

      * These are only useful when they let you force a meaningful admin action or read something privileged from a credentialed API.
    </Tab>

    <Tab title="LFI / Path Traversal / XXE">
      **Impact:** Secret disclosure, source disclosure, internal access

      * File read is useful when it gives you config files, source code, session stores, private keys, or internal responses that help you reach admin.
    </Tab>
  </Tabs>
</div>

<br />

#### Post-admin (RCE)

For the post-admin track, the bug only becomes interesting once the feature is reachable from the admin context or clearly depends on data the admin can create. The main question is whether the vuln can actually become code execution:

<div className="tab-frame">
  <Tabs>
    <Tab title="Command Injection">
      **Impact:** Direct command execution

      * This is the cleanest RCE way - look out for filters & bypass though.
      * Pro-tip: Validate it with a quick `wget` or `curl` command to your controlled server to confirm if the command is actually being executed.
    </Tab>

    <Tab title="File Upload / File Write">
      **Impact:** Webshell, file overwrite, code execution

      * Check from the source code to see how is the uploaded file processed, and where does it land.
      * If the application later executes or includes the written file, this can quickly become RCE.
    </Tab>

    <Tab title="Deserialization">
      **Impact:** Gadget-chain RCE, arbitrary method invocation

      * Dangerous when attacker-controlled serialized data reaches magic methods, object construction, or known framework gadgets.
    </Tab>

    <Tab title="SSTI / Template Injection">
      **Impact:** Code execution through the template engine

      * If expression evaluation works inside a server-side template, it is basically code execution.
    </Tab>
  </Tabs>
</div>

<br />

<div className="tab-frame">
  <Tabs>
    <Tab title="SQLi to RCE">
      **Impact:** Database-level file write or command execution

      * Some databases can write files, load extensions, execute commands, or leak credentials for another service. That is where SQLi becomes more than data theft.
    </Tab>

    <Tab title="XXE / SSRF">
      **Impact:** Internal pivot, secret extraction, secondary RCE

      * These are useful when they can hit internal admin panels, metadata endpoints, debug services, local-only services, or other server-side parsing paths.
    </Tab>

    <Tab title="LFI / Path Traversal">
      **Impact:** Include-chain RCE, secret-assisted RCE

      * File read alone has little impact. It becomes critical when paired with upload, logs, templates, or useful config disclosure.
    </Tab>

    <Tab title="Prototype Pollution">
      **Impact:** RCE through polluted options or gadgets

      * Mostly relevant in Node.js. It matters only if the polluted property reaches something dangerous, such as template options, command options, or object construction.
    </Tab>
  </Tabs>
</div>

There is naturally some overlap between the two lists. SQLi, XXE, SSRF, and traversal can all be either pre-admin or post-admin depending on where they are reachable and what they expose. The key is not to see it as a rigid framework, but a mental map helps you quickly prioritize your effort during the exam.

### Search Patterns

Have grep patterns ready for the languages you might see will make your life way easier. I usually start by spamming a few grep searchs, open the interesting-looking files, then trace the input properly from route -> controller -> helper/model/sink. Here are a few examples:

<CodeGroup>
  ```bash PHP wrap theme={null}
  # Routes / auth checks
  grep -rn "Route::\|->connect\|->get\|->post\|Router::" --include="*.php" .
  grep -rn "beforeFilter\|isAuthorized\|checkAccess\|allow(" --include="*.php" .

  # SQLi / auth-bypass leads
  grep -rn "mysqli_query\|->query(\|->exec(\|PDO" --include="*.php" .
  grep -rn "\"[[:space:]]*\.[[:space:]]*\$_\|'[[:space:]]*\.[[:space:]]*\$_\|SELECT.*\$" --include="*.php" .

  # RCE / file / parsing sinks
  grep -rn "eval(\|assert(\|call_user_func\|create_function" --include="*.php" .
  grep -rn "exec(\|shell_exec(\|system(\|passthru(\|popen(\|proc_open(" --include="*.php" .
  grep -rn "unserialize(\|__wakeup\|__destruct\|__toString" --include="*.php" .
  grep -rn "move_uploaded_file\|file_put_contents\|\$_FILES\|include(\|require(" --include="*.php" .
  grep -rn "DOMDocument\|simplexml_load\|XMLReader\|xml_parse\|LIBXML_NOENT" --include="*.php" .
  ```

  ```bash Java wrap theme={null}
  # Routes / controllers
  grep -rn "@RequestMapping\|@GetMapping\|@PostMapping\|extends HttpServlet\|doGet\|doPost" --include="*.java" .

  # Auth / token leads
  grep -rn "doFilter\|FilterChain\|getSession\|getCookies\|setAttribute\|getAttribute" --include="*.java" .
  grep -rn "new Random()\|Math.random()\|java.util.Random\|resetPassword\|generateToken\|activationKey\|resetKey" --include="*.java" .

  # SQLi leads
  grep -rn "executeQuery\|executeUpdate\|execute(" --include="*.java" . | grep -v "prepareStatement\|PreparedStatement"
  grep -rn '"SELECT\|"INSERT\|"UPDATE\|"DELETE' --include="*.java" . | grep "\+"

  # RCE / parsing sinks
  grep -rn "DocumentBuilderFactory\|SAXParserFactory\|XMLReader\|XMLInputFactory" --include="*.java" .
  grep -rn "ObjectInputStream\|readObject\|SerializationUtils\|deserialize\|fromXML" --include="*.java" .
  grep -rn "MultipartFile\|Part\|FileUpload\|getOriginalFilename\|getSubmittedFileName" --include="*.java" .
  grep -rn "ProcessBuilder\|Runtime.exec\|ScriptEngineManager\|Yaml.load" --include="*.java" .
  ```

  ```bash .NET wrap theme={null}
  # Routes / auth config
  grep -rn "\[AllowAnonymous\]\|\[Authorize\]" --include="*.cs" .
  grep -rn "public.*ActionResult\|public.*IActionResult" --include="*.cs" .
  grep -rn "UseAuthentication\|UseAuthorization\|UseRouting\|UseEndpoints" Startup.cs

  # SQLi leads
  grep -rn "SqlCommand\|SqlConnection\|ExecuteReader\|ExecuteNonQuery" --include="*.cs" . | grep -v "Parameters\|AddWithValue\|@"
  grep -rn "FromSqlRaw\|ExecuteSqlRaw\|Database\.Sql" --include="*.cs" .
  grep -rn '"SELECT.*WHERE\|"UPDATE.*SET\|"INSERT INTO' --include="*.cs" . | grep "\+"

  # RCE / parsing sinks
  grep -rn "BinaryFormatter\|LosFormatter\|ObjectStateFormatter\|NetDataContractSerializer" --include="*.cs" .
  grep -rn "XmlSerializer\|Deserialize\|ReadObject\|TypeNameHandling" --include="*.cs" .
  grep -rn "FileUpload\|HttpPostedFile\|Request\.Files\|SaveAs\|CopyTo" --include="*.cs" .
  grep -rn "Process\.Start\|ProcessStartInfo\|cmd\.exe\|powershell" --include="*.cs" .
  ```

  ```bash HTML / JavaScript wrap theme={null}
  # Client-side sinks
  grep -rn "innerHTML\|outerHTML\|document.write\|insertAdjacentHTML\|eval(" --include="*.js" --include="*.html" .

  # Client-side sources
  grep -rn "location.hash\|location.search\|location.href\|document.URL\|document.referrer" --include="*.js" --include="*.html" .

  # Secrets / admin-only frontend hints
  grep -rn "apiKey\|api_key\|token\|secret\|admin\|role\|isAdmin" --include="*.js" --include="*.html" .

  # Script-generated URLs / API calls
  grep -rn "fetch(\|XMLHttpRequest\|axios\|$.ajax\|url:" --include="*.js" --include="*.html" .
  ```
</CodeGroup>

### Helpers and AI in Prep

As is the case for all OffSec exams, conversational AI tools are banned during the exam. However, that doesn't mean you can't use them before the exam for preparation.

I used LLMs heavily during prep to make sure I covered the areas I was weaker in, such as Java and PHP. Here is an example of what my prompt looked like. Always tweak the prompt around - different lengths, structures, wordings, and focuses - until you find something useful:

```markdown LLM Prompt wrap theme={null}
Generate a white-box/grey-box vulnerability cheatsheet for [LANGUAGE] using the following structure:

1. **Language Overview** – structure, routing logic, key features, important syntax/functions
2. **Initial Recon** – high-value files, entry points, and areas to prioritize during code review
3. **Auth Bypass** – hunting strategies, relevant vuln classes, exploitation techniques (include cross-language techniques where applicable, and variants beyond what the course explicitly covers)
4. **RCE** – same scope as above; include technique variants (e.g., for deserialization: XML, BinaryFormatter, etc.)
5. **White-box Hunting Tips** – language-specific tips, grep patterns, static analysis tricks

Notes:
- Assume white-box access with a live app and optional dynamic debugger (grey-box where useful)
- Reference course syllabus topics where applicable
- Include relevant techniques not explicitly in the syllabus
```

I also had it assist me in creating most of my helper snippets. However, LLMs sometimes love to overcomplicate things. It once built me a highly sophisticated custom HTTP session handler, basically a huge `requests` wrapper with extra steps, while all I needed was one simple line of `requests.Session().get()`. Definitely know your basics, so that you can call out the slop when needed.

Here are some helper snippets worth having ready and tested before exam day:

* **Blind SQLi extractor** with parallel worker threads (I built a very handy one!)
* Token brute-forcer
* **Web server** that can both serve payloads and receive callbacks (e.g., handling XSS callbacks, serving DTDs, etc.)
* **Reverse shell handler** with separate send and receive threads
* Argparse skeleton for new exploit scripts (all the flags & parameters for your final exploit)
* Modular PoC structure that you can easily plug in and remove the above snippets

One advanced tip I have is to spend time figuring out what to **hardcode into the final Python script**. For example, if you need to upload a binary file / ZIP file for your exploit, you can't really "attach" that file alongside your Python script as a bundle. To solve this, I simply encode the file to Base64, embed the Base64 string into the script, then decode it during runtime. As long as the file size isn't too big, this is usually the cleanest way to handle attachments.

The other example is XXE exploits - I would hardcode the DTDs and XML payloads in my scripts. While the official guide notes that you can start a Netcat listener and web server before executing the script, I prefer building that into the script so it works smoothly for the examiner. Do note that some tools, such as phpggc, may require dynamic inputs like target IPs or ports, so this trick may not always work gracefully. In that case, I prefer writing a webshell to a reachable location for simplicity.

My own helpers and PoC templates are in my GitHub repo: [https://github.com/hkm67/OSWE-Notes](https://github.com/hkm67/OSWE-Notes). Feel free to reference and adapt them to your needs!

## Closing Notes

***

Once again, thank you for reading till the end. The methodology here is purely from my personal experience and way of thinking, so I know it may not be as intuitive to everyone. I tried my best to make it make sense, but please feel free to reach out if you have any questions or recommendations - my inbox is always open! (Tho sometimes I may reply a bit slow haha)
