1.phpdest
文件包含漏洞
- require_once:包含指定文件一次,若出现异常则停止脚本进行
require_once()第一次包含flag.php后,如果再次包含此文件的话不会执行。
很明显flag就是在flag.php里,考虑绕过require_once()函数。
此处的绕过涉及require_once()拒绝重复匹配的底层原理:
- 当首次包含某个文件时,会在相应的文件哈希表上进行"注册",当重复包含时会产生hash值的匹配从而拒绝包含。当软连接使用 /proc/self/root 的层数足够多时,可使得hash重新匹配,从而造成重复包含,进而绕过once的限制。
进行base64解码,得到flag
2.easyphp
代码审计之后可以发现if代码块根本不会执行。
考虑利用set_error_handler()函数,注意到此处有字符串拼接
于是考虑传入数组触发自定义报错函数从而获取flag
3.easyrce
代码审计,远程代码执行RCE漏洞,这题几乎把所有的命令执行函数给ban了
看了下wp,new trick:
hex2bin($string):将十六进制的字符串转化为ASCII字符串,
对system('cat /flag')进行bin2hex,然后在post时使用hex2bin
注意:没有ban的字符就使用原字符,否则无法爆出flag
post传参获取flag
4.funny_upload
文件上传漏洞配合一句话木马进行连接
直接上传一句话木马和图片木马,发现均存在过滤
尝试之后发现.htaccess文件可以上传,考虑使用htaccess的相关配置实现绕过
5.EasySSTI
发现有回显,结合题目的名字进行注入测试,使用{{..}}可进行SSTI注入攻击
绕过blacklist,获取flag
6.middle
import os
import config
from flask import Flask, request, session, render_template, url_for,redirect,make_response
import pickle
import io
import sys
import base64
app = Flask(__name__)
class RestrictedUnpickler(pickle.Unpickler):
def find_class(self, module, name):
if module in ['config'] and "__" not in name:
return getattr(sys.modules[module], name) # 返回module模块中的name属性的值,可以构造出backdoor函数的调用
raise pickle.UnpicklingError("global '%s.%s' is forbidden" % (module, name))
def restricted_loads(s):
return RestrictedUnpickler(io.BytesIO(s)).load() # opcode转python语句
@app.route('/')
def show():
base_dir = os.path.dirname(__file__)
resp = make_response(open(os.path.join(base_dir, __file__)).read()+open(os.path.join(base_dir, "config/__init__.py")).read())
resp.headers["Content-type"] = "text/plain;charset=UTF-8"
return resp
@app.route('/home', methods=['POST', 'GET'])
def home():
data=request.form['data']
User = restricted_loads(base64.b64decode(data))
return str(User)
if __name__ == '__main__':
app.run(host='0.0.0.0', debug=True, port=5000)
import os
def backdoor(cmd):
# 这里我也改了一下
if isinstance(cmd,list) : # 传入list
s=''.join(cmd)
print("!!!!!!!!!!")
s=eval(s)
return s
else:
print("??????")
Flask框架、pickle反序列化前置知识去找找文档学习下!
代码审计,是python的pickle中的反序列化漏洞。
利用思路:
-
可利用的任意执行函数为backdoor中的eval()
-
发现在/home路径下可提交data数据
-
data数据经由restricted_loads()函数进行load()反序列化
-
RestrictedUnpickler中进行了过滤,必须包含'config'字段和不能使用'__',其中的return getattr(),可以配合backdoor()函数进行利用
构造如下exp.py
并生成python opcode(“序列化”),同时注意要进行一次base64编码(查看源代码不难分析):
url中使用/home路径,传参获取flag
7.ezip
下载图片,扔进FlexHex进行解析,发现一串base64字符串
解码获得upload.php和zip的类声明代码:
upload.php:
<?php
error_reporting(0);
include("zip.php");
if(isset($_FILES['file']['name'])){
// 过滤文件名和拓展名
if(strstr($_FILES['file']['name'],"..")||strstr($_FILES['file']['name'],"/")){
echo "hacker!!";
exit;
}
if(pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION)!="zip"){
echo "only zip!!";
exit;
}
$Myzip = new zip($_FILES['file']['name']);
// 创建目录
mkdir($Myzip->path);
// 文件存放位置的移动
move_uploaded_file($_FILES['file']['tmp_name'], './'.$Myzip->path.'/' . $_FILES['file']['name']);
echo "Try to unzip your zip to /".$Myzip->path."<br>";
// 调用解压函数
if($Myzip->unzip()){echo "Success";}else{echo "failed";}
}
?>
zip.php:
<?php
class zip
{
public $zip_name;
public $path;
public $zip_manager;
public function __construct($zip_name){
$this->zip_manager = new ZipArchive();
$this->path = $this->gen_path();
$this->zip_name = $zip_name;
}
public function gen_path(){
$chars="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
$newchars=str_split($chars); // 将字符串转化成数组
shuffle($newchars); // 打乱数组中的元素位置
$chars_key=array_rand($newchars,15);
$fnstr = "";
for($i=0;$i<15;$i++){
$fnstr.=$newchars[$chars_key[$i]];
}
return md5($fnstr.time().microtime()*100000);
}
public function deldir($dir) {
//先删除目录下的文件:
$dh = opendir($dir);
while ($file = readdir($dh)) {
if($file != "." && $file!="..") {
$fullpath = $dir."/".$file;
if(!is_dir($fullpath)) {
unlink($fullpath);
} else {
$this->deldir($fullpath);
}
}
}
closedir($dh);
}
function dir_list($directory) // 字段转化成数组
{
$array = [];
$dir = dir($directory);
while ($file = $dir->read()) {
if ($file !== '.' && $file !== '..') {
$array[] = $file;
}
}
return $array;
}
public function unzip() // 解压
{
$fullpath = "/var/www/html/".$this->path."/".$this->zip_name;
$white_list = ['jpg','png','gif','bmp']; // 列出白名单
$this->zip_manager->open($fullpath); // 打开zip文件
for ($i = 0;$i < $this->zip_manager->count();$i ++) {
if (strstr($this->zip_manager->getNameIndex($i),"../")){ // 索引中过滤../
echo "you bad bad";
return false;
}
}
if(!$this->zip_manager->extractTo($this->path)){ // extractTo() 将文件解压到指定文件
echo "Unzip to /".$this->path."/ failed";
exit;
}
// 触发extractTo的失败 exit避免文件删除
// unlink()删除文件
@unlink($fullpath);
//
$file_list = $this->dir_list("/var/www/html/".$this->path."/");
for($i=0;$i<sizeof($file_list);$i++){
// is_dir() 判断给定文件名是否为一个目录
if(is_dir($this->path."/".$file_list[$i])){
echo "dir? I deleted all things in it"."<br>";@$this->deldir("/var/www/html/".$this->path."/".$file_list[$i]);@rmdir("/var/www/html/".$this->path."/".$file_list[$i]);
}
else{
if(!in_array(pathinfo($file_list[$i], PATHINFO_EXTENSION),$white_list)) {echo "only image!!! I deleted it for you"."<br>";@unlink("/var/www/html/".$this->path."/".$file_list[$i]);}
}
}
return true;
}
}
发现在zip类中的unzip函数中存在
rmdir 删除目录
unlink 删除文件
发现在此之前有exit(),考虑利用exit进行绕过
这里涉及php的ZipArchive类中的函数:extractTo()函数
extractTo(string $path,mixed $entries):bool
功能:传入绝对路径,将经ZipArchive类实体open的zip文件进行压缩到绝对路径位置 $entries参数指明了要解压zip文件中的哪些文件
利用:当在zip文件中设置两个file_name一致的文件时,会造成解析错误,解压继续执行但是返回布尔值false
本题只要在zip文件中部署一句话木马hack.php及其相同的文件名,即可绕过rmdir和unlink
最后中国蚁剑连接,查询/flag目录即可