+3

KMA CTF 2023 LẦN 1 WRITE UP

Web challenges

Vào đây!

image.png

Phân tích

image.png

Vào chall ta nhận được full source code bằng nodejs, đọc sơ qua thì thấy web có 3 api

  • /login: Đăng nhập với 2 input là user và pass
  • register: Đăng ký với 3 input là user, pass và bio
  • /: Show source code

Test thử các api đó xem nó hoạt động sao:

/register:

image.png

/login:

Vì khi đăng ký password được hash md5 nhưng khi đăng nhập lại không hash md5 password mình nhập nên mình cần tự md5 password trước khi đưa vào request

image.png

Các tính năng chỉ có vậy.

Quay lại đọc kỹ hơn vào source code thì ta thấy có nodejs và mysql

image.png

Một hướng khai thác bypass login khi hệ thống sử dụng nodejs và mysql là đây

Ta thấy rằng:

  • Hệ thống lấy thẳng dữ liệu từ req.body để truyền vào câu truy vấn:

image.png

  • Khi truyền dữ liệu với Content-Type: application/x-www-form-urlencoded:

image.png

  • Khi truyền dữ liệu với Content-Type: application/json:

image.png

  • Trong nodejs khi bạn parse() lại json thì nó sẽ trả về đối tượng object trước. Do đó khi thay giá trị của chuỗi password thành một object thì object đó sau khi parse sẽ được truyền vào câu truy vấn và gây ra lỗi:

image.png

image.png

  • Sửa object rỗng trên thành một object có giá trị là cặp "key": "value":

image.png

  • Vẫn gây ra lỗi nhưng câu truy vấn của trúng ta lúc này là:

image.png

  • Wow keyvalue đã được đưa vào câu truy vấn và tạo thành một chuỗi so sánh.
  • key được kẹp trong cặp ký tự ` (`key`) mà ký tự ` (backtick) trong MySQL được sử dụng để bao quanh các đối tượng cơ sở dữ liệu như tên bảng, tên cột, tên trường và các từ khóa.
  • Giải thích dễ hiểu thì đoạn password = `key` = 'value' này có thể hiểu là so sánh cột password bằng cột key nếu bằng sẽ trả về 1 (true) không bằng sẽ trả về 0 (false) sau đó lấy 2 giá trị true và false tiếp tục so sánh với 'value'

Khai thác

  • Dựa trên ý tưởng payload như vậy ta gửi request:

image.png

  • Câu truy vấn sẽ trở thành:

image.png

  • Tại đây ta có cột password bằng cột `password` nên sẽ trả về true và bằng với giá trị true ở vế so sánh sau và thế là ta đã bypass được đoạn login trên.

Áp dụng payload tương tự vào challenge và thế là có được flag :v

image.png

More info:

  • Không chỉ riêng object, mysqljs còn bị lỗi tương tự khi ta truyền vào mảng: user[username]=1&pass[password]=1
  • Nếu không biết tên cột thì ta có thể sử dụng payload ngược lại, ví dụ:

image.png

Bởi vì lúc này cột password bằng cột `username` sẽ trả về false và bằng với false ở vế so sánh sau.

image.png

