php框架入门
因为是安全相关,主要是讲解一下:
- 一个框架中有那些重要的文件和文件夹
- 运维的粗心会导致哪些问题
- 框架的类加载机制
前言
这是我于2020.11.8的例会分享内容,如有错误还请指正!
先从文件和文件夹讲起
public/web目录
我们先康康不同框架中,public/web文件夹里面有什么东西。
可以看道三个文件夹共有的特征便是——都拥有index.php
文件。文件内容都大致如以下伪代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <?php define("...", "..."); //... // 定义了一些常量,例如yii是定义其debug是否开启等...
require __DIR__.'/../vendor/autoload.php'; // 导入autoload.php来自动加载类,这个之后在讲
$config = require '../config/config.php'; require ...; // 导入一些其它文件,比如config,引导文件等...
(new app($config))->run(); ================= $http = (new App())->http; $response = $http->run(); $response->send(); $http->end($response); ================= ... // 启动框架
|
可见index.php
便是整个框架的入口文件了。那么public/web文件夹应该是被作为网站根目录来使用的。
对于渗透人员来说,如果运维人员配置错误,会发生什么?例如以下场景。
Q: 有时候我们会发现,一个网站的主页并不是http://vps.com/,而是http://vps.com/public/,甚至是http://vps.ip/app/public/时,这说明了什么?
A: 很明显,懒狗并没有按照框架的要求将public/web目录设置为网站主目录。这里分开讨论:
- 对于http://vps.com/public/的情况,运维应该是将框架目录设为了网站主目录,这种情况下,我们就可以根据不同的文件夹去读取在public/web下读不到的东西。
- 对于http://vps.ip/app1/public/的情况,那就是大奖了。很有可能是以下情况:
1 2 3 4 5
| var--www--app1 -app2 -app3 -... -index.php
|
在这个服务器上存在多个网站,但运维懒狗到懒到不想为每个目录独立配置服务器,设置了更上一层的目录为网站根目录,还“贴心”设置了index.php
引导用户跳转到不同文件夹下的不同目录。对于这种设置,我们就可以读到更多原来读不到的文件。
对于其它的public/web下的其它文件,就说一下yii2下的web/assets
文件夹吧。根据官网文档,这个文件夹的作用是如下:
如果资源包放在 Web 不能访问的目录, 当视图注册资源时资源会被拷贝到一个 Web 可访问的目录中, 这个过程称为资源发布。资源包即为那些css/js
文件。php将会在assets创建一个资源包文件的链接。
这个功能是默认开启的,但当assets
没有www-data的写权限时,yii
会爆出无法写入文件的错误,解决途径有两个,一是关闭资源发布,二是给其写权限。如果是没有经验的运维,会老老实实根据yii
爆出的错误,给予该文件夹写权限。那么如果有任意目录的文件上传,你知道该上传到哪里了吧?
vendor目录
这个目录里装载了一个框架所需的依赖包,如果开发需要一些包作为辅助时,也会安装到这个文件夹。
这里不得不谈的是php的类加载机制。
autoload.php文件
在入口文件中,我们看到,其导入了vendor/autoload.php
文件,而其又导入了composer/autoload_real.php
文件。我们来看看autoload_real.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 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
|
class ComposerAutoloaderInite66911f1406e2749477b48d048bd8aca { private static $loader;
public static function loadClassLoader($class) { if ('Composer\Autoload\ClassLoader' === $class) { require __DIR__ . '/ClassLoader.php'; } }
public static function getLoader() { if (null !== self::$loader) { return self::$loader; }
require __DIR__ . '/platform_check.php';
spl_autoload_register(array('ComposerAutoloaderInite66911f1406e2749477b48d048bd8aca', 'loadClassLoader'), true, true); self::$loader = $loader = new \Composer\Autoload\ClassLoader(); spl_autoload_unregister(array('ComposerAutoloaderInite66911f1406e2749477b48d048bd8aca', 'loadClassLoader'));
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); if ($useStaticLoader) { require __DIR__ . '/autoload_static.php'; call_user_func( \Composer\Autoload\ComposerStaticInite66911f1406e2749477b48d048bd8aca::getInitializer($loader) ); } else { $map = require __DIR__ . '/autoload_namespaces.php'; foreach ($map as $namespace => $path) { $loader->set($namespace, $path); }
$map = require __DIR__ . '/autoload_psr4.php'; foreach ($map as $namespace => $path) { $loader->setPsr4($namespace, $path); }
$classMap = require __DIR__ . '/autoload_classmap.php'; if ($classMap) { $loader->addClassMap($classMap); } }
$loader->register(true);
if ($useStaticLoader) { $includeFiles = Composer\Autoload\ComposerStaticInite66911f1406e2749477b48d048bd8aca::$files; } else { $includeFiles = require __DIR__ . '/autoload_files.php'; } foreach ($includeFiles as $fileIdentifier => $file) { composerRequiree66911f1406e2749477b48d048bd8aca($fileIdentifier, $file); }
return $loader; } }
function composerRequiree66911f1406e2749477b48d048bd8aca($fileIdentifier, $file) { if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { require $file;
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; } }
|
可以看到,php的自动类加载遵循了psr-4规范。
对于java来说,当一个文件存在多个类时,会自动将多出来的类放入新的class文件内,而不是两个类共存同一个文件。而php的标准对于这个就无能为力了。
在WMCTF2020中有一道webweb的反序列化链便存在这个问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <?php namespace CLI;
class WS { }
class Agent { public function __destruct() { } }
|
这道题使用fatfree
框架,我们的起点__destruct
在CLI\Agent
下,但此类和CLI\WS
共存在ws.php
上,根据上面的加载规则,Agent
类是加载不到的。
这是这个题的一个坑点,如果我们想要加载这个类,就要提前去导入ws.php
。做法也简单,在整个payload
外层包裹一层CLI\WS
即可。这样,类加载器会根据最外层的CLI\WS
一类,首先导入ws.php
,然后解析器会同时解析CLI\WS
和CLI\Agent
,再导入CLI\Agent
类时,就成功了。
其它
还有一些零散的文件可以注意一下,这里简单进行一个概况
如果出现了在上面public文件夹出现的问题可以注意一下:
- .env 系统环境变量,可能会有数据库地址和密码,以及类似于laravel中的密钥
- README.md 获得框架名称、版本,以便对框架进行代码审计
- composer.lock 获得当前框架所有依赖名
如果当前目录下有assets
文件夹,大概率是yii2
框架
最后
感谢懒狗运维\开发给我们饭恰(bushi)