CTF

[hkcert] Mystiz's Mini CTF (2) Write Up

Wermut 2025. 2. 27. 01:14

 

minictf-2.zip
0.70MB

 

mass assignment 취약점

 

본 문제는 mass assignment 취약점이 존재하는 문제인데, 이 취약점은 공격자가 서버에서 할당한

클라이언트의 변수를 충분히 검증하지 못해 내부의 속성으로 변환하는 취약점으로, 

 

// models.user.py

    def marshal(self):
        return {
            'id': self.id,
            'username': self.username,
            'is_admin': self.is_admin,
            'score': self.score
        }

 

문제를 보면 is_admin 이란 변수로 사용자가 admin 권한을 갖고 있는지 검증하고 있다.

 

 

이때 버프스위트를 사용해 회원가입 시에 is_admin 속성을 추가해 1로 설정해 주면 서버는 이를 그대로 적용해

공격자가 admin 권한을 탈취하는 것이 가능하다.

 

admin 경로에 접근한 모습

 

 

 

// views/_init_.py

from flask import Blueprint, request, jsonify
from flask.views import MethodView
import collections

from app.views import pages
from app.views.api import users
from app.views.api import challenges
from app.views.api.admin import challenges as admin_challenges
from app.models.user import User
from app.models.challenge import Challenge
from app.models.attempt import Attempt


class GroupAPI(MethodView):
    init_every_request = False

    def __init__(self, model):
        self.model = model

        self.name_singular = self.model.__tablename__
        self.name_plural = f'{self.model.__tablename__}s'
    
    def get(self):
        # the users are only able to list the entries related to them
        items = self.model.query_view.all()

        group = request.args.get('group')

        if group is not None and not group.startswith('_') and group in dir(self.model):
            grouped_items = collections.defaultdict(list)
            for item in items:
                id = str(item.__getattribute__(group))
                grouped_items[id].append(item.marshal())
            return jsonify({self.name_plural: grouped_items}), 200

        return jsonify({self.name_plural: [item.marshal() for item in items]}), 200


def register_api(app, model, name):
    group = GroupAPI.as_view(f'{name}_group', model)
    app.add_url_rule(f'/api/{name}/', view_func=group)
    

def init_app(app):
    # Views
    app.register_blueprint(pages.route, url_prefix='/')

    # API
    app.register_blueprint(users.route, url_prefix='/api/users')
    app.register_blueprint(challenges.route, url_prefix='/api/challenges')
    app.register_blueprint(admin_challenges.route, url_prefix='/api/admin/challenges')

    register_api(app, User, 'users')
    register_api(app, Challenge, 'challenges')
    register_api(app, Attempt, 'attempts')

 

admin 계정을 탈취한 직후 코드에 명시되어 있는 경로를 확인했을 때, 위 /views/_init_. py 파일에서

/api/admin/challenges로 접근하여 flag를 확인할 수 있다.

 

 

flag가 숨어있음을 알 수 있다.