Categories:

Tags:



Enumeration

Reading app.js reveals its mechanics.

  1. /api/board when requested via GET method, returns all rows of data with a modified id for secret posts
  2. /api/board when requested via PUT method, saves the given data
  3. /api/board/:board_id returns a row of data corresponding to the specified id
┌──(m0nk3y@kali)-[~/DH/mongoboard]
└─$ cat app.js
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)
});
┌──(m0nk3y@kali)-[~/DH/mongoboard]
└─$ cat models/board.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);
┌──(m0nk3y@kali)-[~/DH/mongoboard]
└─$ cat routes/index.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});

        });
    });
}

Exploitation

Since /api/board/:board_id shows the contents of any posts regardless of its secrecy, if only we could find the id of the hidden post, we will be able to see its contents.

When looking at the id of each post, there seems to be some kind of pattern.

In order to find out if there truly is a pattern, I’ll write some posts.

By analyzing the results, we can see that there are two variables in play when id is assigned.
According to what is seems, current time in seconds seems to consist the former part of the id and the number of posts seems to consist the latter.

If my speculation is correct, then the id for the secret post would be 627a7fc4af693a43849915b9 since there are 4 seconds of gap between the secret post and its previous post, thus turning the id from b8 to b9 and c0 to c4.

Post Exploitation

After a successful exploitation, I’m able to view the flag.