1/12/2016

[WEB HACKING] MongoDB Injection으로 알아보는 NoSQL Injection

웹 취약점 분석에서 나름 많은 부분을 차지하는 Injection. 그 중 DB 관련한 NoSQL Injection에 대한 이야기를 할까 합니다.  

기존 SQL Injection과 비슷하나, 나름 다른점도 있고 RDBMS 이외 NoSQL DB를 사용하는 서비스도 점점 많아져 알아두고 있어야 할 부분입니다.

NoSQL이란?

NoSQL은 RDBMS와 다르게 Not only SQL, Non-Relational Operational Database로 불리며 말 그대로 SQL로 이루어지지 않은 DB를 의미합니다.
Data Model이 단순하기 때문에 가용성과 확장성이 좋습니다.

대표적으로 MongoDB 같은 데이터베이스가 있지요.
(직접 사용해보지는 않았습니다만.. 주변에 공부하던 친구들이 있었다죠)

MongoDB Injection

모든 인젝션 기법이 그렇듯이 문제는 사용자 입력값에 대한 검증 부족에서 발생합니다. Web Request를 통해 넘어온 값에 Special Character에 대해 필터링 없이 DB에서 처리되어 문법의 흐름을 바꿔 공격자가 원하는 이득을 취하는것이 기본 구조입니다.



NoSQL Injection도 동일한 형태입니다. 다만 적용되는 대상만 다를뿐이지요.

아래와 같은 특수문자를 통해서 구문을 우회하여 공격에 성공할 수 있습니다.

' " \ ; { }

샘플코드를 구해서 해볼까 하다가 OWASP에 좋은 Example 코드가 있어 그걸로 해도 좋을 것 같습니다.

아래 보시면 아시겠지만 Javascript 내 흐름을 바꿔놓는 XSS와 유사한 형태를 가지는 것을 볼 수 있을겁니다. (대다수 공격이 그래요!)

OWASP예제 기반

db.myCollection.find( { $where: function() { return obj.credits - obj.debits < 0; } } ); 
위와 같이 db 하단의 myCollection의 find 메소드를 통해 데이터를 찾는 구조를 가지고 있는 NoSQL 구문이 있습니다.

여기서 우리가 입력한 부분은 굵게 표기한 "0" 이 되겠지요.
공격자는 XSS나 SQL Injection에서 사용하는 기법과 유사하게 ;을 통해 한줄의 끝을 쓰고, 새로 구문을 작성하여 로직의 흐름을 바꿔놓을 수 있습니다.

아래 예제에서는 date에 Date 클래스로 할당받아 while을 돌며 계속 빙빙 도는 코드를 넘겨주게 됩니다.

Input : 0;var date=new Date(); do{curDate = new Date();}while(curDate-date<10000)
실제로 이 부분이 필터링 없이 넘어가게 되면 아래와 같이 우회된 구문이 완성되겠지요.

Output

function() { return obj.credits - obj.debits < 0;var date=new Date(); do{curDate = new Date();}while(curDate-date<10000); }
이런 형태로 쉽게 흐름의 변환이 가능합니다. 물론 db 데이터를 보고있는 상태에서 바로 확인이 가능하지만 blackbox test에서도 output이나 error 등을 보고 판단하여 차근차근 구문을 만들어 갈 수도 있습니다.

또 간단한 MongoDB 구문으로 테스트를 해볼까요?

간단한 취약 구문으로 테스트

db.noon.findMember()
라고 대충 이름을 지어봅니다. 실제로 구성해서 테스트해보시는게 좋습니다.
이 친구는 noon 하단에서 Member를 찾는 기능을 한다고 가정하고 아래와 같이 인자값을 줄 수 있습니다.

db.noon.findMember({ $or : [ { name : [INPUT1] } , { level : ($lte[INPUT2]) } ] } )
여기서 공격자는 테스트를 통해 대략적인 구문 형태를 추측합니다.
(항상 그렇듯이 노가다..)

우리가 공격구간으로 볼 수 있는 부분은 [INPUT1], [INPUT2] 정도가 있을 것 같습니다. [INPUT1] 에 공격을 수행한다고 하면 아래와 같이 흐름을 바꿀 수 있는 구문을 만들어야겠지요.
(물론 기본적으로 특수문자에 대한 필터링 여부를 확인을 해야합니다.)

Input : {$ne:HaHwul}
위와 같이 입력하면 $ne 연산자로 인해 name과 문자열 HaHwul이 같지 않으면 찾게되어 다수 데이터가 나타나게 됩니다.

db.noon.findMember({ $or : [ { name : {$ne:"HaHwul"}} , { level : ($lte[INPUT2]) } ] } )
사실 따지고 보면 SQL Injection이랑 별로 다를게 없습니다.
다만 공격이 수행되는 장소만 다를뿐이지요.

대체로 테스트에선 $ne 연산자 값이 상황을 뒤집을 수 있는 연산자가 많이 사용되니 로직을 파악하는 방법과 연산자에 집중하면 좋습니다.

어떻게 막아야 할 것인가?

상세한 대응방법은 플랫폼, 코드별로 조금씩 다를 수 있곘지만 대부분의 웹 공격의 대안처럼 사용자 입력값(Web Request)에 대한 철저한 필터링입니다.

위에서 대충 만든 예제로 보면

db.noon.findMember({ $or : [ { name : [INPUT1] } , { level : ($lte[INPUT2]) } ] } )
[INPUT1] , [INPUT2] 부분 모두 입력될 때 로직 자체를 우회 시킬 수 있는 특수문자 $, { } [ ] ( ) 등에 대해서 필터링 처리해야합니다.
꼭 필요하다면 db가 처리하지 않도록 변환해줘야합니다.

알아두면 좋은 MongoDB 연산자

$ne : 같지않음
$not : 복수의 데이터간 여집합을 반환
$exists : 특정키를 가지고 있는지 질의
$lt : <
$gt : >
$lte : <=
$gte : >=

Reference

https://www.owasp.org/index.php/Testing_for_NoSQL_injection
http://aroundck.tistory.com/949


HAHWUL

Security engineer, Gopher and H4cker!

Share: | Coffee Me:

2 comments:

  1. 또 여기까지 오게됐수다 ㅋㅋㅋㅋ 잘 읽고 갑니다~

    ReplyDelete
    Replies
    1. 옛날에 쓴거라 부족한게 많아요. 아무튼 감사합니다 :)

      Delete