(CTF출제) mongoboard

2024. 9. 4. 11:52·DreamHack/CTF

 

 

 

 

var express     = require('express');
var app         = express();
var bodyParser  = require('body-parser');
var mongoose    = require('mongoose');
var path        = require('path');

// Connect to MongoDB
var db = mongoose.connection;
db.on('error', console.error);
db.once('open', function(){
    console.log("Connected to mongod server");
});
mongoose.connect('mongodb://localhost/mongoboard');

// model
var Board = require('./models/board');

// app Configure
app.use('/static', express.static(__dirname + '/public'));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

app.all('/*', function(req, res, next) {
  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT");
  res.header("Access-Control-Allow-Headers", "Content-Type");
  next();
});

// router
var router = require(__dirname + '/routes')(app, Board);
app.get('/', function(req, res) {
    res.sendFile(path.join(__dirname + '/index.html'));
});

// run
var port = process.env.PORT || 8080;
var server = app.listen(port, function(){
 console.log("Express server has started on port " + port)
});

 

app.js

 

 

 

 

module.exports = function(app, MongoBoard){
    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
                }
            }));
        })
    });

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

    app.put('/api/board', function(req, res){
        var board = new MongoBoard();
        board.title = req.body.title;
        board.author = req.body.author;
        board.body = req.body.body;
        board.secret = req.body.secret || false;

        board.save(function(err){
            if(err){
                console.error(err);
                res.json({result: false});
                return;
            }
            res.json({result: true});

        });
    });
}

 

index.js

 

 

 

 

 

 

 

 

var mongoose = require('mongoose');
var Schema = mongoose.Schema;

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

module.exports = mongoose.model('board', boardSchema);

 

board.js

 

총 3개의 js 파일이 있다.

 

문제를 확인해 보자

 

 

 

 

 

 

 

 

문제를 보면 게시글이 존재해 아무거나 클릭을 해보면...

 

 

 

 

 

 

 

이렇게 글을 읽을 수 있는 구조인 거 같다.

 

 

 

 

 

 

 

 

 

 

 

 

물론 FLAG title를 누르자 날먹을 걷어차버린다.

 

MongoDB 를 사용한다 하니 이점을 알아두고 코드를 한번 봐보자.

 

 

 

 

 

 

 

 

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

 

board.js를 보면 boardSchema를 확인할 수 있다.

 

확인해 보면 title, body, author 필드들은 문자열을 저장하는 String 타입이고

 

secret을 보면 Boolean 타입을 넣어 비밀글인지 설정한다. (기본적으로는 비밀글이 아닌 false를 적용)

 

publish_date는 게시물이 작성된 날짜를 저장하는 필드이다. 

 

대충 어떤 느낌으로 게시글이 작성되는 환경인지 알 수가 있다.

 

다른 js 파일들에게서 중요한 부분을 확인해 보자.

 

 

 

 

 

 

 

 

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

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

 

index.js 에서 발췌한 중요한 코드이다.

 

요약하자면

 

첫 번째 소스코드를 보면 GET을 통해서 /api/board 경로에 모든 board 문서를 조회하고 비밀글인 경우에는 '_id'를 숨긴다.

 

두 번째 소스코드를 보면 /api/board/:board_id에서 board_id를 기반으로 개별 문서를 조회한다.

 

따라서 board_id 가 키 포인트라고 볼 수 있다.

 

문제에서는 mongoDB로 구성되어 있다고 하였으니 MongoDB에 id인 Objectid를 이야기한다.

 

그러면 Objectid를 확인해 보자

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Objectid는 문제화면에 그대로 쓰여있다.

 

그러면 Objectid에 대한 구조를 파악 후 FLAG title에 대한 ID를 구하면 FLAG를 구할 수 있을 거 같다.

 

구조 먼저 파악해 보자.

 

 

 

ObjectID

MongoDB에서 사용하는 각 문서를 고유하게 식별하기 위해 사용되는 값

 

타임스탬프(4byte) = Objectid가 생성된 시점을 나타냄 

66 d7 b271 (title Mongo)

66 d7 b277 (title Good)

 

머신 ID(5byte) = ObjectID가 생성된 기계 식별값

2 af2744230 (title Mongo)

2af2744230 (title Good)

 

증분 카운터(3byte) = 고유시키기 위해 1씩 증가시킴

2 f9 dfa (title Mongo)

2 f9 dfc (titleGood)

 

 

타임스탬프와 증분카운터가 차이를 보이고 있다 증분 카운터는 끝자리 16진수를 예상하면 '2 f9 dfb'가 나올 것 같다

 

타임스탬프를 계산을 하기 위해서는 만들어진 시간을 눈여겨봐야 한다.

 

 

 

 

 

 

(title) Mongo와 (title) Good 은 총 6초가량 차이가 나온다 그래서 타임스탬프 끝 자리를 보면 '71' , '77'이다.

 

그러면 (title) FLAG는 (title) Mongo 하고 5초가량 차이가 나기 때문에 '76' 이란걸 알 수 있다.

 

정리해서 get 방식 토대로 시도해 보면

 

 

 

 

 

 

 

플래그가 출력이 된다.

 

'DreamHack > CTF' 카테고리의 다른 글

(CTF 출제) BypassIF  (0) 2024.09.04
(CTF 출제) Random-Test  (0) 2024.09.04
(CTF출제) Type c-j  (0) 2024.09.03
(CTF출제) baby-union  (0) 2024.09.03
(CTF 출제)Out of money  (0) 2024.05.08
'DreamHack/CTF' 카테고리의 다른 글
  • (CTF 출제) BypassIF
  • (CTF 출제) Random-Test
  • (CTF출제) Type c-j
  • (CTF출제) baby-union
G_OM
G_OM
최대한 설명 열심히 하려고 합니다. 궁금한 거 있으면 언제든지 물어보셔도 좋습니다.
  • G_OM
    끄적끄적
    G_OM
  • 전체
    오늘
    어제
    • 분류 전체보기 (138)
      • 모의해킹 (7)
      • Wargame (69)
        • Linux_bandit (33)
        • Webhacking.kr (36)
      • DreamHack (52)
        • WEB (14)
        • Reverising (9)
        • System (0)
        • CTF (22)
      • Android_security (5)
      • 정보보안기사 (2)
      • IT? (3)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

    • DreamHack 에 대한 문제들...
  • 인기 글

  • 태그

    overthewire
    안드로이드
    Android
    bandit20
    리눅스 워게임
    CTF
    Linux
    wargame
    url encode
    php
    lfi
    리눅스
    Linux wargame
    난독화
    bandit17
    union
    cookies
    Dreamhack
    bandit18
    drozer
    sql injection
    워게임
    insecurebankv2
    bandit30
    Cookie
    webhacking.kr
    정보보안기사
    webhacking
    php wrapper
    bandit
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.0
G_OM
(CTF출제) mongoboard
상단으로

티스토리툴바