Challenge

犬のプロフィールを作成できるWebアプリを公開しました。

image.png

Solution

ゴール

session.username=”admin”の状態にして/admin にアクセスするとフラグが得られる。

Adminは最初から存在しているがパスワードが不明

let users = {
  admin: {
    password: crypto.randomBytes(16).toString('hex'),
    avatar: '🍭',
    description: 'I am admin!'
  }
};

getFilteredProfile()

const DEFAULT_PROFILE = { avatar: '🐶', description: 'bow wow!' };

function getFilteredProfile(username) {
  const profile = users[username];
  const filteredProfile = Object.entries(profile).filter(([k]) => {
    return k in DEFAULT_PROFILE;
  });
  return Object.fromEntries(filteredProfile);
}

ユーザ名を与えてDEFAULT_PROFILEのキー(avatar/description)を返すように見える。

DEFAULT_PROFILEにpasswordを生やせると嬉しい。

/register

app.post('/register', async (req, res) => {
  const { username, password, profile } = req.body;

  if (users.hasOwnProperty(username)) { ... }  // 既存チェック

  users[username] ??= { password, ...DEFAULT_PROFILE };

  for (const key in profile) {
    users[username][key] = profile[key];
  }

  req.session.username = username;
  return res.send({ 'message': 'ok' });
});

ここでPrototype Pollutionできる。

username="__proto__" を入れると users["__proto__"]Object.prototype を指す。 ??=(Nullishのみ代入)は発生せず、profile{ "password": true } を設定すると users["proto"]["password"] = true → Object.prototype.password = true となる。