10 min read

J2TEAM CTF Walkthrough 2023

cover
Table of Contents

Mùng 2 Xuân Quý Mão, J2TEAM có mở một event lì xì nho nhỏ. Cùng mình phá đảo nó và giật lì xì từ anh Juno nhé 😁

Lưu ý, bài viết này sẽ spoil toàn bộ lời giải của game, vì vậy nếu bạn chưa chơi thì mình khuyên bạn nên trải nghiệm thử tại đây.

Task 1

Nhiệm vụ này khá đơn giản, chỉ cần Ctrl + U xem mã nguồn là bạn sẽ thấy flag

Task 2

Ta có thể thấy flag đã hiện sẵn, nhưng không copy được. Để copy bạn hãy bật DevTools lên và inspect vào nó.

Task 3

Flag vẫn được cho sẵn, nhưng vấn đề là ta không thể nhập được flag vào form submit. Inspect ta thấy, form đã bị limit còn 6 kí tự. Vậy chỉ cần sửa value của maxlength là xong

Task 4

Flag vẫn có sẵn, nhưng khi ta nhập vào form thì nó tự đổng chuyển qua chữ thường. Ta có thể thấy có một đoạn script khiến flag nhập vào sẽ tự động chuyển qua chữ hoa

Thủ phạm gây ra chính là function này

function changeValue() {
  $flag.value = $flag.value.toLowerCase();
}

Sửa toLowerCase thành toUpperCase rồi paste vào console là xong

Task 5

Flag vẫn có sẵn, nhưng khi ta submit flag nó lại báo rằng hãy nhập một URL

Inspect ta thấy form có type input là url, vậy chỉ cần sửa thành text là xong

Task 6

Này thì dễ rồi 😁, flag có màu trùng với nền nên bị khuất.

Task 7

Nhiệm vụ chỉ rằng hãy xem thật kỹ video. Ban đầu mình tưởng nó sẽ ẩn nấp đâu đó trong video nhưng sau khi xem xong mình chẳng thấy có flag ở đoạn nào cả. Xuống xem mô tả với comment thì mới thấy

Task 8

Lần này thì không còn cho sẵn flag nữa, thử Ctrl + U xem sao

Đây rồi, nhưng tại sao không submit được nhỉ?

Thì ra thủ phạm chính là đoạn script này. Vậy thì mình sẽ submit trực tiếp qua Console:

document.querySelector("form").submit();

Task 9

Flag sẽ xuất hiện sau khi bạn click đủ 1000 lần. Mình thấy nhiều người làm task này khá hardcore. Có người thật sự bấm tay luôn, có người thì sử dụng các phần mềm auto click 😅. Mình sẽ giải task này bằng 2 cách

Cách 1

Đơn giản là cho chạy loop

for (let i = 0; i < 1000; i++) {
  document.getElementById("btn-click-me").click();
}

Cách 2

Cách này mình tình cờ phát hiện ra trong lúc đang inspect cái nút Click me

Cái data-txt trông có vẻ khá sus. Thấy có dấu bằng ở đầu mình nghĩ nó là Base64 nhưng bị đảo ngược lại.

=QEW3A1RWFzMPtkWY5kQLpkWHtETZ5ESWB1XNFURUJjS

Mình thử đảo ngược lại nó

SjJURUFNX1BWSE5ZTEtHWkpLQk5YWktPMzFWR1A3WEQ=

Sau đó giải mã Base64 và lmao nó chính là flag

Task 10

Mô tả nhiệm vụ task 10 chỉ có một tấm hình, và như thường lệ, inspect nó xem sao và ta sẽ thấy ngay flag

Task 11

Vẫn là tấm hình đó 🤔, nhưng khi inspect chỉ thấy nó là một cái div

Xem thuộc tính CSS của cái div, ta thấy link ảnh

Mở tấm ảnh sang tab mới ta sẽ thấy flag

Task 12

Mô tả nhiệm vụ là hãy phá vỡ các quy tắc bắt buộc.

Trong CTF thì có một thứ bắt buộc chính là Flag, không có Flag thì không qua được.

Vậy thì thử xóa required trong form rồi submit xem sao

Welp, và chính xác là như vậy 😁

Task 13

Lúc mình bôi đen flag thì nhận ra nó có một khoảng trắng nhỏ ngăn cách. Thử inspect xem sao.

Vậy là mình phải xóa cái element có id wolf mới có thể submit

Task 14

Solve hơn chục task chắc hẳn bạn cũng phải nhận ra rằng chỉ có làm mới có ăn, không có chuyện flag lại trơ trơ ra như thế này được 😄. Vẫn phải inspect mới biết được.

Flag lần này cho sẵn nhưng bị đảo lộn thứ tự, chỉ cần sắp xếp đúng theo thứ tự từ nhỏ đến lớn là ok

const html = `<code>J2TEAM_<span data-x="12">C</span><span data-x="8">M</span><span data-x="4">7</span><span data-x="1">K</span><span data-x="11">9</span><span data-x="22">8</span><span data-x="20">R</span><span data-x="3">7</span><span data-x="16">O</span><span data-x="15">D</span><span data-x="21">W</span><span data-x="7">3</span><span data-x="2">C</span><span data-x="5">N</span><span data-x="9">V</span><span data-x="25">G</span><span data-x="10">5</span><span data-x="19">2</span><span data-x="17">B</span><span data-x="6">1</span><span data-x="23">X</span><span data-x="24">7</span><span data-x="13">M</span><span data-x="18">I</span><span data-x="14">B</span></code>`;
const parser = new DOMParser();
const doc = parser.parseFromString(html, "text/html");
const spans = doc.querySelectorAll("span");
const characters = Array.from(spans).map((span) => ({
  character: span.textContent,
  dataX: parseInt(span.getAttribute("data-x")),
}));
characters.sort((a, b) => a.dataX - b.dataX);
const result = characters.map(({ character }) => character).join("");
console.log("J2TEAM_" + result);

Task 15

Cũng giống như task 14, nhưng thay vì là số thì nó là các chữ số tiếng anh

Task 16

Chỉ thành viên VIP mới có thể xem, vậy có nghĩa là nó có liên quan tới cookie.

Có thể thấy mình đang mang role member, mà thành viên VIP mới có thể xem. Vậy chỉ cần sửa role thành vip_member là ok 😁 Sau khi sửa cookie thì flag sẽ hiện ra, nhưng không copy được mà phải inspect rồi copy.

Task 17

Inspect ta thấy link ảnh

Dựa vào mô tả nhiệm vụ, mình thử sửa good thành god và cái ảnh đã thay đổi

Nhưng vẫn chưa thấy flag, mình mở cái ảnh sang tab mới sau đó Ctrl + U thì mới thấy flag.

Task 18

Dâng tận miệng nhưng không húp được vì bị mất con trỏ chuột 😡

Thủ phạm chính là thằng div này, xóa nó đi là xong

Task 19

Không thấy mô tả nên chắc lại giấu đâu trong source thôi

Đây rồi, cái này dễ thế nhỉ 😁

Task 20

Dựa vào mô tả nhiệm vụ, mình đoán flag sẽ được giấu ở file robots.txt. Bác nào làm SEO chắc sẽ biết nó là gì 😉

Tệp robots.txt cho trình thu thập dữ liệu của công cụ tìm kiếm biết có thể truy cập vào những URL nào trên trang web của bạn. Tệp này chủ yếu dùng để ngăn trình thu thập dữ liệu gửi quá nhiều yêu cầu cho trang web; đây không phải là cơ chế để ẩn một trang web khỏi Google.

Truy cập robots.txt ta thấy url /ctf/files/20/flag.html. Tiếp tục truy cập và chúng ta sẽ thấy flag trong mã nguồn

Task 21

Task 21 có mô tả nhiệm vụ như sau:

Đọc đoạn code PHP ta thấy nó sẽ tạo một chuỗi mới ngược lại với flag khi ta submit. Vì vậy chỉ cần đảo ngược lại flag rồi submit là được xD

Task 22

Nhìn có vẻ kèo thơm nhưng không, request đã bị chặn lại

Qua tab network có thể thấy request đã bị block

Chỉ có thể submit flag bằng AJAX, mình copy as fetch rồi qua console thêm header X-Requested-With: XMLHttpRequest rồi submit là done.

P/s: tks sep nomi-sama da hint

Task 23

Không thấy có gì hot, mình Ctrl U thử

Đây rồi, thử truy cập xem nó có gì

xD vậy chỉ cần Ctrl Shift N là xong 😉

Task 24

Không thấy gì, inspect xem sao

Chỗ này mình nghĩ được 2 hướng:

  1. Đầu ở đây có thể là thẻ <head>, nhưng check thì không thấy
  2. Đầu ở đây là headers

Mình thử check headers thì thấy flag luôn ^^

Task 25

Có vẻ flag được giấu đâu đó trong cái repo này

Thấy repo có tận 12 branch, nhưng trong đó chỉ có flag của task 26, nên mình check commit của branch mặc định trước và đã tìm thấy flag

Task 26

Đã solve bên trên

Task 27

Flag chỉ cần Ctrl U là thấy, nhưng vấn đề là phải đợi sau 1000 giây mới được submit.
Thủ phạm là thằng này:

Câu này có nhiều solution:

  1. Đợi nó chạy hết rồi submit flag 😅
  2. Dùng Cheat Engine rồi bật speedhack
  3. Set lại thời gian của đoạn script trên
  4. Submit bằng fetch

Mình dùng cách thứ 4 vì thấy nó nhanh nhất 😁

Task 28

Nhìn có vẻ trông giống một lỗi server, nhưng thực chất flag được giấu ở cuối trang

Có flag rồi thì chỉ việc submit thôi

document.querySelector('[name="flag"]').value =
  "J2TEAM_KCSP9S1FYQV8VAZ7TKTGE3NTK";
document.querySelector("form").submit();

Task 29

Inspect ta sẽ thấy hint

Nhắc tới transparent image thì không thể không nghĩ tới png và những cú lừa rồi 🤣

Mở link ảnh qua tab mới, thay đuôi jpg thành png, ta được chỉ dẫn tiếp theo là thay sang txt là sẽ thấy flag

Task 30

Vẫn như thường lệ, flag chỉ cần view source là sẽ thấy. Nhưng không submit được

Nhìn vào đoạn code PHP ta thấy, hàm substr sẽ lấy 15 kí tự đứng sau kí tự thứ 15 của flag chúng ta nhập vào, vì vậy để submit ta cần thêm 15 kí tự bất kì vào đầu flag.

Task 31

Mô tả nhiệm vụ dẫn ta tới một bài viết facebook, đó là một cái gif hình đồng hồ đang quay ngược.

Có vẻ nó như muốn gợi ý cho chúng ta rằng hãy quay ngược thời gian trở về quá khứ 😆. Vì vậy hãy check lịch sử chỉnh sửa của bài viết, bạn sẽ thấy flag

Task 32

Mô tả nhiệm vụ hãy bảo ta tìm kiếm xung quanh trang web. Từ đầu đến giờ mình chưa dùng gợi ý lần nào, nhưng lần này có vẻ như phải dùng rồi 😅

Inspect thử, ta thấy flag được encode Base64, chỉ cần decode là xong

Task 33

Mô tả nhiệm vụ bảo ta chia sẻ để lấy flag. Nhưng sau khi chia sẻ thì không có gì xảy ra.

Inspect ta thấy đoạn script của cái nút chia sẻ. Flag nằm trong cái URL callback.

Task 34

Vâng trông rất bịp. Đây là task đã khiến nhiều người dừng lại vì nghĩ đã solve xong hết tất cả các task rồi. Mình cũng suýt bị lừa nhưng mình nhận ra chưa thấy lì xì từ anh Juno đâu nên mình mới solve tiếp 😅

Flag nằm trong thẻ meta, việc còn lại là submit. Mình submit flag bằng fetch.

Task 35

Một trong những task tốn nhiều thời gian solve nhất của mình. Để có thể tìm được bài viết đầu tiên, mình đã thử dùng API để tìm nhưng vô vọng. Sau đó mình nghĩ đến Google Dork và nhờ nó mình mới solve được.

Dork mình sử dụng:

site:facebook.com intext:“Mạnh Tuấn created the group J2TEAM Community”

Flag được giấu trong cái comment này

Task 36

Câu này khá dễ, chỉ cần đổi User-Agent là được.

Task 37

Flag được giấu đâu đó trong GitHub của anh Juno github.com/j2team

Check repo readme ta thấy file flag.rar trong phần release

Xem phần wiki của repo ta sẽ thấy gợi ý pass giải nén, giờ chỉ việc giải nén lấy flag thôi 😁

Task 38

Có vẻ giống như task 32, nhưng nó không nằm trong trang này. Vậy hãy view source các trang khác.

Flag được giấu ở trang ranking

Task 39

Sau vài phút OSINT thì mình tìm được một link sus từ tài khoản Instagram của ảnh instagram.com/junookyo

Có vẻ như đó là format flag.

Check revision ta sẽ thấy một đoạn morse code, giải mã nó và viết theo format là done.

Task 40

Rất dễ, khi inspect cái ảnh ta sẽ thấy format flag và một dãy số. Viết dãy số đó dựa theo bàn phím của cái điện thoại là ra flag.

Mình không nghĩ task gần cuối mà lại dễ như này 😁

Task 41

Cuối cùng cũng đến trùm cuối

Vâng rất quen thuộc =))

Inspect thì thấy có 3 cái ảnh, mình tải cả 3 về. Sau đó sử dụng ExifTool để xem thì thấy flag trong cái ảnh png 😁

Kết

Vậy là mình đã phá đảo thành công ^^, tại thời điểm mình solve xong mình đứng top 5, cơ mà vẫn không kịp giật lì xì 😭.

Để đánh giá thì mình thấy nó cũng khá dễ thở, không khó như mấy CTF bên ATTT. Cũng dễ hiểu khi game hướng tới đối tượng người chơi là cộng đồng J2TEAM Community nói chung.