ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 03-02. Server Side Attack - Non-Relational DBMS
    해킹/웹해킹 이론 2023. 11. 21. 02:51

    Non-Relational DBMS

    관계형 DBMS와 달리 SQL를 사용하지 않고 복잡하지 않은 데이터를 저장해 단순 검색 및 추가 검색 작업을 위해 매우 최적화된 저장 공간인 것이 큰 특징이자 RDBMS와의 차이점이다. RDBMS는 SQL이라는 정해진 문법을 통해 데이터를 저장하기 때문에 한 가지의 언어로 다양한 DBMS을 사용할 수 있다. 반면에 NoSQL은 Redis, Dynamo, CouchDB, MongoDB 등 다양한 DBMS가 존재하기 때문에 각각의 구조와 사용 문법을 익혀야한다는 단점이 있다.

    MongoDB, Redis, CouchDB에 대해서 알아보자.

    MongoDB(https://docs.mongodb.com/manual/reference/operator/query/)

    MongoDB는 JSON 형태인 도큐먼트(Document)를 저장하며, 다음과 같은 특징을 가진다.

    • 스키마를 따로 정의하지 않아 각 컬렉션(Collection)에 대한 정의가 필요하지 않습니다. (컬렉션은 RDBMS의 테이블과 비슷한 개념)
    • JSON 형식으로 쿼리를 작성할 수 있습니다.
    • _id 필드가 Primary Key 역할을 합니다.

    연산자

    Name Description
    $eq equal
    $in 배열 안의 값들과 일치하는 값을 찾는다
    $ne not equal
    $nin $in의 반대
    $and 논리적 AND
    $not 논리적 NOT
    $nor 논리적 NOR (둘 다 아니어야 함)
    $or 논리적 OR
    $exists 지정된 필드가 있는 문서를 찾는다
    $type 지정된 필드가 지정된 유형인 문서를 찾는다
    $expr 쿼리 언어 내에 집계식 사용
    $regex 지정된 정규식과 일치하는 문서 선택
    $text 지정된 텍스트 검색
    $where JavaScript 표현식을 만족하는 문서와 일치합니다.

    SQL과 문법 비교

    SQL MongoDB
    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 INTO account(user_id,user_pw,) VALUES ("guest", "guest");  db.account.insertOne(  { user_id: "guest",user_pw: "guest" })
     
    DELETE FROM account; db.account.remove()
    DELETE FROM account WHERE user_id="guest"; db.account.remove(  {user_id: "guest"})
    UPDATE account SET user_id="guest2" WHERE user_idx=2; db.account.updateOne(  { user_idx: 2 },  { $set: { user_id: "guest2" } })

    Redis(https://redis.io/commands)

    • 키-값 쌍을 가진 데이터를 저장
    • 메모리기반의 DBMS로 메모리를 사용해 다른 DBMS보다 훨씬 빨리 읽고 쓸 수 있어 임시 데이터를 캐싱하는 용도로 주로 사용된다.
    명령어 구조 설명
    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 INCR key 데이터 값 ++1
    DECR DECR key 데이터 값 --1
    INFO INFO [section] DBMS 정보 조회
    CONFIG GET CONFIG GET parameter 설정 조회
    CONFIG SET CONFIG SET parameter value 새로운 설정을 입력

    CouchDB(https://docs.couchdb.org/en/latest/api/index.html)

    • JSON 형태인 도큐먼트를 저장
    • 웹 기반의 DBMS / REST API 형식으로 요청 처리

    코드 예시

    $ 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"}

     

    요소 설명
    POST 새로운 레코드를 추가합니다.
    GET 레코드를 조회합니다.
    PUT 레코드를 업데이트합니다.
    DELETE 레코드를 삭제합니다.
    / 인스턴스에 대한 메타 정보를 반환합니다.
    /_all_dbs 인스턴스의 데이터베이스 목록을 반환합니다.
    /_utils 관리자페이지로 이동합니다.
    /db 지정된 데이터베이스에 대한 정보를 반환합니다.
    /{db}/_all_docs 지정된 데이터베이스에 포함된 모든 도큐먼트를 반환합니다.
    /{db}/_find 지정된 데이터베이스에서 JSON 쿼리에 해당하는 모든 도큐먼트를 반환합니다.

    NoSQL Injection(Mongo DB)

    Mongo DB는 데이터의 자료형으로 Object형도 사용하기 때문에 query에 다양한 값을 넣을 수 있다. data라는 파라미터로 들어오는 값을 저장하는 코드가 있다고 하자. 이런식으로 다양하게 넣을 수 있다.

    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

    따라서 우리는 이 값들에 연산자를 넣어 공격을 할 수 있다.

    http://localhost:3000/query?uid[$ne]=a&upw[$ne]=a

    이렇게 실행할 경우 데이터는 find($and{{"uid": {"$ne":"a"}},{"upw":{"$ne":"a"}}}) 로 들어가 uid가 a가 아니면서 upw도 a가아닌 유저의 정보를 리턴할 것이다.

    따라서 uid 에 admin / upw에  {"$ne":""}를 입력하는 형식으로 정보를 탈취할 수 있다.

    Blind NoSQL Injection

    $regex와 $where을 사용해 Blind NoSQL Injection을 시도할 수 있다.

      • regex
        • upw: {$regex:"표현식"}을 넣어주면 일치할 때 return을 받을 수 있다.따라서 ^a, ^aa, ^ab, ^aba 이런식으로 늘려가며 blind injection을 수행 할 수 있다. 혹은 이런식으로 ^(.{n-1}a.{0,})$ regex를 작성해 원하는 위치에 a가 있는지 검사하는 식으로도 작성할 수 있다.
      • where
        • 표현식
          • 인자로 전달한 js 표현식을 만족하는 데이터를 조회한다. (사실 이해 못했음)
          • {$where : "return 1==1"} 
        • substring
          • regex와 같이 한글자씩 비교할 수 있다. 
          • {$where: "this.upw.substring(0,1)=='a'"}
        • sleep 함수를 통한 time based injection
          • 표현식과 함께 sleep함수를 사용해 지연 시간을 통해 참/거짓 결과를 확인한다. 하단 쿼리를 던져주면 substring이 일치할 경우 sleep이 발생하고 시간지연이 일어나므로 정보를 탈취 할 수 있다.
          • /?uid=guest'&&this.upw.substring(0,1)=='a'&&sleep(5000)&&'1
        • Error based Injection
          • 에러가 나는 코드를 비교구문 뒤에 배치해 에러가 날 경우 해당 비교가 옳다는 것을 알아내는 방식
          • db.user.find({$where: "this.uid=='guest'&&this.upw.substring(0,1)=='g'&&asdf&&'1'&&this.upw=='${upw}'"}); 
    •  

     

     

     

    댓글

Designed by Tistory.