Web题:来试试WEB题吧,点击下方“立即申请”任务,即可获得本题地址。
小D最爱看的视频网站最近关站了,关站前他用 Fiddler 和 Web Archive 保存了一位主播的视频,但他发现存下来的文件无法播放。你能帮小D找回他的回忆吗?(.saz 与 .wacz 任选其一即可解题)
https://down.52pojie.cn/kFMiuT.7z | PassWord:UZFQq9BzpAG5
虽然说是web,但其实是misc题
解压查看文件类型:
52tube.saz: Zip archive data, at least v2.0 to extract, compression method=store
52tube.wacz: Zip archive data, at least v1.0 to extract, compression method=store
用Fiddler加载saz文件,找一些有用的信息
https://52tube.mmxxii/hls/live.m3u8
#EXT-X-KEY:METHOD=AES-128,URI="key://live"
POST https://52tube.mmxxii/api/drm/
h=7b10311e6e310f0df068d9ede10475a8&id=live
byte[] arrOutput = { 0x08, 0xA5, 0xE6, 0xC2, 0xC2, 0x61, 0xA8, 0xAC, 0xB4, 0xD7, 0x9C, 0x49, 0xAF, 0x16, 0x0A, 0x3A, 0xDA, 0x4E, 0x5C, 0xEA, 0xE1, 0x6F, 0xED, 0x46, 0xEB, 0x6F, 0x49, 0x8C, 0x9B, 0x63, 0xD5, 0x3B };
保存了8个ts文件,是加密的,key和iv不太好找
把一开始的script.bundle.js保存下来搜一下,发现对 /api/drm
的处理可能有用,截取关键逻辑如下,大概就是把h参数和随机生成的字节做一些异或:
(() => {
'use strict';
var t = r(631);
let e = '/api/ping/', i = '/api/drm/';
function n(t) {
return [...new Uint8Array(t)].map(t => t.toString(16).padStart(2, '0')).join('');
}
function s(t, e) {
let r = new Uint8Array(t.length);
for (let i = 0; i < t.length; i++)
r[i] = t[i] ^ e[i];
return r;
}
class a extends t.DefaultConfig.loader {
constructor(t) {
super(t);
var r = this.load.bind(this);
this.load = function (t, a, o) {
if (t && t.url.startsWith('key://')) {
let r = t.url.substr(6);
t.url = e;
var l = o.onSuccess;
o.onSuccess = function (t, e, a) {
(async function (t) {
let e = await async function () {
let t = new Uint8Array(16);
crypto.getRandomValues(t);
let e = n(t.buffer) + Date.now() + Math.random();
return new Uint8Array((await async function (t) {
const e = new TextEncoder().encode(t);
return await crypto.subtle.digest('SHA-256', e);
}(e)).slice(0, 16));
}();
var r = new URLSearchParams();
r.append('h', n(e.buffer)), r.append('id', t);
var a = {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: r
};
let o = await fetch(i, a), l = await o.arrayBuffer();
if (32 !== l.byteLength)
throw new Error('Invalid response');
let u = new Uint8Array(l.slice(0, 16)), c = new Uint8Array(l.slice(16, 32));
return s(s(u, e), c);
}(r).then(t => {
l({ data: t.buffer }, e, a);
}));
};
}
r(t, a, o);
};
}
}
var o = document.getElementById('video');
if (t.isSupported()) {
var l = new t({ loader: a });
l.loadSource('./hls/live.m3u8'), l.attachMedia(o), l.on(t.Events.ERROR, function (t, e) {
e.type, e.details, e.fatal, alert('Video load failed!'), console.error(t, e);
});
} else
alert("Your browser doesn't support HLS!");
})();
python翻译逻辑如下:
# coding:utf8
h_list = bytes.fromhex('7b10311e6e310f0df068d9ede10475a8') # IV
aa = [0x08, 0xA5, 0xE6, 0xC2, 0xC2, 0x61, 0xA8, 0xAC, 0xB4, 0xD7, 0x9C, 0x49, 0xAF, 0x16, 0x0A, 0x3A, 0xDA, 0x4E, 0x5C, 0xEA, 0xE1, 0x6F, 0xED, 0x46, 0xEB, 0x6F, 0x49, 0x8C, 0x9B, 0x63, 0xD5, 0x3B]
mid_result = []
for i in range(16):
tmp = h_list[i] ^ aa[i]
mid_result.append(tmp)
key = ''
for i in range(16):
tmp = mid_result[i] ^ aa[i+16]
key += f'{tmp:0>2x}'
print(key)
# a9fb8b364d3f4ae7afd00c28d571aaa9 key
一开始并不知道哪个是key哪个是iv,只好都试一下,看看结果是否正确
CyberChef的AES_Decrypt还是很方便的,模式是AES-128-CBC
因为只有8个ts文件,就不写脚本了
解密之后看视频,发现flag在live_00003.ts视频里
flag{like_sub_52tube}