Skip to main content

Command Palette

Search for a command to run...

UTCCTF Write UP 2026

Updated
6 min readView as Markdown
UTCCTF Write UP 2026

Write up Small Blind - Binary Exploit 2025

Author: niddalA Date: 13/03/2026 image

Phân tích

Bài chỉ cho một link kết nối tới server và nói chúng ta sẽ chơi poker Đầu tiên sẽ kết nối tới server image Về luật chơi của Poker hãy xem link sau: https://youtu.be/CpSewSHZhmo Quay lại với bài tác giả không muốn chúng ta thắng như bình thường bây giờ hãy khai thác chương trình khi chúng ta nhập input ở Enter your name: image Ở đây chương trình đã mắc lỗi Format-String ở mục nhập tên thay vì in lại %p thì nó lại in ra các địa chỉ như: 0x16.

Tiếp tục ta có thể dùng %n để ghi đè giá trị của biến chips của bản thân. Để làm như thế thì dùng ta cần phải biết biến chips nằm ở đâu. Sau khi thử với %p%s tôi không tìm được chips nằm ở đâu, sau khi hỏi tác giả, author gợi ý dùng %x Ta dùng pwntools để làm việc này:

from pwn import *

def find_chips_with_hex():
    context.log_level = 'error'
    p = remote('challenge.utctf.live', 7255)
    
    # Gửi một chuỗi dài các %x để soi toàn bộ Stack dưới dạng Hex
    # Chúng ta tìm số 1f4 (500) hoặc 3e8 (1000)
    payload = "START." + ".%x"*40
    p.sendlineafter(b"name: ", payload.encode())
    
    p.recvuntil(b"Welcome to the table, START.")
    data = p.recvuntil(b"!", drop=True).decode()
    p.close()
    
    parts = data.split('.')
    print("\n[!] Stack View (Hex):")
    for i, val in enumerate(parts):
        if i == 0: continue
        # 1f4 là 500 chip
        if "1f4" in val.lower():
            print(f"Index {i:2}: {val}  <-- CÓ THỂ LÀ BIẾN CHIPS!")
        else:
            print(f"Index {i:2}: {val}")

find_chips_with_hex()

Chạy chương trình ta thấy image Nghi ngờ các index 1,6,7,8,9,13 Sau khi test ta xác nhận index 7

Exploit

image Sau khi trigger được hệ thống: image Ta tiếp tục exploit Ở đây ta đã đạt được mục đích của tác giả bằng chứng là tới bước này ta không cần chơi poker và chọn n image Mục đích chỉ là có chips cực lớn vượt quá chips có thể thắng một cách 'bình thường' => FLAG!!! FLAG: utflag{counting_chars_not_cards}

Write up House Of Joy

Author: niddalA Date: 13/03/2026 image

Phân tích

Challenge cung cấp cho chúng ta một file binary Tiếp theo hãy dùng Ghidra để phân tích nó image Ta đã có source code:

undefined8 main(void)

{
  size_t sVar1;
  int local_5c;
  char local_58 [76];
  int local_c;
  
  setup();
  local_c = -0x21524111;
  printf("What is your name? ");
  fgets(local_58,0x40,stdin);
  sVar1 = strcspn(local_58,"\n");
  local_58[sVar1] = '\0';
  printf("Hello, ");
  printf(local_58);
  puts("!");
  printf("Enter the secret code: ");
  __isoc99_scanf(&DAT_0010203c,&local_5c);
  if (local_5c == local_c) {
    print_flag();
  }
  else {
    puts("Wrong! Nice try.");
  }
  return 0;
}

Ở đây vì chương trình cho chúng ta file binary và chúng ta chạy local và có flag thay vì cố gắng tìm lỗi ta hãy tìm luôn flag vì nó đã được tải về theo. Phân tích ta thấy hàm print_flag() ta tiếp tục tìm đến nó: image

void print_flag(void)

{
  byte local_28 [28];
  int local_c;
  
  local_28[0] = 0x37;
  local_28[1] = 0x36;
  local_28[2] = 0x24;
  local_28[3] = 0x2e;
  local_28[4] = 0x23;
  local_28[5] = 0x25;
  local_28[6] = 0x39;
  local_28[7] = 0x24;
  local_28[8] = 0x72;
  local_28[9] = 0x30;
  local_28[10] = 0x2f;
  local_28[0xb] = 0x76;
  local_28[0xc] = 0x36;
  local_28[0xd] = 0x1d;
  local_28[0xe] = 0x31;
  local_28[0xf] = 0x36;
  local_28[0x10] = 0x30;
  local_28[0x11] = 0x73;
  local_28[0x12] = 0x2c;
  local_28[0x13] = 0x25;
  local_28[0x14] = 0x1d;
  local_28[0x15] = 0x2e;
  local_28[0x16] = 0x71;
  local_28[0x17] = 0x76;
  local_28[0x18] = 0x29;
  local_28[0x19] = 0x71;
  local_28[0x1a] = 0x26;
  local_28[0x1b] = 0x3f;
  for (local_c = 0; local_c < 0x1c; local_c = local_c + 1) {
    putchar((uint)(local_28[local_c] ^ 0x42));
  }
  putchar(10);
  return;
}

