Treasure Map

前言

也不是第一次接触js逆向了

但这一道 明显 比之前接触的都好

确实学到辣很多东西

不得不说 国际赛确实比国内的比赛有东西(每次玩 都能学到很多新东西)

当然, 每次也都被虐爆 不过 很开心 痛并快乐着

不扯了 看题

先贴一篇博文 – 笔者也是引用的这个

https://rinnnt.github.io/ctf/2023/04/16/plaidctf-2023-writeup.html

解决

image-20230417164935413

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
const b64 = `
A
B
C
D
E
F
G
H
I
J
K
L
M
N
O
P
Q
R
S
T
U
V
W
X
Y
Z
a
b
c
d
e
f
g
h
i
j
k
l
m
n
o
p
q
r
s
t
u
v
w
x
y
z
0
1
2
3
4
5
6
7
8
9
+
/
=`;
export const go = async () => {
const bti = b64.trim().split("\n").reduce((acc, x, i) => (acc.set(x, i), acc), new Map());
const upc = window.buffer.shift();
const moi = await fetch(import.meta.url).then((x) => x.text())
# 改这行的import就行
const tg = await fetch(moi.slice(moi.lastIndexOf("=") + 1)).then((x) => x.json())
const fl = tg.mappings.split(";").flatMap((v, l) =>v.split(",").filter((x) => !!x).map((input) => input.split("").map((x) => bti.get(x)).reduce((acc, i) => (i & 32 ? [...acc.slice(0, -1), [...acc.slice(-1)[0], (i & 31)]] : [...acc.slice(0, -1), [[...acc.slice(-1)[0], i].reverse().reduce((acc, i) => (acc << 5) + i, 0)]].map((x) => typeof x === "number" ? x : x[0] & 0x1 ? (x[0] >>> 1) === 0 ? -0x80000000 : -(x[0] >>> 1) : (x[0] >>> 1)).concat([[]])), [[]]).slice(0, -1)).map(([c, s, ol, oc, n]) => [l,c,s??0,ol??0,oc??0,n??0]).reduce((acc, e, i) => [...acc, [l, e[1] + (acc[i - 1]?.[1]??0), ...e.slice(2)]], [])).reduce((acc, e, i) => [...acc, [...e.slice(0, 2), ...e.slice(2).map((x, c) => x + (acc[i - 1]?.[c + 2] ?? 0))]], []).map(([l, c, s, ol, oc, n], i, ls) => [tg.sources[s],moi.split("\n").slice(l, ls[i+1] ? ls[i+1]?.[0] + 1 : undefined).map((x, ix, nl) => ix === 0 ? l === ls[i+1]?.[0] ? x.slice(c, ls[i+1]?.[1]) : x.slice(c) : ix === nl.length - 1 ? x.slice(0, ls[i+1]?.[1]) : x).join("\n").trim()]).filter(([_, x]) => x === upc).map(([x]) => x)?.[0] ?? tg.sources.slice(-2, -1)[0];
import(`./${fl}`).then((x) => x.go());
}
//# sourceMappingURL=121.js.map

进入源代码后 代码复制一遍就行

注意的是

  1. moi中fetch中的import改成 ‘./xxx.js ’
  2. window.buffer = “25个字符”.split(“”), 输入

image-20230417170617809

输入flag

运用console调试代码

image-20230417170748319

根据gpt哥队这几行的解释 得知 路径要遍历到success.js

我们可以直接写个js 得到success.js的路径

image-20230417170959657

1
2
3
4
5
6
7
8
9
10
11
12
13
14
for (let i = 0; i < 200; i++) {
let moi = await fetch(`./${i}.js`).then((x) => x.text())

let tg = await fetch(moi.slice(moi.lastIndexOf("=") + 1)).then((x) => x.json())

let fl = tg.mappings.split(";").flatMap((v, l) =>v.split(",").filter((x) => !!x).map((input) => input.split("").map((x) => bti.get(x)).reduce((acc, i) => (i & 32 ? [...acc.slice(0, -1), [...acc.slice(-1)[0], (i & 31)]] : [...acc.slice(0, -1), [[...acc.slice(-1)[0], i].reverse().reduce((acc, i) => (acc << 5) + i, 0)]].map((x) => typeof x === "number" ? x : x[0] & 0x1 ? (x[0] >>> 1) === 0 ? -0x80000000 : -(x[0] >>> 1) : (x[0] >>> 1)).concat([[]])), [[]]).slice(0, -1)).map(([c, s, ol, oc, n]) => [l,c,s??0,ol??0,oc??0,n??0]).reduce((acc, e, i) => [...acc, [l, e[1] + (acc[i - 1]?.[1]??0), ...e.slice(2)]], [])).reduce((acc, e, i) => [...acc, [...e.slice(0, 2), ...e.slice(2).map((x, c) => x + (acc[i - 1]?.[c + 2] ?? 0))]], []).map(([l, c, s, ol, oc, n], i, ls) => [tg.sources[s],moi.split("\n").slice(l, ls[i+1] ? ls[i+1]?.[0] + 1 : undefined).map((x, ix, nl) => ix === 0 ? l === ls[i+1]?.[0] ? x.slice(c, ls[i+1]?.[1]) : x.slice(c) : ix === nl.length - 1 ? x.slice(0, ls[i+1]?.[1]) : x).join("\n").trim()])

if (fl.filter(([x, _]) => x === 'success.js').length > 0) {
console.log(fl)
console.log(i)
console.log(fl.filter(([x, _]) => x === 'success.js'))
}
}
# 只需要修改两次success.js, 就可以逆回去得到flag

然后重复操作(只需要修改两次success.js, 就可以逆回去得到flag)

image-20230417171221081

得到flag

Flag: PCTF{Need+a+map/How+about+200!}

思路

开始我是一整个麻的, 全靠chatgpt来得解(我还没学会js Orz)

我的思路是

  1. flag为长度为25 + 6

  2. 根据js代码的名称 (为什么有两个0.js) 作为索引 一系列操作

img

然后 想错了

做题 考察的是学习新知识的速度

