Quick lesson why you should mind your regexes.
Apparently Tiktok’s login page uses a query parameter redirect_url to save where it should return after a successful login attempt, alack a check is implemented to redirect_url to make sure it’s a trusted origin and thus prevent open redirect attacks, the check goes as follows (the code is minified/obfuscated since it’s in production a common practice):
// ...
u =
d.match(location.origin) &&
"/" === (null === (r = new URL(d)) || void 0 === r ? void 0 : r.pathname)),
(p =
[
/^https?:\/\/([\w\-.]+?\.)?tiktok\.(com|in)(\/.*)?$/,
/^https:\/\/seller\.tiktokglobalshop\.com\//,
/^https:\/\/business\.tiktokshop\.com(\/.*)?/,
].some((e) => new RegExp(e).test(d)) ||
(t &&
[
/^https:\/\/seller-boe\.byteintl\.net\//,
/^https?:\/\/([\w\-.]+?\.)?bytedance\.net(\/.*)?$/,
].some((e) => new RegExp(e).test(d)))));
// ...
The regexes seems to be sufficient, alas the third regex (/^https:\/\/business\.tiktokshop\.com(\/.*)?/) is particulary interesting since it doesn’t mandate a trailing slash (/), nor does it mandate it to finish by .com i.e. by adding a trailing dollar sign ($), this opens the door for a textbook trick known by most bug hunters/CTF players, which is that a URI (as defined in the RFC) can contain a userinfo part which is seperated from the host by an at (@), so a browser that parses this url for example https://business.tiktokshop.com@example.com will process example.com as the host, thus bypassing the intended checks.
PoC
https://www.tiktok.com/login?redirect_url=https://business.tiktokshop.com@example.com
Note that I personally won’t be bug hunting in Tiktok’s program since it’s last aquisition.