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 |