# Links:

  - [[Cross Site Scripting (XSS)]]

# Metadata

  - Name  
    Understanding Session XSS
  - Overview  
    Pages 33–34 document an informational “Session stored XSS” finding on the TMC Schedule Execution Board. The issue is real (unescaped user input in a JavaScript context) but impact is limited because only the submitting user’s session is affected—classic self-XSS, not cross-user attack.

## Todos

  - \[X\] review-finding  
    Read pages 33–34 and map finding to SaveSearchCriteriaToSession + ScheduleExecutionBoard.aspx flow
  - \[X\] locate-source  
    Open TMC Web Portal repo and find session save + inline script render for date/orderID/time
  - \[ \] remediate-encode  
    Apply HttpUtility.JavaScriptStringEncode to all session values in SetupControls() (\~1849-1879)
  - \[ \] remediate-validate  
    Add server-side validation in SaveSearchCriteriaToSession before writing SEBSessionState
  - \[ \] remediate-retest  
    Retest with direct POST payload + normal UI search flow on ScheduleExecutionBoard

# Understanding the Session Stored XSS Finding (Pages 33–34)

## Where this sits in the report

The [Microlise TMC PO WA April 2026 v1.0.pdf](d:/_dev/_misc/Pentest-04-26/Microlise%20TMC%20PO%20WA%20April%202026%20v1.0.pdf) lists **14 findings** total. Pages 33–34 ([33-34.pdf](d:/_dev/_misc/Pentest-04-26/33-34.pdf)) are the last technical finding before “END OF DOCUMENT”:

