Trong giải CyberSpace này thì team mình đã đạt thứ hạng khá cao là top 2. Nói riêng về mảng web thì team mình còn 1 bài web logic là không kịp làm, cá nhân mình không tự solve được bài nào cả, và bài Twig Playgound là challenge mà mình tốn nhiều thời gian để làm nhất (nhưng vẫn không solve được)
Khi merge lại để chơi chung thì điểm lợi sẽ là mình được trao đổi, học hỏi kiến thức của những teammates khác. Từ đó giảm thiểu việc mình lún quá sâu vào các rabbithole hoặc đi sai hướng. Nhưng nó đã hại mình khá nhiều vì tạo cho mình thói quen xem hướng của anh em trước, khiến việc spot vuln và hàm lỗi của mình trở nên rất kém. Điều này mình đã và đang cố gắng khắc phục, kết quả giải này cho thấy quá trình này vẫn còn rất dài khi mình vẫn chưa thể tự mình làm được 1 challenge nào một cách hẳn hoi.
Mình viết lại write up này cũng chỉ chia sẻ lại góc nhìn của mình về challenge, cũng như những kiến thức mình học được vì mình ấn tượng nhất và thấy bài này khá hay. Thôi không dài dòng nữa, bắt đầu thôi!
Đề bài rất straightforward, là một website để render Twig template online, thì mình cũng hiểu được là phải khai thác lỗi SSTI tại challenge này.
Flag được move vào / và đổi tên, yêu cầu đưa ra chắc chắn là RCE
Source của challenge rất đơn giản, đưa thẳng input của user vào template để render, nhưng điều mình chú ý là cái đống blacklist này là quá đủ để đá hết các payload của Hacktrick và PayloadAllTheThings ra chuồng gà
_self(đã bị cấm), _context, _charset,...
Với Twig template thì việc khai thác SSTI chủ yếu phụ thuộc vào cơ chế filter data của Twig cho phép biến đổi dữ liệu tùy theo mục đích sử dụng template. Dữ liệu đi qua filter thông qua dấu |
, ví dụ {{data|filter(args..)}}
. Một số filter thông dụng là slice, upper, lower, join. Nhưng bên cạnh đó có một số filter phổ biến bởi có thể lợi dụng nó để RCE, điểm chung của các filter này là chuyên dùng để xử lý mảng, và mình có thể truyền vào một hàm mũi tên cụ thể để tùy biến quá trình xử lý dữ liệu theo ý mình thích biểu diễn bằng hàm mũi tên. Lúc này mình có thể truyền vào hàm là một system function để thực thi các câu lệnh, còn data sẽ là command, từ đó ta mới có các payload SSTI trên HackTrick sẽ tự tựa nhe kiểu:
Ngoài ra có thể kể đến các filter như: map, sort, filter, reduce -> đều đã bị cấm hết. Tất nhiên, với đống blacklist kia thì mấy payload như này không hoạt động. Nên mình đã đi tìm cách để craft được payload như thế này, nhưng việc khai báo string với mảng là không thể vì chall cũng đã cấm dấu ngoặc vuông là 2 kiểu nháy. Điều này làm mình bế tắc trong 1 khoảng thời gian dài mà không tìm được cách nào, nó làm mình mông lung không xác định được trọng tâm cần phải làm những gì để RCE được challenge này.
Sau khi teammate tìm ra được cách để nối chuỗi là sử dụng dump() và ghép các ký tự trong đấy để thành chuỗi, mình mới xác định được 2 bước cần làm để solve challenge:
Với việc cấm tất cả dấu nháy, việc khai báo một string hay array cơ bản là không thể. Tuy nhiên ta có thể dụng dấu ~
để nối chuỗi các string với nhau hoặc các biến với nhau. Nên ý tưởng ban đầu là sử dump() -> var_dump ra context hiện tại, dùng slice để lấy từng ký tự trong chuỗi trả ra và nối chúng lại với nhau thành chuỗi.
Với cách này ta có thể tạo thành string, tuy nhiên bị giới hạn về mặt từ ngữ, và việc ghép từng ký tự theo số như này rất mệt, nhất là việc flag có nhiều ký tự như kia mà ta không có ký tự * thì nối đến chết
{}
để một tham số thành mảng, nơi giá trị của tham số là value của nó:dump()
_context
không chứa dấu / và nếu ta cũng không thể đưa dấu *
vào bên trong dump({})
vì như thế sẽ lỗi ngay:/
và dấu -
bằng cách tiếp tục sử dụng dump()
-
thì trong _charset
ta sẽ thấy có xuất hiện dấu đó nằm trong chuỗi UTF-8
. Ta lấy ký tự thứ 4, tương ứng với slice(3,1) vì vị trí string bắt đầu đếm từ 0 để lấy dấu -
ra:
Còn dấu /
thì ta có thể lấy ký tự xuống dòng (sẽ xuất hiện khi dump) sau đó thêm filter nl2br
để chuyển ký tự xuống dòng thành thẻ HTML <br/>
, tiếp tục sử dụng filter raw
để giữ nguyên thẻ không bị html encode, rồi slice lấy dấu /
bên trong thẻ br:
Ngoài cách sử dụng {}
, ta cũng có thể sử dụng filter split chuỗi đầu vào với ký tự không tồn tại trong chuỗi để chuyển chuỗi thành mảng, vì công dụng của split là chia chuỗi thành mảng các phần tử theo ký tự xác định
Công cụ đã có đủ cả, giờ thứ mình và đồng đội đã stuck rất lâu mới tìm ra, đó là filters phù hợp để có thể RCE.
Biết được phiên bản Twig đang sử dụng là 3.12, mình tìm kiếm nhưng kết quả no hope, cày hết cái doc các filters của Twig thì toàn cái không dùng được, những cái dùng được đều bị blacklist hết.
Bí ngòi cả 1 ngày thì teammate tìm ra filter find
có thể được sử dụng để RCE:
Về cách sử dụng thì y chang các filter đã bị blacklist, tự hỏi tại sao nó không ở trong doc thì có lẽ doc upadte chưa tới do filter này mới được thêm vào tại phiên bản 3.11:
Filter này nằm tại file /Twig/src/Extension/CoreExtension.php
, là một trong các filter có sẵn của Twig, đoạn code xử lý filter này sẽ như sau:
Từ đó, ta sẽ truyền vào $array giá trị là system hoặc passthru, và truyền vào nội dung mảng là câu lệnh cần thực hiện. Khi đó sẽ tương đương với việc thực thi PHP Code:
Để test thì tạm thời mình comment lại đoạn check blacklist để run cho tiện:
Ngoài filter find
ra, mình đi lượn github thì có người còn tìm được operator has some
cũng có thể được sử dụng để RCE, với nguyên lý cũng tương tự find
:
Đây là operator nên ta không cần sử dụng dấu |
để apply filter mà có thể viết thẳng như sau:
Cách này khá lỏd và có lẽ mình sẽ sử dụng trong một ctf challenge nào đó =)))
Tổng hợp lại để khai thác, đầu tiên mình craft payload ls /
để đọc flag trước đã, mình sử dụng passthru thay cho system vì đỡ phải nối chuỗi 3 phát=))
Mình có được tên flag là: flag-efbeeaddce
Giờ thì craft câu lệnh đọc flag thôi:
Trên server thì flag có tên: flag-edbfcbcaef