这儿更好的思路就是

  1. 先审计一下js代码
  2. 然后控制台快速了解是干嘛的 (这么方便的东西 要好好把握)
  3. 想想如何跳转到success.js(借助console输出 – 脚本小子
  4. 从后往前逆 得到flag

image-20230417165609325

另外学到的脚本 - by Hur1k

从url网址获取文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import os
import requests

# 目标URL的前缀和后缀
prefix = "http://treasure.chal.pwni.ng/"
suffix = ".js"

# 下载目录
download_dir = "./downloads"

# 创建下载目录
if not os.path.exists(download_dir):
os.makedirs(download_dir)

# 批量下载文件
for i in range(200):
url = prefix + str(i) + suffix
filename = str(i) + suffix
filepath = os.path.join(download_dir, filename)

response = requests.get(url)

if response.status_code == 200:
with open(filepath, "wb") as f:
f.write(response.content)
print(f"File {filename} saved.")
else:
print(f"Error: Failed to download {url}")

提取每个文件映射表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
const fs = require('fs');
const path = require('path');

const b64 = `
A
B
C
D
E
F
G
H
I
J
K
L
M
N
O
P
Q
R
S
T
U
V
W
X
Y
Z
a
b
c
d
e
f
g
h
i
j
k
l
m
n
o
p
q
r
s
t
u
v
w
x
y
z
0
1
2
3
4
5
6
7
8
9
+
/
=`;

const bti = b64
.trim()
.split("\n")
.reduce((acc, x, i) => (acc.set(x, i), acc), new Map());
// 读取文件夹中的所有 js.map 文件
fs.readdir('E:\\CTF\\PlaidCTF\\Treasure Map', (err, files) => {
if (err) {
console.error(err);
return;
}

// 遍历 js.map 文件
files.forEach(file => {
if (path.extname(file) === '.map') { // 判断文件是否为 js.map 文件
const filePath = path.join('E:\\CTF\\PlaidCTF\\Treasure Map', file);

// 读取 js.map 文件并解析为 JSON
fs.readFile(filePath, 'utf8', (err, data) => {
if (err) {
console.error(err);
return;
}
console.log(filePath)
const tg = JSON.parse(data);

// 将 tg 传入函数进行处理
const tg_output = processTG(tg);

// 输出 tg_output 中的最后一个元素的值
console.log(tg_output[tg_output.length - 1]);
if (tg_output[tg_output.length - 1][1] === 201) {
debugger; // 下断点
}
});
}
});
});

function processTG(tg) {
return tg.mappings
.split(";")
.flatMap((v, v_Idx) =>
v
.split(",")
.filter((x) => !!x) //只有当 x 不为 falsy 值(即不为 false、null、undefined、0、NaN、空字符串 '')时才会保留
.map((input) =>
input//每个又细分了
.split("")
.map((x) => bti.get(x))//从上面的映射获取键值对的值
//得到对应数组
.reduce(
(accumulator, curValue) =>
curValue & 32
? [...accumulator.slice(0, -1), [...accumulator.slice(-1)[0], curValue & 31]] //大于32则添加value%32
: [ //小于32,则accumulator加上 curValue<<5+lastValue
...accumulator.slice(0, -1),
[
[...accumulator.slice(-1)[0], curValue]
.reverse()
.reduce((acc, cur) => (acc << 5) + cur, 0)
]
]
.map((x) => {
// 如果是数值,直接返回
if (typeof x === "number") {
return x;
} else {
// 否则根据 x[0] 的值返回相应的结果
if (x[0] & 0x1) {
if (x[0] >>> 1 === 0) {
return -0x80000000;
} else {
return -(x[0] >>> 1);
}
} else {
return x[0] >>> 1;
}
}
})
.concat([[]]),
[[]]
)
.slice(0, -1)
)//最好记录一下到这里干嘛了
.map(([c, s, ol, oc, n]) => [v_Idx, c, s ?? 0, ol ?? 0, oc ?? 0, n ?? 0])//如果存在null或undefined就换掉
.reduce(
(acc, cur, curIdx) => [
...acc,
[v_Idx, cur[1] + (acc[curIdx - 1]?.[1] ?? 0), ...cur.slice(2)]
],
[]
)
)//record
.reduce(
(acc, e, i) => [
...acc,
[
...e.slice(0, 2),
...e.slice(2).map((x, c) => x + (acc[i - 1]?.[c + 2] ?? 0))
]
],
[]
);

}

CSS - buhui

收集一下jiaoben

来自 上面贴的博客

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
let tops = ['0px', '20px', '-380px', '-60px', '40px', '-20px', '-180px', '-80px', '-80px', '-80px', '-40px', '-60px', '-20px', '-240px', '-140px', '-100px', '-20px', '-20px', '-120px', '-160px', '-380px', '20px', '-20px', '-160px', '-200px', '-80px', '-60px', '-60px', '60px', '-140px', '-60px', '-240px', '60px', '-80px', '-180px', '-60px', '40px', '-60px', '-240px', '-60px', '-220px', '40px', '-260px', '0px', '-20px', '-60px', '-120px', '60px', '-240px', '40px', '-60px', '-20px', '40px', '-60px', '20px', '40px']

for (let n = 0; n < 14; n++) {
let grandparent = document.children.item(0).children.item(1).children.item(0)
let parent = grandparent.children.item(8 + n)
let details = parent.children.item(6)
let done = false
for (let i = 0; i < 26; i++) {
details.children.item(i).open = false
}
for (let i = 0; i < 27; i++) {
for (let j = 26; j < 52; j++) {
details.children.item(j).open = false
}
for (let j = 26; j < 53; j++) {
for (let k = 52; k < 78; k++) {
details.children.item(k).open = false
}
for (let k = 52; k < 79; k++) {
if (window.getComputedStyle(details.children.item(78).children.item(0)).top == tops[n * 4 + 0] && window.getComputedStyle(details.children.item(79).children.item(0)).top == tops[n * 4 + 1] && window.getComputedStyle(details.children.item(80).children.item(0)).top == tops[n * 4 + 2] && window.getComputedStyle(details.children.item(81).children.item(0)).top == tops[n * 4 + 3]) {
done = true
break
}
if (k < 78) {
details.children.item(k).open = true
}
}
if (done) {
break
}
if (j < 52) {
details.children.item(j).open = true
}
}
if (done) {
break
}
if (i < 26) {
details.children.item(i).open = true
}
}
}

image-20230417171712974

image-20230417171749360

image-20230417171951815

Flag: PCTF{youre_lucky_this_wasnt_a_threesat_instance}