Xin chào các bạn, hôm nay mình sẽ nói về một bài lab trên Web Security Academy. Bài này được họ đánh giá ở mức độ Expert nên khá là dài nhưng không khó lắm đâu nhé! Cá bạn có thể thực hành ngay tại đây.
Cùng bắt đầu ngay nhé
Đầu tiên hãy bật Burp Suite lên, kết nối vào bài lab và đăng nhập vào tài khoản được cấp. Khi bạn dùng Burp để xem request đến /my-account các bạn sẽ thấy cookie của mình giống như hình dưới đây:
Bây giờ thử decode nó bằng tab decoder trong Burp nhé
Đọc tiêu đề bài lab thì chúng ta có thể biết nó là Java deserialization nhưng nếu gặp thực tế thì chúng ta nhận biết thế nào? Hãy nhìn vào chỗ mình tô màu vàng trong decode nhé. Đoạn data.session.token.AccessTokenUser
như kiểu đường dẫn của một class vậy, còn chữ java/lang
kia nữa. Đó được coi là một trong những dấu hiệu giúp chúng ta nhận ra nhé.
Discover content
Tiếp theo, sau khi discover content trang web mình thu được kết quả như ảnh dưới đây.
Chúng ta có thể nhìn thấy trang web để lộ hai file java. Hãy truy cập đến hai file đó ngay thôi nào..
Ở file AccessTokenUser.java
ta thấy class được implements Serializable
. Và trùng hợp là cái tên class này có trong cái cookie mà chúng ta đã decode lúc nãy. Qua đây chúng ta có thể đoán được là khi server nhận cookie sẽ thực hiện deserializable để nhận được đối tượng.Rồi cứ để đó đã, chúng ta xem tiếp file còn lại có gì nào. À quên, các bạn có thể đọc về Serializable và deserializable tại đây nhé
Như các bạn thấy, class này cũng được implements từ Serializable, nghĩa là cũng có thể biến đổi từ object sang dạng byte stream. Nhưng class này chỉ biến đổi thuộc tính id
vì Product đã được thêm transient
. Để ý tiếp xuống dưới thì ta thấy hàm readObject
có thực hiện một câu sql với đối số truyền vào là id
.
Qua hai class thì chúng ta hãy thử tưởng tượng xem sẽ như thế nào nếu cái cookie không phải là đối tượng thuộc class 1 mà lại là đối tượng thuộc class 2 nhỉ. Liệu có thể có SQLi…
Developing a custom gadget chain
Rồi, bây giờ chúng ta thử tạo class giống class 2 sau đó thực hiện biến đổi đối tượng sang dạng byte stream rồi encode dưới dạng Base64 để tạo ra một cookie giả nhé. Do sa chân lỡ bước mình đã không may va vào code của họ :))
Các bạn cần chú ý hàm Main dưới đây nhé.
|
|
và class TemplateProduct
có dạng
SQLi
Đầu tiên, mình thêm một dấu nháy đơn vào chỗ payload và chạy code sau đó sửa cookie gốc bằng kết quả code vừa chạy.
Và đây là kết quả
Oke thế là bài toán bây giờ chính thức chuyển thành SQLi.
Mình đã thử và có vẻ chèn bất kì câu sql nào vào đều có thể chạy được. Vậy là dễ rồi, hãy xem mình làm tiếp thế nào nhé…
Xác định số cột
Mình dùng UNION
vì vậy bước đầu tiên chính là xác định xem câu truy vấn gốc lấy ra bao nhiêu cột. Để xác định được thì các bạn cứ , null
tiếp vào payload dưới đây của mình cho đến khi đúng thì thôi. Khi đấy số lượng từ null chính là số cột của câu truy vấn gốc.
' UNION SELECT null, null, null
Xác định kiểu dữ liệu từng cột
Để xác định kiểu dữ liệu của từng cột, chúng ta lại thay null
bằng một ví dụ của kiểu dữ liệu đó. Vd nếu muốn biết có phải integer không thì các bạn thay null = 1
hoặc string thì thay null = 'a'
.Nếu không có lỗi gì xảy ra thì đúng là cái kiểu dữ liệu bạn đã thử hoặc có trường hợp có lỗi nó cũng báo luôn kiểu dữ liệu của cột đó như thế này
Tìm tên cột tên bảng
Trong sql thường có một bảng tổng tên là information_schema.tables
có cột table_name
lưu tên tất cả các bảng trong cơ sở dữ liệu. Các bạn có thể tìm kiểm information schema + tên hệ quả trị CSDL để biết thêm. Còn đây là payload tiếp theo của mình
' UNION SELECT null, null, null, table_name, null, null, null, null FROM information_schema.tables--
Tuy nhiên nó bị hiện lỗi như sau
Nghĩa là ở câu truy vấn gốc kiểu dữ liệu là integer nhưng câu sau UNION lại là character. Tiếp theo mình đã dùng hàm CAST()
để ép kiểu từ character thành integer.
' UNION SELECT null, null, null, CAST(table_name as integer), null, null, null, null FROM information_schema.tables--
Sở dĩ dùng hàm CAST()
vì chắc chắn nó sẽ bị lỗi nếu đầu ra của table_name
chứa một kí tự không phải số. Và một điều nữa là khi có lỗi thì nó sẽ hiện cái lỗi đấy ra và chúng ta sẽ đọc được như thế này.
Rồi sau khi có tên bảng ta tiếp tục tìm tên cột chứa password bằng payload sau
' UNION SELECT null, null, null, CAST(column_name as integer), null, null, null, null FROM information_schema.columns WHERE table_name = 'users' --
Tuy nhiên nó không hiện ngay cái bảng chúng ta cần tìm, chúng ta lại tiếp tục sửa payload
' UNION SELECT null, null, null, CAST(column_name as integer), null, null, null, null FROM information_schema.columns WHERE table_name = 'users' AND column_name != 'username'--
Tìm thông tin tiếp
Cuối cùng là tìm đến mật khẩu của Administrator thôi
Và đây là kết quả
Tổng kết
Như vậy là chúng ta đã đi qua một bài lab dài thật là dài. Hãy cùng xem lại những kiến thức vừa đi qua nhé
- Discover content.
- Serializable >< Deserializable.
- SQL Injection, cách tìm ra lần lượt các thành phần trong cơ sở dữ liệu.