IDOR in Downloadable Chat Transcripts
Sequential transcript filenames expose an authorization gap: changing the requested object retrieves another lab user's conversation and synthetic credential.
Every lab note, grouped by its topic path. Choose a filter or browse the full collection.
Sequential transcript filenames expose an authorization gap: changing the requested object retrieves another lab user's conversation and synthetic credential.
Changing the account identifier in the request returns another lab user's profile, demonstrating missing object-level authorization and exposing the synthetic API key.
The application trusts a client-controlled role cookie, so changing its value grants access to the administration panel and its privileged lab action.
Public client-side JavaScript discloses the unpredictable administration path, and the server permits an unauthenticated visitor to use its privileged action.
The robots exclusion file reveals an administrative route that lacks server-side authorization, allowing direct access and the lab's deletion action.
POST /forgot-password embeds the supplied username in an internal REST path without canonicalizing it first. Traversal segments and a URL fragment redirect the backend request to the administrator's passwordResetToken field.
The front end blocks visible /admin paths, but the backend honors X-Original-URL when selecting a route. Sending an innocuous request target with the protected path in that header reaches the administrative action.
Encoded query delimiters alter the internal reset API parameter set, exposing a path to retrieve the administrator reset token.
Requesting Carlos's account produces a redirect, but the server includes his API key in the 302 response body before the browser follows that redirect. Authorization must protect the response itself, not rely on navigation behavior.
The product page uses GET /api/products/1/price, yet the same route also accepts an undocumented PATCH request. Setting the price to zero lets an ordinary account buy the jacket without paying its intended cost.
The account page trusts a user-controlled id parameter and renders the selected user's password into an HTML password field. Browser masking changes only presentation: the underlying value remains available to the client.
Direct navigation to the account page bypasses the pending second-factor step after valid password authentication.
The account endpoint uses GUID-like identifiers rather than predictable usernames or integers. That makes guessing harder, but the application publishes Carlos's identifier in an author link.
Alternating valid and invalid login attempts resets the IP-based failure counter and permits continued password guessing.
The profile endpoint binds extra client-supplied JSON properties while updating an email address. Its response exposes roleid, and adding "roleid": 2 to the request promotes the current account to administrator.
A stay-logged-in cookie encodes the username alongside an unsalted MD5 password hash. Recomputing that predictable token for password candidates yields a valid cookie for Carlos's account.
The checkout API returns an internal discount property and also accepts that property from client-supplied JSON. Submitting a 100% discount reduces the jacket order to zero and bypasses the intended price control.
A stolen persistent-login cookie reveals a weak password hash that can be cracked offline and reused for authentication.
Interactive documentation at /api discloses the request needed to delete a user. The backend accepts that operation from the ordinary wiener account, allowing it to delete carlos without adequate authorization.
The reset submission trusts its username field instead of binding the new password to the account that requested the token.
Attacker-controlled forwarded-host metadata poisons the emailed reset URL and sends Carlos reset token to the exploit server.
Account lockout becomes the oracle: repeated failures trigger a warning only for a real username, after which a controlled password test completes the lab.
Distinct POST /login messages separate unknown users from valid accounts with wrong passwords, providing a direct response-text signal for enumeration.
The lab's two-step login flow does not securely bind the second-factor check to the account authenticated during the password step. Instead, /login2 uses a client-controlled verify cookie to decide whose code is being checked.
Response-time differences reveal when a username reaches password verification, enabling reliable account enumeration.
A single punctuation difference in the login error reveals whether a username exists, even though the two responses otherwise look identical.
A safe exercise in redrawing a state machine before reaching a conclusion about an authentication flaw.
Because POST /cart accepts the product price from the browser, changing price=133700 to price=1 adds the leather jacket for one cent. The server should derive prices from its own catalog rather than client input.
The cart remembers only the last coupon. Alternating SIGNUP30 and NEWCUST5 therefore reapplies both discounts until the $1,337 jacket falls below the $100 balance.
A signed quantity in POST /cart permits negative line items that offset the jacket's price. Balancing those entries to a small positive total lets checkout succeed despite insufficient funds.
An overlong email is delivered in full but truncated before authorization, yielding a stored address in the privileged domain.
Registration verifies an email address, but /my-account/change-email immediately trusts a replacement. Setting [email protected] satisfies the domain-only /admin rule.
A reusable discount makes a gift card cost less than the credit received when it is redeemed. Repeating the purchase-and-redeem cycle increases the account balance.
Calling the final checkout endpoint out of sequence skips the payment transition while the server still accepts the purchase.
Repeated quantity=99 additions overflow the cart's signed 32-bit total and wrap it below zero. Further adjustments place the total within the account balance, allowing an otherwise unaffordable purchase.
Removing current-password from the dual-use change endpoint selects a weaker branch that resets the administrator credential.
The authentication state machine assumes every user completes the role-selection transition after login. Dropping that request leaves the new session in an unsafe default state with access to administrative functionality.
The CSRF check compares the form value only with a csrf cookie, without binding either value to the authenticated session. Header injection through search lets an attacker plant a matching pair before the victim submits the email change.
The server validates CSRF tokens without binding them to the session that issued them. A token from the attacker's account therefore remains valid when the victim's browser submits it with the victim's session cookie.
The email-change handler binds the csrf form token to the non-session csrfKey cookie rather than the login session. Response-header injection through the search endpoint can install an attacker-controlled matching token and cookie.
POST /my-account/change-email requires a csrf token, but the same endpoint accepts a token-free GET request. Supplying the email in the query string returns 302 and changes the account address.
The endpoint rejects GET and invalid CSRF tokens, yet accepts POST when the csrf parameter is omitted entirely. Removing the parameter returns 302 and completes the email change.
The /social-login flow reaches /oauth-callback and refreshes the session cookie without an explicit SameSite attribute. Chrome's two-minute Lax-by-default exception then permits a cross-site POST that changes the victim's email.
Although the endpoint rejects a plain GET, its _method=POST override performs the email change during top-level navigation. That method confusion allows the victim's SameSite=Lax session cookie to accompany a cross-site request.
The confirmation script appends postId to /post/ without blocking path traversal. A crafted ../my-account/change-email URL turns a cross-site visit into a same-site redirect, so the browser's Strict cookie accompanies the request.
An XSS flaw on the support sibling domain runs in the same site as the target and opens its WebSocket endpoint with the victim's Strict cookie. Sending READY exposes Carlos's chat history across the sibling-domain trust boundary.
The "Welcome back!" message forms a one-bit SQL oracle for predicates injected through TrackingId. Length checks establish a 20-character password, and SUBSTR comparisons recover the administrator value one character at a time.
The email-change endpoint authorizes a state-changing POST using only the browser's session cookie. With no anti-CSRF token or equivalent origin check, an external page can submit the request as the authenticated victim.
Oracle EXTRACTVALUE and XMLType place the administrator password into a Collaborator subdomain requested by the database. The resulting DNS lookup carries the secret even though the application response contains no query output.
The proof arrives outside the HTTP response: the in-band response is unchanged, but EXTRACTVALUE(xmltype(... SYSTEM "http://COLLAB/" ...),'/l') FROM dual causes a Collaborator DNS interaction.
The page content remains unchanged, but a PostgreSQL payload containing pg_sleep(10) delays the response by ten seconds. That repeatable timing difference proves the TrackingId cookie reaches an executable SQL context.
The database exposes values through latency rather than response content. CASE WHEN with pg_sleep(10) establishes a 20-character password, and SUBSTRING comparisons recover each character from the administrator credential.
A two-column UNION query identifies PostgreSQL through version() and enumerates information_schema.tables to find users_occukb. Querying information_schema.columns then reveals username_gfkpyb and password_xrdklp for credential extraction.
The injectable category filter supports a two-column Oracle UNION query that enumerates all_tables and finds USERS_GSEMNG. Querying user_tab_cols reveals USERNAME_RKTXFE and PASSWORD_YCJIMQ, which return the administrator credential.
Oracle requires each UNION branch to select from a table, so adding FROM dual makes the two-column probe succeed. Rendering banner from v$version in the text-compatible column then identifies the database release.
Rendering version() through a two-column UNION exposes the MySQL banner in the category results. A Microsoft SQL Server probe would use @@version, so the returned text and accepted syntax distinguish the platforms.
ORDER BY 3 succeeds while ORDER BY 4 fails, establishing that the original query returns three columns. UNION SELECT null,null,null confirms the result shape with a compatible all-null row.
Only one UNION position can carry the required string: placing yNBXRx in column one errors, while UNION SELECT null,'yNBXRx',null displays it in column two. The visible yNBXRx value identifies column two as text-compatible.
ORDER BY 2 succeeds and ORDER BY 3 fails, proving that the product query returns two columns. UNION SELECT username,password FROM users renders both credential fields and exposes the administrator login.
This lab contains blind SQL injection in the TrackingId cookie. Successful and unsuccessful queries do not produce useful content differences, but a database exception causes a distinct HTTP error.
Only the second UNION column renders text, so separate username and password values cannot both be displayed directly. PostgreSQL concatenation with username||':'||password combines each credential pair into that single visible column.
Injecting administrator' or '1'='1' -- changes the username predicate to true and comments out the password comparison. The resulting query authenticates an administrator session without a valid password.
The category parameter is inserted before an AND released = 1 restriction in the product query. Injecting a true predicate and a comment removes both filters, causing the response to include unreleased products from every category.
The stock API evaluates the XML storeId value as SQL, although its filter inspects the value before XML entities are decoded. Encoding a UNION SELECT payload with Hackvertor bypasses the filter and returns the users table credentials.
The stock selector copies storeId from the query string into document.write; closing the option and select lets an encoded image-error payload become executable DOM in the vulnerable lab.
The search page inserts location.search into a document.write tracker string; an encoded quote-closing SVG payload escapes the image attribute and creates an executable handler in the vulnerable lab.
The result div assigns query input through innerHTML: inserted script markup stays inert, while an encoded image-error payload creates a live element and executes its handler in the vulnerable lab.
The feedback page copies returnPath from location.search into the Back link's href with jQuery attr(). Supplying a javascript: URL turns the subsequent click into script execution.
A hashchange handler decodes location.hash and concatenates it into a jQuery :contains() selector. Loading a crafted fragment in an iframe and firing print() drives the vulnerable selector into executable markup.
Unsanitized comment HTML can add username and password inputs that the simulated victim's password manager autofills. JavaScript then sends those credentials to Collaborator before the attacker signs in and deletes Carlos.
A stored comment executes JavaScript for later viewers and can read document.cookie because the session token lacks HttpOnly. Exfiltrating the administrator cookie to Collaborator enables session hijacking and deletion of Carlos.
Stored script runs in the application's origin, fetches /my-account, and extracts the current CSRF token. It can then submit that token with a forged email change, bypassing a defense that remains inaccessible only to cross-origin pages.
The search API reflects input into JSON that client code passes to eval(responseText). Escaping the JSON string and preserving valid syntax converts the reflected value into JavaScript execution in the browser.
The raw query string enters the href attribute of a canonical link in the document head. An injected accesskey and onclick handler exploit that attribute context even though angle brackets cannot create another element.
Search input is embedded in a single-quoted JavaScript string while angle brackets are HTML-encoded. An unescaped quote terminates the string, allowing a syntactically valid statement to execute.
The page escapes single quotes with backslashes but leaves attacker-supplied backslashes untouched. Escaping the escape character restores control of the surrounding JavaScript string and permits code execution.
Quote and backslash escaping protects the JavaScript string but not the enclosing HTML script element. Injecting a closing script tag makes the HTML parser end the raw-text block before a new executable element is introduced.
The lab reflects the search query inside an AngularJS-controlled region. Although angle brackets and double quotes are HTML-encoded, the browser-side framework still treats {{...}} as an expression.
The application Unicode-escapes quotes, brackets, backslashes, and backticks inside a template literal. Template interpolation remains active, so a ${...} expression executes without requiring any of those filtered characters.
Search text appears inside a quoted input value while angle brackets alone are encoded. Closing the attribute with a double quote and adding an onmouseover handler creates executable markup on pointer movement.
The filter blocks standard HTML tags but accepts custom elements and global event attributes. A focusable custom element targeted by the URL fragment triggers an onfocus handler without using a permitted standard tag.
Fuzzing reveals that the filter still allows a body element and its onresize event. Placing the page in an iframe and changing its dimensions invokes the injected handler automatically.
The search parameter is copied directly into HTML body content without contextual encoding. A script element therefore becomes real markup and executes as soon as the reflected result is parsed.
Although common HTML tags and event handlers are denied, the filter permits SVG animation elements and onbegin. An animateTransform payload uses that surviving event to execute script without a user click.
Stored comments pass through replacements that sanitize only the first angle-bracket pair before reaching innerHTML. Adding a harmless pair first leaves a later image element intact, where its error handler executes.
The stored website field becomes an author's href attribute, and encoding double quotes does not constrain the URL scheme. A javascript: value executes when a reader follows the author link.
POST /post/comment persists the comment body and later inserts it as raw HTML for every viewer. A stored script element consequently executes whenever the affected post is opened.
A stored website value is embedded inside JavaScript in an anchor's onclick attribute. Because HTML entities decode before JavaScript parsing, an encoded apostrophe breaks out despite the application's escaping.
A method note for reading permission relationships in a simulated directory through a small, verifiable graph.
Sequential transcript filenames expose an authorization gap: changing the requested object retrieves another lab user's conversation and synthetic credential.
Changing the account identifier in the request returns another lab user's profile, demonstrating missing object-level authorization and exposing the synthetic API key.
The application trusts a client-controlled role cookie, so changing its value grants access to the administration panel and its privileged lab action.
Public client-side JavaScript discloses the unpredictable administration path, and the server permits an unauthenticated visitor to use its privileged action.
The robots exclusion file reveals an administrative route that lacks server-side authorization, allowing direct access and the lab's deletion action.
POST /forgot-password embeds the supplied username in an internal REST path without canonicalizing it first. Traversal segments and a URL fragment redirect the backend request to the administrator's passwordResetToken field.
The front end blocks visible /admin paths, but the backend honors X-Original-URL when selecting a route. Sending an innocuous request target with the protected path in that header reaches the administrative action.
Encoded query delimiters alter the internal reset API parameter set, exposing a path to retrieve the administrator reset token.
Requesting Carlos's account produces a redirect, but the server includes his API key in the 302 response body before the browser follows that redirect. Authorization must protect the response itself, not rely on navigation behavior.
The product page uses GET /api/products/1/price, yet the same route also accepts an undocumented PATCH request. Setting the price to zero lets an ordinary account buy the jacket without paying its intended cost.
The account page trusts a user-controlled id parameter and renders the selected user's password into an HTML password field. Browser masking changes only presentation: the underlying value remains available to the client.
Direct navigation to the account page bypasses the pending second-factor step after valid password authentication.
The account endpoint uses GUID-like identifiers rather than predictable usernames or integers. That makes guessing harder, but the application publishes Carlos's identifier in an author link.
Alternating valid and invalid login attempts resets the IP-based failure counter and permits continued password guessing.
The profile endpoint binds extra client-supplied JSON properties while updating an email address. Its response exposes roleid, and adding "roleid": 2 to the request promotes the current account to administrator.
A stay-logged-in cookie encodes the username alongside an unsalted MD5 password hash. Recomputing that predictable token for password candidates yields a valid cookie for Carlos's account.
The checkout API returns an internal discount property and also accepts that property from client-supplied JSON. Submitting a 100% discount reduces the jacket order to zero and bypasses the intended price control.
A stolen persistent-login cookie reveals a weak password hash that can be cracked offline and reused for authentication.
Interactive documentation at /api discloses the request needed to delete a user. The backend accepts that operation from the ordinary wiener account, allowing it to delete carlos without adequate authorization.
The reset submission trusts its username field instead of binding the new password to the account that requested the token.
Attacker-controlled forwarded-host metadata poisons the emailed reset URL and sends Carlos reset token to the exploit server.
Account lockout becomes the oracle: repeated failures trigger a warning only for a real username, after which a controlled password test completes the lab.
Distinct POST /login messages separate unknown users from valid accounts with wrong passwords, providing a direct response-text signal for enumeration.
The lab's two-step login flow does not securely bind the second-factor check to the account authenticated during the password step. Instead, /login2 uses a client-controlled verify cookie to decide whose code is being checked.
Response-time differences reveal when a username reaches password verification, enabling reliable account enumeration.
A single punctuation difference in the login error reveals whether a username exists, even though the two responses otherwise look identical.
Because POST /cart accepts the product price from the browser, changing price=133700 to price=1 adds the leather jacket for one cent. The server should derive prices from its own catalog rather than client input.
The cart remembers only the last coupon. Alternating SIGNUP30 and NEWCUST5 therefore reapplies both discounts until the $1,337 jacket falls below the $100 balance.
A signed quantity in POST /cart permits negative line items that offset the jacket's price. Balancing those entries to a small positive total lets checkout succeed despite insufficient funds.
An overlong email is delivered in full but truncated before authorization, yielding a stored address in the privileged domain.
Registration verifies an email address, but /my-account/change-email immediately trusts a replacement. Setting [email protected] satisfies the domain-only /admin rule.
A reusable discount makes a gift card cost less than the credit received when it is redeemed. Repeating the purchase-and-redeem cycle increases the account balance.
Calling the final checkout endpoint out of sequence skips the payment transition while the server still accepts the purchase.
Repeated quantity=99 additions overflow the cart's signed 32-bit total and wrap it below zero. Further adjustments place the total within the account balance, allowing an otherwise unaffordable purchase.
Removing current-password from the dual-use change endpoint selects a weaker branch that resets the administrator credential.
The authentication state machine assumes every user completes the role-selection transition after login. Dropping that request leaves the new session in an unsafe default state with access to administrative functionality.
The CSRF check compares the form value only with a csrf cookie, without binding either value to the authenticated session. Header injection through search lets an attacker plant a matching pair before the victim submits the email change.
The server validates CSRF tokens without binding them to the session that issued them. A token from the attacker's account therefore remains valid when the victim's browser submits it with the victim's session cookie.
The email-change handler binds the csrf form token to the non-session csrfKey cookie rather than the login session. Response-header injection through the search endpoint can install an attacker-controlled matching token and cookie.
POST /my-account/change-email requires a csrf token, but the same endpoint accepts a token-free GET request. Supplying the email in the query string returns 302 and changes the account address.
The endpoint rejects GET and invalid CSRF tokens, yet accepts POST when the csrf parameter is omitted entirely. Removing the parameter returns 302 and completes the email change.
The /social-login flow reaches /oauth-callback and refreshes the session cookie without an explicit SameSite attribute. Chrome's two-minute Lax-by-default exception then permits a cross-site POST that changes the victim's email.
Although the endpoint rejects a plain GET, its _method=POST override performs the email change during top-level navigation. That method confusion allows the victim's SameSite=Lax session cookie to accompany a cross-site request.
The confirmation script appends postId to /post/ without blocking path traversal. A crafted ../my-account/change-email URL turns a cross-site visit into a same-site redirect, so the browser's Strict cookie accompanies the request.
An XSS flaw on the support sibling domain runs in the same site as the target and opens its WebSocket endpoint with the victim's Strict cookie. Sending READY exposes Carlos's chat history across the sibling-domain trust boundary.
The "Welcome back!" message forms a one-bit SQL oracle for predicates injected through TrackingId. Length checks establish a 20-character password, and SUBSTR comparisons recover the administrator value one character at a time.
The email-change endpoint authorizes a state-changing POST using only the browser's session cookie. With no anti-CSRF token or equivalent origin check, an external page can submit the request as the authenticated victim.
Oracle EXTRACTVALUE and XMLType place the administrator password into a Collaborator subdomain requested by the database. The resulting DNS lookup carries the secret even though the application response contains no query output.
The proof arrives outside the HTTP response: the in-band response is unchanged, but EXTRACTVALUE(xmltype(... SYSTEM "http://COLLAB/" ...),'/l') FROM dual causes a Collaborator DNS interaction.
The page content remains unchanged, but a PostgreSQL payload containing pg_sleep(10) delays the response by ten seconds. That repeatable timing difference proves the TrackingId cookie reaches an executable SQL context.
The database exposes values through latency rather than response content. CASE WHEN with pg_sleep(10) establishes a 20-character password, and SUBSTRING comparisons recover each character from the administrator credential.
A two-column UNION query identifies PostgreSQL through version() and enumerates information_schema.tables to find users_occukb. Querying information_schema.columns then reveals username_gfkpyb and password_xrdklp for credential extraction.
The injectable category filter supports a two-column Oracle UNION query that enumerates all_tables and finds USERS_GSEMNG. Querying user_tab_cols reveals USERNAME_RKTXFE and PASSWORD_YCJIMQ, which return the administrator credential.
Oracle requires each UNION branch to select from a table, so adding FROM dual makes the two-column probe succeed. Rendering banner from v$version in the text-compatible column then identifies the database release.
Rendering version() through a two-column UNION exposes the MySQL banner in the category results. A Microsoft SQL Server probe would use @@version, so the returned text and accepted syntax distinguish the platforms.
ORDER BY 3 succeeds while ORDER BY 4 fails, establishing that the original query returns three columns. UNION SELECT null,null,null confirms the result shape with a compatible all-null row.
Only one UNION position can carry the required string: placing yNBXRx in column one errors, while UNION SELECT null,'yNBXRx',null displays it in column two. The visible yNBXRx value identifies column two as text-compatible.
ORDER BY 2 succeeds and ORDER BY 3 fails, proving that the product query returns two columns. UNION SELECT username,password FROM users renders both credential fields and exposes the administrator login.
This lab contains blind SQL injection in the TrackingId cookie. Successful and unsuccessful queries do not produce useful content differences, but a database exception causes a distinct HTTP error.
Only the second UNION column renders text, so separate username and password values cannot both be displayed directly. PostgreSQL concatenation with username||':'||password combines each credential pair into that single visible column.
Injecting administrator' or '1'='1' -- changes the username predicate to true and comments out the password comparison. The resulting query authenticates an administrator session without a valid password.
The category parameter is inserted before an AND released = 1 restriction in the product query. Injecting a true predicate and a comment removes both filters, causing the response to include unreleased products from every category.
The stock API evaluates the XML storeId value as SQL, although its filter inspects the value before XML entities are decoded. Encoding a UNION SELECT payload with Hackvertor bypasses the filter and returns the users table credentials.
The stock selector copies storeId from the query string into document.write; closing the option and select lets an encoded image-error payload become executable DOM in the vulnerable lab.
The search page inserts location.search into a document.write tracker string; an encoded quote-closing SVG payload escapes the image attribute and creates an executable handler in the vulnerable lab.
The result div assigns query input through innerHTML: inserted script markup stays inert, while an encoded image-error payload creates a live element and executes its handler in the vulnerable lab.
The feedback page copies returnPath from location.search into the Back link's href with jQuery attr(). Supplying a javascript: URL turns the subsequent click into script execution.
A hashchange handler decodes location.hash and concatenates it into a jQuery :contains() selector. Loading a crafted fragment in an iframe and firing print() drives the vulnerable selector into executable markup.
Unsanitized comment HTML can add username and password inputs that the simulated victim's password manager autofills. JavaScript then sends those credentials to Collaborator before the attacker signs in and deletes Carlos.
A stored comment executes JavaScript for later viewers and can read document.cookie because the session token lacks HttpOnly. Exfiltrating the administrator cookie to Collaborator enables session hijacking and deletion of Carlos.
Stored script runs in the application's origin, fetches /my-account, and extracts the current CSRF token. It can then submit that token with a forged email change, bypassing a defense that remains inaccessible only to cross-origin pages.
The search API reflects input into JSON that client code passes to eval(responseText). Escaping the JSON string and preserving valid syntax converts the reflected value into JavaScript execution in the browser.
The raw query string enters the href attribute of a canonical link in the document head. An injected accesskey and onclick handler exploit that attribute context even though angle brackets cannot create another element.
Search input is embedded in a single-quoted JavaScript string while angle brackets are HTML-encoded. An unescaped quote terminates the string, allowing a syntactically valid statement to execute.
The page escapes single quotes with backslashes but leaves attacker-supplied backslashes untouched. Escaping the escape character restores control of the surrounding JavaScript string and permits code execution.
Quote and backslash escaping protects the JavaScript string but not the enclosing HTML script element. Injecting a closing script tag makes the HTML parser end the raw-text block before a new executable element is introduced.
The lab reflects the search query inside an AngularJS-controlled region. Although angle brackets and double quotes are HTML-encoded, the browser-side framework still treats {{...}} as an expression.
The application Unicode-escapes quotes, brackets, backslashes, and backticks inside a template literal. Template interpolation remains active, so a ${...} expression executes without requiring any of those filtered characters.
Search text appears inside a quoted input value while angle brackets alone are encoded. Closing the attribute with a double quote and adding an onmouseover handler creates executable markup on pointer movement.
The filter blocks standard HTML tags but accepts custom elements and global event attributes. A focusable custom element targeted by the URL fragment triggers an onfocus handler without using a permitted standard tag.
Fuzzing reveals that the filter still allows a body element and its onresize event. Placing the page in an iframe and changing its dimensions invokes the injected handler automatically.
The search parameter is copied directly into HTML body content without contextual encoding. A script element therefore becomes real markup and executes as soon as the reflected result is parsed.
Although common HTML tags and event handlers are denied, the filter permits SVG animation elements and onbegin. An animateTransform payload uses that surviving event to execute script without a user click.
Stored comments pass through replacements that sanitize only the first angle-bracket pair before reaching innerHTML. Adding a harmless pair first leaves a later image element intact, where its error handler executes.
The stored website field becomes an author's href attribute, and encoding double quotes does not constrain the URL scheme. A javascript: value executes when a reader follows the author link.
POST /post/comment persists the comment body and later inserts it as raw HTML for every viewer. A stored script element consequently executes whenever the affected post is opened.
A stored website value is embedded inside JavaScript in an anchor's onclick attribute. Because HTML entities decode before JavaScript parsing, an encoded apostrophe breaks out despite the application's escaping.
A safe exercise in redrawing a state machine before reaching a conclusion about an authentication flaw.
A method note for reading permission relationships in a simulated directory through a small, verifiable graph.