Jo`in Le'm

image.png

Phân tích

image.png

Vào challenge ta nhận được 1 đoạn code php với rất nhiều câu lệnh goto trông rất rối mắt @@

Sau một hồi ngồi đọc và làm "đẹp" (đối với mình) thì mình thu được:

image.png

Cái này trong lúc làm mình follow theo nên vẫn hiểu, giờ ngồi viết lại, đọc mà thấy rối ngang :v

Nói sơ qua thì:

  1. Đầu tiên đoạn code này thực hiện hàm show_source()
  2. Sau đó define hàm curl(), hàm này sẽ kiểm tra xem response có bị redirect đi đâu không, nếu có thì tiếp tục thực hiện curl đến đường dẫn được redirect đó và trả về content khi curl đến đường dẫn cuối.
  3. Để gọi được hàm curl() ở trên cần phải bypass qua rất nhiều điều kiện check, đầu tiên là check scheme của biến $url xem có phải là http hoặc https hay không? Nên việc dùng file://<file_name> hoặc tương đương đều không được.
  4. Qua check scheme chương trình tiếp tục check đến host của $url xem có phải host hoặc gethostbyname() ra ip là 127.0.0.1 không? Nên việc dùng http://127.0.0.1/<file_name>https://localhost/<file_name> hoặc tương đương đều không được.
  5. Qua check host chương trình tiếp tục check xem urlencode($url)curl_escape(..., $url) có ra kết quả giống nhau không nếu giống thì sẽ die chương trình luôn.
  6. Khi đã vượt qua tất cả những điều kiện trên thì hàm curl($url) với $url do chúng ta control mới được thực hiện. Và nó sẽ được in ra ngay trên màn hình.

Đầu tiên mình muốn biết hàm curl() kia khi được gọi nó sẽ trả về như thế nào nên mình đã thuận theo những điều kiện check như là scheme phải là httpshost không được là localhost nên mình chọn $url=https://example.com. Nhưng để call được hàm curl() thì vẫn cần phải pass qua 1 điều kiện so sánh về encode nữa. Sau một hồi search thì mình có tìm ra ký tự đó là + (%20) và tiến hành curl thử:

image.png

Dĩ nhiên không được vì không có url nào là https://example.com%20 cả nên mình thêm ?a=+ và urlencode nó để trong biến $url vẫn có ký tự + nhưng url chỉ có https://example.com:

image.png

Nhưng vẫn không được vì khi ta sử dụng dấu + thì sẽ làm hỏng gói tin http request dẫn đến không nhận về được gì. Mình tiếp tục search thì tìm ra thêm 1 ký tự nữa đó chính là ký tự ~. Thực hiện curl với ký tự này:

image.png

Vậy là đã có thể thực hiên curl được!

Vì chưa biết flag nằm ở đâu nhưng tâm link mách bảo mình rằng nó nằm trong local nên mình tìm cách bypass qua những điều kiện check schemehost để vô được local thì hàm curl() được define đã giúp mình làm việc đó khi nó cho phép thực hiện curl đến url redirect. Vậy nên ý tưởng là mình sẽ web host 1 website php với tính năng là khi được GET thì nó sẽ redirect đến đường dẫn file://<file_name>file://<file_name> sẽ được curl() rồi trả về content tại màn hình.

Khai thác

  1. Thực hiện tạo file a.php:

    <?php
    header("Location: file:///etc/passwd");
    ?>
    
  2. Host server php bằng command: $ php -S 127.0.0.1:9000

  3. Ngrok để public file a.php ra ngoài internet bằng command: $ ngrok http 9000

  4. Curl đến public site:

image.png

Trong lúc diễn ra giải thì mình bế stuck từ đây khi không tìm được gì :<

Sau khi kết thúc giải được hint từ tác giả và những người solved mình mới có thể đi tiếp:

image.png

  1. Sửa header Location thành file:///proc/mounts và thực hiện curl:

image.png

  1. Sửa header Location thành file:///home/siuvip_saoanhbatduocem/etc/passwd và thực hiện curl:

image.png

Flag Holder

image.png

Phân tích

image.png

Vào challenge ta nhận được một form submit khi click Submmit thì ta sẽ được kết quả

image.png

Ngoài ra còn 1 path được ẩn đi đóa là path /source ta có thể tìm thấy nó qua scan path hoặc Ctrl + U

image.png

View source:

image.png

Đoạn code trên là một ứng dụng web Flask gồm 3 route:

  • / Show form submit
  • /render Hiển thị nội dung được tạo bởi form trên
  • /source Show source

image.png

Để có được flag thì ta phải thêm được chuỗi {FLAG} vào trong biến template.

Mới nhìn sơ qua thì rất dễ để đoán bài này dính lỗi SSTI nhưng sau khi thử bypass qua blacklist mãi không được cộng thêm với việc có hint

image.png

image.png

và cả của người đã solved vì mình làm bài này sau khi giải đã kết thúc :<

Mình bắt đầu đi search và tìm được bài này

Hiểu đơn giải thì ký tự İ (chữ I in hoa của Thổ Nhĩ Kỳ) sau khi đi qua hàm lower() của python sẽ được tác ra là hai ký tự là U+0049 Chữ La tinh in hoa (I) và U+0307 là dấu chấm ở bên trên

Vậy nên sau khi lower() ký tự İ thì len của nó sẽ tăng lên gấp đôi.

Khai thác

Hàm lower() được gọi khi hàm waf() được gọi để check blacklist và chỉ lấy 20 ký tự đầu để kiểm tra. Nên dựa theo phân tích bên trên ta chỉ cần input chuỗi template có 10 ký tự đầu là "İİİİİİİİİİ" còn 10 ký tự sau có thể nhập tùy ý vì sau khi lower() ta có len("İİİİİİİİİİ".lower()) = 20 vào trong 20 ký tự này thì không có trong blacklist.

Vậy nên ta có payload cuối cùng là ?template=İİİİİİİİİİ{FLAG}&variable=a:

image.png

Updating


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí