2

php框架入门

 2 years ago
source link: https://afkl-cuit.github.io/2020/11/15/php%E6%A1%86%E6%9E%B6%E5%85%A5%E9%97%A8/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
a year ago12 minutes read (About 1833 words)  58 visits

php框架入门

php框架入门

因为是安全相关,主要是讲解一下:

  1. 一个框架中有那些重要的文件和文件夹
  2. 运维的粗心会导致哪些问题
  3. 框架的类加载机制

这是我于2020.11.8的例会分享内容,如有错误还请指正!

先从文件和文件夹讲起

public/web目录

我们先康康不同框架中,public/web文件夹里面有什么东西。

可以看道三个文件夹共有的特征便是——都拥有index.php文件。文件内容都大致如以下伪代码。

<?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目录设置为网站主目录。这里分开讨论:

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

// autoload_real.php @generated by Composer

class ComposerAutoloaderInite66911f1406e2749477b48d048bd8aca
{
// 加载器
private static $loader;

// 加载加载器文件
public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}

// 获取加载器
/**
* @return \Composer\Autoload\ClassLoader
*/
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);
}
}

// 对加载器进行注册
// 其内部的大致流程是: classMap->psr-4->psr-0
$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的反序列化链便存在这个问题。

// ws.php // Agent.php
<?php
namespace CLI;

class WS {
//...
}

class Agent {
//...
public function __destruct() {
// 危险代码
}
}

这道题使用fatfree框架,我们的起点__destructCLI\Agent下,但此类和CLI\WS共存在ws.php上,根据上面的加载规则,Agent类是加载不到的。

这是这个题的一个坑点,如果我们想要加载这个类,就要提前去导入ws.php。做法也简单,在整个payload外层包裹一层CLI\WS即可。这样,类加载器会根据最外层的CLI\WS一类,首先导入ws.php,然后解析器会同时解析CLI\WSCLI\Agent,再导入CLI\Agent类时,就成功了。

还有一些零散的文件可以注意一下,这里简单进行一个概况

如果出现了在上面public文件夹出现的问题可以注意一下:

  1. .env 系统环境变量,可能会有数据库地址和密码,以及类似于laravel中的密钥
  2. README.md 获得框架名称、版本,以便对框架进行代码审计
  3. composer.lock 获得当前框架所有依赖名

如果当前目录下有assets文件夹,大概率是yii2框架

感谢懒狗运维\开发给我们饭恰(bushi)


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK