티스토리 뷰

Web

NOSQL Injection from MongoDB

Tribal 2017. 3. 28. 16:22

NOSQL 개요


  NOSQL은 Not Only SQL의 약자로 비관계형 DB를 말한다. 

  SQL은 DB에 Table을 만든 후, Column이라는 데이터가 내포한 의미를 나타내는 키워드를 사용한 관계형 DB이지만, NOSQL은 비관계형 DB이기 때문에 따로 Column 같은 데이터의 관계를 정하지 않고 사용하는 DB이다. 분산처리를 할 경우, 중앙에서 데이터를 종합할 필요가 있는데 이런 경우에 분산된 시스템마다 데이터의 관계가 다를 수 있다. 이를 위해 나온 것이 NOSQL이다.


  간단하게 SQL DB와 NOSQL DB가 어떤 관계인지 살펴보도록 하자.

  SQL DB가 계정 정보를 다룬다고 가정해보자. 먼저 SQL DB에 데이터를 저장하기 전, ID, PW, E-mail, 기타 Index 같은 Column을 생성한다. 그리고 각 사용자에 맞춰 ID, PW, E-mail, Index 같은 정보를 Column에 맞춰 채우게 된다. 만약, Column이 5개라면 모든 사용자가 5개의 Column을 채운다.


  NOSQL DB는 다르다. 딱히 관계를 정하지 않기 때문에 Column을 만들지 않고 사용자마다 채우는 갯수 등이 달라진다. 어떤 사용자는 2개의 정보를 저장하지만, 또 다른 사용자는 12개 이상의 정보를 저장할 수도 있다. Column이 지정되지 않았기 때문에 자유롭게 채울 수 있다.


  그렇다고 NOSQL DB가 SQL DB랑 비교하여 더 좋다는 것이 아니다. 2개의 DB는 필요 요구에 따라 선택하여 사용하면 된다. SQL과 NOSQL의 차이점은 다음과 같다.

참고 : http://unabated.tistory.com/entry/MongoDB-NoSQL-%EC%9D%B4%EB%9E%80


차이점 설명

  • 정해진 규격(관계)를 Schema라고 하는데, 위에서 이런 관계를 볼 수 있었다.
  • Join은 두 가지 이상의 테이블의 관계로 데이터를 검색하는 것 인데 NOSQL은 비관계형이라 불가능하다.
  • 트랜잭션은 DBMS에서 한 꺼번에 처리되는 일련의 연산을 말한다. 트랜잭션은 4가지 성질(원자성, 일관성, 격리성, 영속성)을 가지므로 따로 찾아 읽어보는 것이 좋다. 암튼, NOSQL은 분산처리에 트랜잭션을 사용하는 것은 어려움이 있다고 한다.
  • NOSQL은 분산처리 작업을 위해 나온 만큼 분산처리 작업이 쉽다.

  MongoDB는 NOSQL DBMS 중 하나이다. 따라서, 개념은 다음과 같다.

 DB > DBMS > (MySQL, MSSQL, MongoDB....)

  DB가 있다면, DB의 데이터를 저장하고 검색하듯이 관리하는 시스템이 존재할 것인데 DBMS(DataBase Management System)이 이런 시스템이다. DBMS도 벤더 등에 따라 달라져 여러 종류가 나뉘게 된다.


NOSQL Injection 실습


  NOSQL Injection이라고 SQL Injection과 크게 다르지 않다. 서로가 문법이 다르기 때문에 문법의 차이일 뿐, 공격을 위해서 접근하는 방법은 동일하다.


  실습 페이지는 여기를 기준으로 진행하였으며, login.php만 실습을 위해 아래와 같이 수정하였다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<html>
<head>
    <title>Login Check</title>
    <meta http-equiv="Content-Type" content="text/html"/>
</head>
<body>
<?php
    include "./conn.php";
 
    $id = $_POST['id'];
    $pw = $_POST['pw'];
 
    $filter = ['id' => $id'pw' => $pw];
    $options = [
        'projection' => ['_id' => 0]
    ];
 
    $query = new MongoDB\Driver\Query($filter$options);
    $rows = $mongo->executeQuery("login.user"$query);
 
    $login = current($rows->toArray());
    var_dump($login);
 
    if(!empty($login)) {
        echo "<br>Login Success";
        if(($id == $login->id) && ($pw == $login->pw)) {
            if($id == "admin") {
                echo "Welcome Admin!<br><br>";
            }
            echo "$id : $pw<br>";
        }
    } else {
        echo "Login Failed...<br>";
    }
?>
</body>
</html>
cs

  22번째 줄에 var_dump로 쿼리의 결과를 확인하는 구문을 추가하였고, 공격 성공을 확실히 보기 위해서 25번째 줄에 Login Success를 위로 끌어올렸다.

  중요한 부분은 13번째 줄인데 POST로 받은 id와 pw 변수를 filter에 채워 쿼리를 실행해 결과를 얻어온다.


  로그인 페이지에서 몇 번의 가입을 한 후, Mongo를 실행해 저장된 데이터를 가져오는 간단한 Command를 실행해 보자.

  4번째 사각박스에 보이는 db.user.find() 구문은 SQL의 Select * from Table명과 같은 의미이다. 실행해보면 저장된 모든 계정이 출력된다. SQL에서 Where을 추가해 필터링하는 것처럼 NOSQL도 find에 관계를 나타내는 Array를 추가해 필터링이 가능한데, 이 필터링을 이용해 Injection을 하면 된다.


  우선, NOSQL에 사용되는 특수한 문자는 다음과 같다.

  위의 특수 문자를 잘 삽입하여 Injection을 하면 아래처럼 일치하지 않는 결과를 획득하는 것도 가능하다.

 tribal이라는 계정의 패스워드는 'q1w2e3r4'인데, 쿼리를 날린 패스워드는 특수문자가 섞인 형태이다. pw가 'hello'가 아닌 경우, 참이 되도록 하는 구문이여서 'q1w2e3r4'는 'hello'와 다르므로 참을 반환하게 되어 위와 같은 결과를 얻을 수 있다.


  처음으로 돌아가 로그인 페이지에서 실습을 진행해보자. join.php의 소스코드를 보면 알겠지만, 중복된 ID가 존재하면 중복된 ID가 존재한다고 알려주는 구문이 있다. 이를 통해, 특정한 계정이 존재하는지 확인을 할 수 있다.

  admin을 검색해보면, admin은 이미 존재하는 계정이기 때문에 계정이 만들어지지 않는다. 이제, pw만 알면 admin으로 로그인하는 것도 가능하지만, 위처럼 Injection을 통해 우회해 볼 것이다.

  pw에 {$ne:"haha"}를 넣어보면 위처럼 실패하는 것을 볼 수 있는데, 입력한 pw가 그대로 string으로 들어가게 되어 실패하였다. pw를 문자열(string)이 아닌 배열(array)로 넣어주자. 여러 방법이 있는 것 같지만, Web 해킹 연습을 위해 python으로 코딩을 하여 해결하였다.

1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/python
import urllib, urllib2
 
param = {'id':'admin''pw[$ne]':"haha"}
print urllib.urlencode(param)
 
url = urllib2.Request('http://URL/nosql/login.php', urllib.urlencode(param))
= urllib2.urlopen(url)
 
print s.read()
 
s.close()
cs

  POST 방식으로 pw를 전송할 때, pw앞에 []를 넣어 배열(array) 형태로 인식되도록 해준 후, []안에 $ne를 넣어 pw의 안에 $ne가 들어가도록 하였다. 그리고 MongoDB는 ':'를 통해 비교를 구분하기 때문에 pw:{$ne:"haha"}의 형태로 변환된다. 그리고 pw에는 결국 {$ne:"haha"}가 전송된다.

  무사히 Injection이 되어 admin에 대한 정보를 가져온 후, Login에 성공하는 결과를 볼 수 있다.


방어방법

  • 특수문자 등을 삽입하여 공격되기 때문에 특수문자를 필터링한다.
    $ne, $not, $lt, []...
  • php의 is_array() 함수로 변수가 array인지 체크하는 방법도 방어방법이 될 수 있겠다.


'Web' 카테고리의 다른 글

CGI Buffer Overflow  (0) 2017.05.11
Apache2 cgi 동적 페이지  (2) 2017.04.05
MongoDB를 이용한 PHP 로그인 페이지  (2) 2017.03.26
MongoDB 설치 및 사용 방법  (0) 2017.03.26
php 에러 출력  (0) 2016.02.15
댓글
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30