| Field       | Value                                                                                       |
| ----------- | ------------------------------------------------------------------------------------------- |
| Title       | **Session stored XSS**                                                                      |
| Severity    | **Informational** (lowest tier; 4 informational findings in the report)                     |
| Status      | Open                                                                                        |
| CWE         | [CWE-79](https://cwe.mitre.org/data/definitions/79.html) — Improper Neutralization of Input |
| Environment | `cert.microlise.com` (cert/UAT), path prefix `/PENTEST/TMCWebPortal/`                       |

Higher-severity items in the same report (SQLi, IDOR, BFLA, etc.) are separate; this finding is documented as **technically valid but low business risk**.

-----

## What XSS is (general)

**Cross-Site Scripting (XSS)** means untrusted data ends up in a web page in a way the **browser treats as executable JavaScript**, instead of inert text.

The name “cross-site” is historical: classic attacks trick a **victim** into loading a page on **your** app so script runs in **your** origin (stealing session cookies, performing actions as the user, etc.).

Common types:

| Type          | Persistence                           | Typical delivery                 |
| ------------- | ------------------------------------- | -------------------------------- |
| **Reflected** | Not stored; one-off response          | Malicious link/query param       |
| **Stored**    | Saved server-side (DB, file, session) | Victim loads a normal page later |
| **DOM-based** | Client-side only                      | Unsafe innerHTML, eval, etc.     |

**Defense in depth:** validate input on the server (whitelist formats), and **encode output** for the exact context (HTML, attribute, JavaScript string, URL).

-----

## What happened in *this* finding (TMC context)

### Affected surface (source located)

| Role                        | Path                                                                                                       |
| --------------------------- | ---------------------------------------------------------------------------------------------------------- |
| Page + inline JS            | [ScheduleExecutionBoard.aspx](d:/_dev/WebPortal/src/code/AmberWebUI/SEB/ScheduleExecutionBoard.aspx)       |
| WebMethod + page properties | [ScheduleExecutionBoard.aspx.cs](d:/_dev/WebPortal/src/code/AmberWebUI/SEB/ScheduleExecutionBoard.aspx.cs) |
| Session storage             | [SEBSessionState.cs](d:/_dev/WebPortal/src/code/AmberWebUI/SEB/SEBSessionState.cs)                         |

  - **Endpoint:** ASP.NET `[WebMethod]` `SaveSearchCriteriaToSession` on `ScheduleExecutionBoard.aspx`
  - **Parameters:** JSON fields `date`, `orderID`, `time` (also `searchID`, `hours`, `quickSearch` in the same flow)
  - **Host (pentest):** `cert.microlise.com`, path `/PENTEST/TMCWebPortal/SEB/...`

### Attack flow (as tested)

``` mermaid
flowchart LR
    subgraph submit [Step1_Submit]
        A[Tester sends POST directly]
        B[SaveSearchCriteriaToSession]
        C[Values stored in server session]
    end
    subgraph render [Step2_Render]
        D[User loads ScheduleExecutionBoard.aspx]
        E[Server embeds session values in script block]
        F[Browser executes unescaped JS]
    end
    A --> B --> C
    C --> D --> E --> F
```

1.  **Save:** User (or tester) POSTs JSON to `SaveSearchCriteriaToSession`. The app saves search criteria into the **server-side session**.
2.  **Render:** On the next load of `ScheduleExecutionBoard.aspx`, those values are written into the HTML **inside a `<script>` block**, as JavaScript string literals.
3.  **Bug:** Values are inserted **without JavaScript string encoding**. A crafted `date` can **break out of the string** and run arbitrary JS.
4.  **Proof:** Pentesters confirmed execution in the browser; screenshots in the PDF show the POST and page source.

### Code path (matches report exactly)

**1. Save — no server-side validation**

``` csharp
// ScheduleExecutionBoard.aspx.cs lines 336-348
[WebMethod]
public static void SaveSearchCriteriaToSession(string searchID, string orderID, string date, string time, int hours, bool displayPriorityJourneys, string quickSearch)
{
    var sebState = new SEBSessionState();
    sebState.ComplexSearch = searchID;
    sebState.OrderBy = orderID;
    sebState.SearchDate = date;
    sebState.SearchTime = time;
    // ...
}
```

**2. Persist — per-user ASP.NET session**

[SEBSessionState.cs](d:/_dev/WebPortal/src/code/AmberWebUI/SEB/SEBSessionState.cs) stores values under keys `dateID`, `timeID`, `orderByID`.

**3. Load — on next full page GET**

``` csharp
// ScheduleExecutionBoard.aspx.cs lines 267-277
private void SetupControls()
{
    var sebState = new SEBSessionState();
    SessionOrderID = sebState.OrderBy;
    SessionDate = sebState.SearchDate;
    SessionTime = sebState.SearchTime;
    // ...
}
```

**4. Render — vulnerable inline JavaScript (root cause)**

``` javascript
// ScheduleExecutionBoard.aspx lines 1853-1875
if ("<%=SessionDate%>") {
    $('#txtStart').val("<%= SessionDate %>");
}
if ("<%=SessionTime%>") {
    $('#inputtime').val("<%=SessionTime%>");
}
if ("<%=SessionOrderID%>") {
    $(orderBySelector + ' option[value="<%=SessionOrderID%>"]').attr('selected', 'selected');
}
```

Example payload in session: `"); alert(document.domain); //`

``` javascript
$('#txtStart').val(""); alert(document.domain); //");
```

**Related:** `QuickSearch` at line \~1879 — fix in the same pass.

### Why “stored”, “session”, and “self-XSS”

  - **Stored:** Payload survives page navigation in **session state**.
  - **Self-XSS:** Only the submitter’s session is affected; no normal cross-user path.
  - Severity **Informational** because threat model is weak vs shared stored XSS.

### Client vs server validation gap

Pentesters bypassed browser validation via direct POST. No server-side validation blocked arbitrary strings.

-----

## Replicating the vulnerability (hands-on)

Use this section to **see the bug work** on an authorized environment (e.g. cert/UAT), then **repeat the same steps after fixes** and compare outcomes.

### Prerequisites

| Requirement       | Detail                                                               |
| ----------------- | -------------------------------------------------------------------- |
| **Authorization** | Pentest scope or internal security test policy only                  |
| **Permission**    | `Microlise:TMC:SEB:Read`                                             |
| **URL**           | e.g. `https://<host>/TMCWebPortal/SEB/ScheduleExecutionBoard.aspx`   |
| **Tools**         | Browser + DevTools or Burp Suite                                     |
| **Build**         | Before-fix build first; redeploy with remediation for after-fix runs |

Must be logged in (valid session cookie on POST).

### What you should observe (before fix)

``` mermaid
sequenceDiagram
    participant You as Tester_browser
    participant API as SaveSearchCriteriaToSession
    participant Sess as ASP.NET_session
    participant Page as ScheduleExecutionBoard_GET

    You->>API: POST JSON with malicious date
    API->>Sess: Store raw date in session
    You->>Page: Reload SEB page
    Page->>You: HTML with unescaped date inside script
    You->>You: alert or other JS runs
```

1.  WebMethod returns HTTP 200.
2.  Payload never went through `DateValidation()`.
3.  Full page reload → JS runs (e.g. `alert`).
4.  View Source: payload inside double-quoted JS string, unescaped.

**Self-XSS:** only your session is poisoned.

### Step-by-step reproduction

**Step 1 — Baseline (optional)**

1.  Open SEB, perform a search.
2.  DevTools → Network → `SaveSearchCriteriaToSession`.
3.  Note POST, `application/json`, body shape, Cookie header.

**Step 2 — Inject via direct POST (bypass UI)**

| Parameter                 | Suggested test value            |
| ------------------------- | ------------------------------- |
| `searchID`                | `0.X`                           |
| `orderID`                 | `0.X`                           |
| `date`                    | `"); alert(document.domain);//` |
| `time`                    | `00:00`                         |
| `hours`                   | `24`                            |
| `displayPriorityJourneys` | `false`                         |
| `quickSearch`             | `""`                            |

**Burp:** Repeater → replace JSON body → send.

**Browser console** (on SEB page, same origin):

``` javascript
fetch('ScheduleExecutionBoard.aspx/SaveSearchCriteriaToSession', {
  method: 'POST',
  credentials: 'include',
  headers: { 'Content-Type': 'application/json; charset=utf-8' },
  body: JSON.stringify({
    searchID: '0.X',
    orderID: '0.X',
    date: '"); alert(document.domain);//',
    time: '00:00',
    hours: 24,
    displayPriorityJourneys: false,
    quickSearch: ''
  })
}).then(r => console.log('status', r.status));
```

**curl** (replace host, path, cookies):

``` bash
curl -s -o /dev/null -w "%{http_code}" \
  -X POST "https://<host>/<TMCWebPortal>/SEB/ScheduleExecutionBoard.aspx/SaveSearchCriteriaToSession" \
  -H "Content-Type: application/json; charset=utf-8" \
  -H "Cookie: <paste-session-cookies>" \
  -d "{\"searchID\":\"0.X\",\"orderID\":\"0.X\",\"date\":\"\\\"); alert(document.domain);//\",\"time\":\"00:00\",\"hours\":24,\"displayPriorityJourneys\":false,\"quickSearch\":\"\"}"
```

**Step 3 — Trigger render (stored XSS)**

1.  Full navigation reload of `ScheduleExecutionBoard.aspx` (F5).
2.  `SetupControls()` embeds session `date` (\~lines 1854–1855).

**Step 4 — Confirm**

| Check             | Before fix (expected)                                                 |
| ----------------- | --------------------------------------------------------------------- |
| Popup / console   | `alert(document.domain)` runs                                         |
| View Source       | Literal `"); alert(...)` inside `$('#txtStart').val("...")` unescaped |
| Network on reload | Normal GET only; XSS from inline script                               |
| Other users       | No effect (different session)                                         |

**Step 5 — Optional:** malicious `time` or `orderID`.

**Step 6 — Clean up:** log out/in or POST valid date/time.

### Comparison matrix (before vs after fixes)

Run the same Steps 2–4 after each change:

| Observation                      | Before fix | After encoding only | After validation only | After both    |
| -------------------------------- | ---------- | ------------------- | --------------------- | ------------- |
| POST malicious `date` accepted?  | Yes (200)  | Yes (200)           | No / not stored       | No            |
| `alert` on reload?               | **Yes**    | **No**              | Depends\*             | **No**        |
| Executable JS in View Source?    | **Yes**    | **No** (escaped)    | Depends\*             | **No**        |
| `#txtStart` shows attack text?   | Maybe      | Escaped/safe        | Default/empty         | Default/empty |
| Normal UI search + reload works? | Yes        | Yes                 | Yes                   | Yes           |

\*If only validation: reload may show no XSS without encoding — still apply both fixes.

### Why UI-only testing misses the bug

| Path                       | `DateValidation()` runs? | Payload reaches session? |
| -------------------------- | ------------------------ | ------------------------ |
| Click Search in UI         | Yes                      | No (normal typing)       |
| Direct POST / Burp / fetch | **No**                   | **Yes**                  |

Reproduction **must** use direct POST to match the pentest.

### Evidence to capture (for fix sign-off)

1.  Request (POST body with payload).
2.  Screenshot of alert (before) or no alert (after).
3.  View Source snippet around `$('#txtStart').val(`.
4.  Regression: legitimate date, reload, criteria restored.

### Safety and scope

  - No production without approval.
  - Prefer `alert(document.domain)` over exfiltration demos.
  - Self-XSS: coding defect demo, not mass compromise.

-----

## How to fix it

Use **two layers**: output encoding + server-side validation.

### Fix 1 — Output encoding (required)

File: [ScheduleExecutionBoard.aspx](d:/_dev/WebPortal/src/code/AmberWebUI/SEB/ScheduleExecutionBoard.aspx), `SetupControls()` (\~1847–1884).

| Line (approx) | Field           | Encode |
| ------------- | --------------- | ------ |
| 1849–1850     | SessionSearchID | Yes    |
| 1854–1855     | SessionDate     | Yes    |
| 1860–1861     | SessionTime     | Yes    |
| 1872–1874     | SessionOrderID  | Yes    |
| 1878–1879     | QuickSearch     | Yes    |

**Before:**

``` javascript
$('#txtStart').val("<%= SessionDate %>");
```

**After:**

``` javascript
$('#txtStart').val("<%= HttpUtility.JavaScriptStringEncode(SessionDate ?? string.Empty) %>");
```

Encode `if` guards too, or use code-behind booleans (`HasSessionDate`).

### Fix 2 — Server-side input validation

File: [ScheduleExecutionBoard.aspx.cs](d:/_dev/WebPortal/src/code/AmberWebUI/SEB/ScheduleExecutionBoard.aspx.cs), `SaveSearchCriteriaToSession` (\~337).

| Parameter  | Validation rule                         |
| ---------- | --------------------------------------- |
| `date`     | Same regex as client `DateValidation()` |
| `time`     | `^[0-2][0-9]:[0-5][0-9]$`               |
| `orderID`  | `^[0-9]+(\.X)?$`                        |
| `searchID` | Same as `orderID`                       |
| `hours`    | Clamp 1–999                             |

``` csharp
[WebMethod]
public static void SaveSearchCriteriaToSession(...)
{
    if (!IsValidSebDate(date) || !IsValidSebTime(time)
        || !IsValidQueryComponentId(orderID) || !IsValidQueryComponentId(searchID))
    {
        return;
    }
    var sebState = new SEBSessionState();
    // ...
}
```

**Date regex:**

    ^[0-9]{4}-(((0[13578]|(10|12))-(0[1-9]|[1-2][0-9]|3[0-1]))|(02-(0[1-9]|[1-2][0-9]))|((0[469]|11)-(0[1-9]|[1-2][0-9]|30)))$

### Fix 3 — What not to do

  - Do not rely on client `DateValidation()` alone.
  - Do not use `HtmlEncode` in JS string literals.
  - Do not use `innerHTML`; keep `.val()`.

### Fix 4 — Verification / test plan

Master procedure: **Replicating the vulnerability** section above.

**Negative test:**

``` json
{
  "searchID": "0.X",
  "orderID": "0.X",
  "date": "\"); alert(1);//",
  "time": "00:00",
  "hours": 24,
  "displayPriorityJourneys": false,
  "quickSearch": ""
}
```

**Pass:** No alert; escaped in source; invalid date not stored (with Fix 2).

**Positive test:** UI search + reload restores criteria.

### Files to change (summary)

| File                                                                                                       | Change                                        |
| ---------------------------------------------------------------------------------------------------------- | --------------------------------------------- |
| [ScheduleExecutionBoard.aspx](d:/_dev/WebPortal/src/code/AmberWebUI/SEB/ScheduleExecutionBoard.aspx)       | `JavaScriptStringEncode` in `SetupControls()` |
| [ScheduleExecutionBoard.aspx.cs](d:/_dev/WebPortal/src/code/AmberWebUI/SEB/ScheduleExecutionBoard.aspx.cs) | Validation in `SaveSearchCriteriaToSession`   |

**References:** [OWASP XSS](https://owasp.org/www-community/attacks/xss/), [PortSwigger Stored XSS](https://portswigger.net/web-security/cross-site-scripting/stored), [JavaScriptStringEncode](https://learn.microsoft.com/en-us/dotnet/api/system.web.httputility.javascriptstringencode)

-----

## Mental model: severity vs correctness

| Question                   | Answer                              |
| -------------------------- | ----------------------------------- |
| Real coding flaw?          | **Yes** (CWE-79)                    |
| Cross-user session hijack? | **Not under normal use** (self-XSS) |
| Should it still be fixed?  | **Yes**, as hygiene                 |
| Priority vs SQLi / IDOR?   | **Much lower** (informational)      |

## Next step

1.  **Reproduce** on cert/UAT (`replicate-vuln` todo).
2.  **Implement** fixes in `D:\_dev\WebPortal` (`remediate-encode`, `remediate-validate`).
3.  **Replay** steps and fill comparison matrix (`remediate-retest` todo).

Confirm execution when ready to change WebPortal code.
