DiceCTF 2022 web/knock-knock

조승현·2022년 2월 7일
0

diceCTF에 한번 참여해봤는데 단 한 문제도 풀지 못했다. 문제 수준이 보통이 아니다. 내가 많이 부족한걸 알지만 한 문제도 못풀 줄은 몰랐다... 다름 사람의 writeup을 봐도 처음 보는 개념이 많아서 일일이 내가 찾아봐야한다. 최선을 다해 모든 문제의 write-up을 써보고자 한다. 아... 난 왤케 못하는걸까..........

첫 화면은 별거 없다. 대충 입력하고 create하면

흠 입력한 값이 그대로 출력된다. 혹시 몰라서 XSS를 해보니 된다. 그런데 쿠키값도 설정된게 없다. id, token값이 보인다. 소스코드 파일도 주어지니 코드를 살펴보자.

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');
});

코드를 대강 읽어보면 입력한 값이 id와 token과 함께 저장하는데 id는 data의 개수, token은 id값을 secret값이 'secret-${crypto.randomUUID}'인 sha-256 암호화한 값이다.
db를 만들자마자 flag를 넣는것으로 보아 id=0에 flag가 저장된다는 것을 유추할 수 있다.

여기까지가 내가 파악한 단서였다. 이 이상 뭘 알아낼 수가 없었다. 한 가지 단서를 내가 못찾았다. 그건 바로 this.secret = `secret-${crypto.randomUUID}`; 이다. 이 randomUUID에 "()"가 없다는 것... 단순 코드 오류라니... 그래서 코드를 대충 짜서 실행시켜보면 된다.

const crypto = require('crypto');
let secret = `secret-${crypto.randomUUID}`;
let id = 0;
console.log(crypto.createHmac('sha256', secret).update(id.toString()).digest('hex'));

근데 여기가 끝이 아니다. 진짜 진짜 황당한 점이 있다. 이 결과값이 nodejs 버전에 따라 다른값이 나온다. 이게 말이되나...? 출제자의 의도가 맞을까? 16.13.2. 버전이라고 한다.

solved
악랄하다

profile
Inha University / CTF Web Player / Team Riot of Noob

0개의 댓글