Ở đây thuật toán sẽ là 0x37 ^ 0x42 = 0x75 -> u Cứ thế ta sẽ được chuỗi FLAG!!! FLAG: utflag{f0rm4t_str1ng_l34k3d}

Write up Rude Guard

Author: niddalA Date: 13/03/2026 image Ở Challange này tiếp tục cung cấp cho chúng ta file binary Dùng ghidra để xem được source code của chương trình. image Tìm hàm main và ta được Source Code:

undefined8 main(int param_1,long param_2)

{
  int iVar1;
  
  if (param_1 == 1) {
    puts("Are you not going to say hello?");
  }
  else {
    iVar1 = atoi(*(char **)(param_2 + 8));
    if (iVar1 == 0x656c6c6f) {
      puts("Hi. What do you want.");
      read_input(0);
    }
    else {
      puts("Hi. Go away.");
    }
  }
  return 0;
}

Tiếp theo chúng ta xem hàm read_input() làm những gì image

undefined8 read_input(int param_1)

{
  int iVar1;
  char local_28 [32];
  
  read(param_1,local_28,100);
  iVar1 = strcmp(local_28,"givemeflag\n");
  if (iVar1 == 0) {
    puts("How rude! utflag{you\'re going to need a sneakier way in...}");
  }
  else {
    puts("I won\'t let you pass. No matter what.");
  }
  return 0;
}

Ở đây ta nhận được chuỗi flag giả là: utflag{you\'re going to need a sneakier way in...} Ta thấy trong main chỉ có hàm read_input được gọi nhưng bây giờ hãy thử tìm kiếm hàm ẩn bằng lệnh sau: image Ở đây thay vì cố gắng NOP để vào nó ta tìm kiếm nó luôn trong ghidra vì challenge không bắt chúng ta chạy trên server nên chắc chắn flag sẽ load luôn vào khi ta tải file. image Source Code:

undefined8 secret_function(void)

{
  byte local_48 [52];
  int local_14;
  byte local_d;
  int local_c;
  
  local_d = 0x32;
  local_48[0] = 0x47;
  local_48[1] = 0x46;
  local_48[2] = 0x54;
  local_48[3] = 0x5e;
  local_48[4] = 0x53;
  local_48[5] = 0x55;
  local_48[6] = 0x49;
  local_48[7] = 0x55;
  local_48[8] = 0x47;
  local_48[9] = 6;
  local_48[10] = 0x40;
  local_48[0xb] = 0x56;
  local_48[0xc] = 0x6d;
  local_48[0xd] = 0x45;
  local_48[0xe] = 6;
  local_48[0xf] = 0x41;
  local_48[0x10] = 0x6d;
  local_48[0x11] = 0x45;
  local_48[0x12] = 1;
  local_48[0x13] = 6;
  local_48[0x14] = 0x59;
  local_48[0x15] = 0x57;
  local_48[0x16] = 0x40;
  local_48[0x17] = 0x6d;
  local_48[0x18] = 0x46;
  local_48[0x19] = 0x5a;
  local_48[0x1a] = 6;
  local_48[0x1b] = 0x5c;
  local_48[0x1c] = 0x6d;
  local_48[0x1d] = 0x5b;
  local_48[0x1e] = 0x6d;
  local_48[0x1f] = 0x46;
  local_48[0x20] = 0x5a;
  local_48[0x21] = 2;
  local_48[0x22] = 0x47;
  local_48[0x23] = 0x55;
  local_48[0x24] = 0x5a;
  local_48[0x25] = 0x46;
  local_48[0x26] = 0x4f;
  local_14 = 0x27;
  for (local_c = 0; local_c < local_14; local_c = local_c + 1) {
    putchar((uint)(local_48[local_c] ^ local_d));
  }
  return 0;
}

Tính toán ta sẽ ra FLAG!!! FLAG: utflag{gu4rd_w4s_w34ker_th4n_i_th0ught}

8 views