본문 바로가기

드림핵

[드림핵 워게임] mongoboard

<문제>

 

 

<풀이>

문제 사이트에 접속하면 처음으로 볼 수 있는 페이지이다.

MongoDB에 저장된 게시물 목록을 볼 수 있고 게시물을 작성할 수 있는 기능이 있는 것 같다.

 

맨 위에 있는 게시물을 클릭해보면 World! 라는 게시물 내용을 볼 수 있다.

 

no가 없는 admin이 작성한 FLAG라는 게시물을 클릭해보면 Secret Document!라는 alert창이 뜨고 내용을 보여주지 않는다.

 

var boardSchema = new Schema({
    title: {type:String, required: true},
    body: {type:String, required: true},
    author: {type:String, required: true},
    secret: {type:Boolean, default: false},
    publish_date: { type: Date, default: Date.now  }
}, {versionKey: false });

제시된 문제 파일 중에서 /models/board.js 파일을 열어보면 스키마를 확인할 수 있다.

title, body, author, secret, publish_date가 있는 것을 확인할 수 있다.

그리고 스키마에 나와있지 않지만 MongoDB에 저장된 모든 도큐먼트는 _id라는 Primary Key를 가지므로 _id도 있을 것이다.

 

    app.get('/api/board', function(req,res){
        MongoBoard.find(function(err, board){
            if(err) return res.status(500).send({error: 'database failure'});
            res.json(board.map(data => {
                return {
                    _id: data.secret?null:data._id,
                    title: data.title,
                    author: data.author,
                    secret: data.secret,
                    publish_date: data.publish_date
                }
            }));
        })
    });

/routes/index.js 파일을 열어보면 GET 방식으로 /api/board에 접속했을 때

board에 있는 _id, title, author, secret, publish_date를 출력해주는 코드를 볼 수 있다.

이때 secret이 true이면 _id는 null로 출력된다.

 

/api/board에 접속해본다.

board에 저장되어있는 도큐먼트들이 출력된 것을 확인할 수 있다.

앞서 봤던 admin의 FLAG 게시물은 secret이 true라서 no가 null로 출력된 것도 볼 수 있다.

 

    app.get('/api/board/:board_id', function(req, res){
        MongoBoard.findOne({_id: req.params.board_id}, function(err, board){
            if(err) return res.status(500).json({error: err});
            if(!board) return res.status(404).json({error: 'board not found'});
            res.json(board);
        })
    });

/routes/index.js의 코드에서 /api/board/:board_id로 접속하면

해당 _id를 갖는 도큐먼트를 json 형식으로 응답하는 것을 확인할 수 있다.

 

맨 위에 있는 게시물의 _id를 이용해 /api/board/:board_id로 접속해본다.

해당 게시물의 도큐먼트가 조회되어 출력되고

body필드에 게시물의 내용이 뜨는 것을 볼 수 있다.

 

FLAG 게시물의 _id를 알아낼 수 있으면 위와 같이 접근해서 게시물의 내용을 읽을 수 있을 것이다.

 

FLAG 게시물의 _id를 알아내기 위해서는 _id가 생성되는 방식을 알아야 한다.

_id는 하나의 컬렉션 내에 있는 각각의 도큐먼트마다 고유한 값을 가지며

어떤 데이터형이든 가질 수 있지만 기본 데이터형은 ObjectId이다.

 

ObjectId에 대해서는 MongoDB에서 제공하는 Manual에 잘 설명되어 있다.

https://www.mongodb.com/docs/manual/reference/method/ObjectId/

 

ObjectId() — MongoDB Manual

Docs Home → MongoDB Manual ObjectId( )Returns a new ObjectId. The 12-byte ObjectId consists of:A 4-byte timestamp, representing the ObjectId's creation, measured in seconds since the Unix epoch.A 5-byte random value generated once per process. This rando

www.mongodb.com

 

ObjectId는 총 12-byte이며

 

4-byte는 초 단위로 찍은 타임스탬프,

5-byte는 프로세스 단위로 생성한 랜덤 값,

3-byte는 증가하는 counter

로 구성되어 있다.

 

_id가 공개되어있는 게시물의 _id를 통해 숨겨진 FLAG 게시물의 _id를 유추해볼 것이다.

 

 

64e43015abd94b0030e65c6a 
64e43017abd94b0030e65c6b
null
64e4301dabd94b0030e65c6d

 

공개되어 있는 게시물들의 _id 이다.

 

 

보기 좋게4-byte, 5-byte, 3-byte로 끊어본다.

 

64e43015 | abd94b0030 | e65c6a
64e43017 | abd94b0030 | e65c6b
null
64e4301d | abd94b0030 | e65c6d

 

프로세스 당 생성한 가운데 5-byte가 모두 같은 것을 보니

FLAG의 가운데 5-byte도 같은 값(abd94b0030)을 가질 것으로 예상할 수 있다.

 

그리고 counter에 해당하는 마지막 3-byte가 1씩 증가하고 있으므로

FLAG의 마지막 3-byte는 e65c6b에서 1 증가한 e65c6c일 것으로 예상할 수 있다.

 

 

첫 4-byte는 타임스탬프이므로 publish_date와 함께 보면 계산할 수 있을 것이다.

 

64e43015 | abd94b0030 | e65c6a

2023-08-22T03:48:37.325Z


64e43017 | abd94b0030 | e65c6b
2023-08-22T03:48:39.334


null

2023-08-22T03:48:41.335Z


64e4301d | abd94b0030 | e65c6d

2023-08-22T03:48:45.337Z

 

첫번째 게시물과 두번째 게시물은 타임스탬프가 2만큼 차이나고, publish_date는 2초만큼 차이나는 것을 볼 수 있다.

두번째 게시물과 네번째 게시물은 타임스탬프가 6만큼 차이나고, publish_date도 6초만큼 차이나는 것을 볼 수 있다.

 

두번째 게시물과 FLAG 게시물은 publish_date가 2초만큼 차이나므로

타임스탬프도 2만큼 차이가 나서 FLAG의 타임스탬프는 64e43019일 것으로 예상할 수 있다.

 

이렇게 찾은 FLAG게시물의 _id는 64e43019abd94b0030e65c6c이다.

(이 _id 값은 생성한 문제 서버에 따라 다른 값을 가질 수 있다.)

 

/api/board/64e43019abd94b0030e65c6c로 접속해본다.

 

FLAG 가 출력되었다.