พลาดเบอร์ไหนถึงจะโดน hack ผ่าน kubelet
Published: 2021-02-05 22:06:23

มีข่าวหนึ่งเมื่อวันที่ 3 กุมภาพันธ์ 2564 เกี่ยวกับ Kubernetes จาก [Unit42 ของ PaloAlto Network](https://unit42.paloaltonetworks.com/about-unit-42/) เนื้อหาก็ประมาณว่า [มี malware ชื่อว่า Hildegard มุ่งโจมตี Kubernetes โดยเฉพาะ](https://unit42.paloaltonetworks.com/hildegard-malware-teamtnt/)  พออ่านลงไปในรายละเอียดของเนื้อหา จุดเริ่มต้นของการโจมตีในกรณีนี้ เกิดจากข้อผิดพลาดในการกำหนดค่า Configuration ของ kubelet เปิดให้เข้าถึงได้โดยไม่ต้องระบุตัวตน จากนั้นก็เรียกคำสั่งให้ทำงานใน container ผ่านทาง kubelet API และในเนื้อหายังบอกอีกว่า ถ้าติดตั้ง Kubernetes แบบมาตรฐานจะเปิดให้เข้าถึงได้โดยไม่ต้องระบุตัวตนเป็นค่าปกติ
> Standard Kubernetes deployments come with anonymous access to kubelet by default.

"งานเข้าล่ะ" คำแรกที่อยู่ในหัวตอนที่อ่านข่าวนี้ เริ่มตั้งถามหลายข้อในหัว แล้วค่อย ๆ ไล่หาคำตอบจนคิดว่า น่าจะสบายใจแล้ว
- จะรู้ได้ไงว่า kubelet เปิด anonymous access อยู่
- จะเรียกใช้งานคำสั่งให้ทำงานใน container ผ่านทาง kubelet API ได้อย่างไร

###จะรู้ได้ไงว่า kubelet เปิด anonymous access อยู่
ไปเจอใน [github Liz Rice](https://github.com/lizrice) VP Open Source Engineering at @aquasecurity เขียนไว้ 

วิธีการการทดสอบ
```bash
curl -sk https://<node_ip>:10250/pods/
```
* ถ้าได้ status code เป็น 401 และมีข้อความตอนกลับมาว่า Unauthorized แสดงว่า ค่า --anonymous-auth ใน kubelet configuration เป็น false อยู่
* ถ้าได้ status code เป็น 403 และมีข้อความตอนกลับมาว่า Forbidden (user=system:anonymous, verb=get, resource=nodes, subresource=proxy) แสดงว่า ค่า --anonymous-auth ใน kubelet configuration เป็น true และ --authorization-mode เป็น Webhook
* ถ้าได้ข้อมูลของ Pod แสดงว่า ค่า --anonymous-auth ใน kubelet configuration เป็น true และ --authorization-mode เป็น AlwaysAllow

###จะเรียกใช้งานคำสั่งให้ทำงานใน container ผ่านทาง kubelet API ได้อย่างไร
วิธีการการทดสอบ
```bash
curl -ks -X POST https://<node_ip>:10250/run/<namespace>/<pod>/<container> -d "cmd=<command>"
```

>ระบบที่ใช้ในการทดสอบ
* Kubernetes version 1.15.2 และ 1.20.2 (ล่าสุด ณ.วันที่ 6 กุมภาพันธ์ 2564)
* **ติดตั้ง Kubernetes ด้วย kubeadm**
* node จำนวน 2 nodes มี ip เป็น 192.168.254.74, 192.168.254.75
```bash
root@cp0:~# kubectl get nodes -o wide
NAME      STATUS   ROLES    AGE   VERSION   INTERNAL-IP      EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION     CONTAINER-RUNTIME
cp0       Ready    master   14h   v1.15.2   192.168.254.71   <none>        Ubuntu 20.04.1 LTS   5.4.0-51-generic   containerd://1.3.3-0ubuntu2.2
node0-0   Ready    <none>   14h   v1.15.2   192.168.254.74   <none>        Ubuntu 20.04.1 LTS   5.4.0-51-generic   containerd://1.3.3-0ubuntu2.2
node0-1   Ready    <none>   14h   v1.15.2   192.168.254.75   <none>        Ubuntu 20.04.1 LTS   5.4.0-51-generic   containerd://1.3.3-0ubuntu2.2
```
###ทดสอบหลังจากติดตั้งทันที
```bash
#[at control plane]
root@cp0:~#  curl -sk https://192.168.254.74:10250/pods/
Unauthorized
```
>ผลการทดสอบพบว่า ไม่สามารถเข้าถึง kubelet โดยไม่ต้องระบุตัวตนได้

###ตรวจสอบ kubelet configuration ในส่วนของ Authentication และ Authorization
```bash
#[at node0-0]
root@node0-0:~# cat /var/lib/kubelet/config.yaml |grep -A 12 authentication
authentication:
  anonymous:
    enabled: false
  webhook:
    cacheTTL: 2m0s
    enabled: true
  x509:
    clientCAFile: /etc/kubernetes/pki/ca.crt
authorization:
  mode: Webhook
  webhook:
    cacheAuthorizedTTL: 5m0s
    cacheUnauthorizedTTL: 30s
```
>พบว่ามีการกำหนดค่าในส่วนของ Authentication และ Authorization ไว้

###ทดสอบตั้งค่า Authentication เป็น false และ Authorization เป็น AlwaysAllow
```bash
#[at node0-0]
root@node0-0:~# cat /var/lib/kubelet/config.yaml |grep -A 4 authentication
authentication:
  anonymous:
    enabled: true
authorization:
  mode: AlwaysAllow

root@node0-0:~# systemctl restart kubelet

#[at control plane]
root@cp0:~# curl -sk https://192.168.254.74:10250/pods/ | jq |head -n 15
{
  "kind": "PodList",
  "apiVersion": "v1",
  "metadata": {},
  "items": [
    {
      "metadata": {
        "name": "weave-net-27v7f",
        "generateName": "weave-net-",
        "namespace": "kube-system",
        "selfLink": "/api/v1/namespaces/kube-system/pods/weave-net-27v7f",
        "uid": "93ac4298-5eb7-497f-b914-9c4183230050",
        "resourceVersion": "72856",
        "creationTimestamp": "2021-02-05T07:35:15Z",
        "labels": {

```
> พบว่า สามารถแสดงรายการของ Pod ใน node นั้นได้

###เรียกคำสั่ง hostname ที่ Container testcon ใน Pod testpd ผ่านทาง kubelet API
```bash
root@cp0:~#  curl -ks -X POST https://192.168.254.74:10250/run/default/testpod/testcon -d "cmd=hostname"
testpod
```
###ทดสอบลบ kubelet configuration ในส่วนของ Authentication และ Authorization ออกทั้งหมด
```bash
#[at node0-0]
root@node0-0:~# cat /var/lib/kubelet/config.yaml | grep authentication
root@node0-0:~# systemctl restart kubelet

#[at control plane]
root@cp0:~# curl -sk https://192.168.254.74:10250/pods/
Unauthorized
```
>พบว่า ไม่สามารถเข้าถึง kubelet โดยไม่ต้องระบุตัวตนได้

##สรุป
* ในการทดสอบ Kubernetes ทั้ง version 1.15.2 และ 1.20.2 ได้ผลการทดสอบเหมือนกัน
* ค่าปกติหลังจากติดตั้ง Kubernetes ด้วย kubeadm ไม่สามารถเข้าถึง kubelet โดยไม่ต้องระบุตัวตนได้
* ต้องกำหนดค่า authentication และ authorization ถึงจะเข้าถึง kubelet โดยไม่ต้องระบุตัวตนได้
```yaml
authentication:
  anonymous:
    enabled: true
authorization:
  mode: AlwaysAllow
```
* **Hacker อาจจะมีวิธีการไม่สามารถเข้าถึง kubelet โดยไม่ต้องระบุตัวตนได้ ในรูปแบบอื่น ๆ ได้อีก** 

test [at] your own risk
####มูลค่าความสุข