# [Root Me] Python - Server-side Template Injection Introduction (có mở rộng) ## Giới thiệu ![](https://hackmd.io/_uploads/ry2WGMkR3.png) Chào mừng bạn đến với thử thách SSTI trong Python! Đây là một bài thử thách đầy thú vị về SSTI, một chủ đề rất hấp dẫn trong lĩnh vực bảo mật web. Nhiệm vụ của bạn là tìm cách RCE và lấy được flag từ máy chủ. Không cần phải lo lắng nếu bạn chưa có kiến thức về SSTI vì bài viết sẽ cung cấp cho bạn những kiến thức cơ bản và hướng dẫn chi tiết về cách khai thác SSTI trong Python bằng Jinja2. Ngoài ra, bài viết cũng sẽ giúp bạn hiểu rõ hơn về cách tận dụng các built-in và class object trong Python để thực hiện RCE. Vậy còn chần chờ gì nữa? Hãy tham gia thử thách và trở thành người giỏi nhất trong lĩnh vực SSTI! ## Mục tiêu và đối tượng 1. Mục tiêu 1. Flag nằm trên máy chủ khi mình RCE được 2. Đối tượng đáng ngờ 1. ô input content ## Triển khai Vì mình có detect được là ô input content sẽ là nơi mà mình inject thành công ![](https://hackmd.io/_uploads/BJH4GGkC3.png) Nên bây giờ mình cứ vọc quanh thằng content thôi Trang chính ra đề có đưa cho mình tham khảo về những khả năng khai thác đôi với Jinja2 [link jinja2](https://podalirius.net/en/publications/grehack-2021-optimizing-ssti-payloads-for-jinja2/) Thì mình có nghiên cứu xem qua thằng **TemplateReference** mà trang trên đề cập, thì mình có thể lợi dụng thằng đó để vào built in của global và import lib os để có thể RCE Có sẵn một vài payload mẫu nên mình sẽ thử dùng payload này ``` {{ self._TemplateReference__context.cycler.__init__.__globals__.os }} ``` ![](https://hackmd.io/_uploads/HyxFMzkRh.png) Và mình đã import thành công module os, nên mình sẽ tận dụng nó để mình liệt kê với payload sau ``` {{ self._TemplateReference__context.cycler.__init__.__globals__.os.popen('ls -a').read() }} ``` ![](https://hackmd.io/_uploads/SyG5zMJ02.png) Mình có thể thấy file .passwd là flag của mình nên mình sẽ đọc nó và submit flag thôi ![](https://hackmd.io/_uploads/B1R9zGyA3.png) Xong nhưng mình muốn mò xem tại sao họ có thể có được payload như vậy, nên mình sẽ back ngược từ phía self ra nhé (cơ bản mình muốn hiểu rõ tại sao có payload thì mình mới craft được ấy). ## Mở rộng ![](https://hackmd.io/_uploads/ryf2fGJAn.png) Ở mình sẽ xem thử thuộc tính của TemplateReference như thế nào bằng __context (cứ đi từ đầu theo người ta để nắm) > _TemplateReference__context ![](https://hackmd.io/_uploads/r11kmf1An.png) Mình thấy được có kha khá class ở đây nên mình sẽ thử init qua từng class để xem có gì vọc khong nhé ![](https://hackmd.io/_uploads/r19k7Mk02.png) ![](https://hackmd.io/_uploads/HkzWXGJ03.png) ![](https://hackmd.io/_uploads/B1hGQz10n.png) Như trên ta chia ra hai loại là Function wrapper và Slot wrapper, ## Slot Wrapper Đối vơi slot wrapper thì mình có thể dùng mro để check xem có class nào, và mình thấy được có class object nên sẽ quay về đó ![](https://hackmd.io/_uploads/HkRrKt-Ah.png) Khi ta quay về được class object thì ta có thể follow theo cách thức sau [jinja SSTI](https://book.hacktricks.xyz/pentesting-web/ssti-server-side-template-injection/jinja2-ssti#recovering-less-than-class-object-greater-than) Ta dùng subclasses để xem hết các class mà ta tận dụng được ![](https://hackmd.io/_uploads/H198tYZ0h.png) Với class object thì theo kinh nghiệm mình sẽ có class Popen ở vị trí 408 ![](https://hackmd.io/_uploads/S1ypKtb02.png) Nên mình vẫn có thể tận dụng thằng này RCE theo cách sau ![](https://hackmd.io/_uploads/HJCuKt-C2.png) Và ta vẫn lụm được flag dù đi đường dài hơn Có cách ngắn hơn và tương tự là ta đi từ class str bằng cách tạo một string giả như sau > ''.\_\_class__.\_\_mro__ Đối với cách này ta cũng sẽ đi được ngược về class object và triển khai tương tự trên ## Function Wrapper Nhưng quay về với Function wrapper thì ta có thể vọc những gì Khi ta init các funtion wrapper và go globals thì ta thấy được rất nhiều built in ở đây ![](https://hackmd.io/_uploads/SkaRKFb0n.png) Và chắc chắn luon tồn tại builtin os mặc định của python rồi ![](https://hackmd.io/_uploads/rJ5J9YZAh.png) Nếu dùng os luôn thì sẽ dùng được popen và RCE như bình thường ![](https://hackmd.io/_uploads/B1xHxqFZAn.png) Tương tự như namespace thì joiner và cycler cũng như vậy ![](https://hackmd.io/_uploads/HJx-cF-A3.png) Thì ta vẫn có thể RCE được ## Ngoại lệ ![](https://hackmd.io/_uploads/BJxz5YZAh.png) Vì khi này từ self là None nên khi ta init nó và go global ta có thể đi tới các builtin một cách dễ dàng Nhưng ở builtin này sẽ không có os mà ta phải đi vòng thông qua \_\_import_\_ ![](https://hackmd.io/_uploads/B1e45F-0n.png) Và lụm flag ![](https://hackmd.io/_uploads/Sy9E9KZCn.png) ## Tổng kết Đối với bất kì lỗi SSTI nào của python, ta sẽ làm mọi cách nếu leo được lên builtin hoặc class Object thì hầu như mọi khả năng ta có thể kiểm soát được rồi. Heheh khá thú vị