2021陇剑杯 线下记录
很久没有发博客了,之前写都是存笔记,但这次觉得有必要发一下,题目也分享一下,自我感受收获挺大的一次比赛。
第一次线下做修复的题目,赛前一直以为陇剑线下只有rhg赛道的pwn和一些流量分析题目,结果还出了10道web题。
希望有师傅能交流一下题目修复的一些经验,学习修复姿势,后期还会整理数据分析的题目。
题目合集在EDI安全公众号回复 2021陇剑杯 即可获得
Pwn
四道题都是32位的静态编译,去掉了符号表,都是先通过f12定位到程序的函数
Rgh1
F12看字符串,找到一个/bin/sh,x查看交叉引用,定位到引用函数,修改引用的地址就patch了
Rgh2
有个/bin/sh,一样改掉了
这一句置零挺危险的,就nop掉了
Rhg3
将50压栈,应该是后面那个有读入功能的函数的参数,这里修改成2
这些看着挺危险的地方都nop掉
顺便把bss上的这个binsh置零了
Rhg4
这个函数应该是free,看着free了两次,就nop掉了一次,直接就patch了
Web xxe
loadXML($userinput, LIBXML_NOENT | LIBXML_DTDLOAD);
$importedxml = simplexml_import_dom($model);
$firstname = $importedxml->firstname;
$phonenumber = $importedxml->phonenumber;
$emailadd = $importedxml->emailadd;echo "That is weird, $emailadd is not valid. Please try again!";?>
设置为true,禁止使用外部实体就可以了
libxml_disable_entity_loader (true);
源码比较少,基于黑名单的 一个图片上传功能
?php
include('config.php');set_error_handler($config['set_error_handler']);
set_exception_handler($config['set_error_handler']);
// register_shutdown_function($config['set_error_handler']);if(!empty($_FILES)){uploadFile();
}else if(isset($_GET['upload'])){show('upload.html');
}else{show('index.html');
}function uploadFile(){global $config;try{// print_r($_FILES);$uploaddir=isset($config['uploadDir'])?$config['uploadDir']:'static/img/gallery/';$filename=basename($_FILES['files']['name']['0']);$ext=pathinfo($filename, PATHINFO_EXTENSION);foreach($config['blackList'] as $b){if($ext===$b){echo "请上传图片!";return ;}}list($width, $height) = getimagesize($_FILES['files']['tmp_name'][0]);$image_p = imagecreatetruecolor($width, $height);$image = imagecreatefrompng($_FILES['files']['tmp_name'][0]);imagecopyresampled($image_p, $image, 0, 0, 0, 0, $width, $height, $width, $width);imagepng($image_p, $uploaddir.$filename);echo "上传成功,审核通过后将会展示您的图片!";return ;}catch (Throwable $t){echo "上传失败!";return;}
}function show($html){global $config;echo file_get_contents($config['htmlPath'].$html);
}function noError(){echo "上传失败!";exit();
}
上通防,直接检测上传文件内容,发现直接 die() 退出会显示 服务异常 ,所以选择输出之后再退出
$data = file_get_contents($_FILES['files']['tmp_name'][0]); if(preg_match("/system|define|flag|exec|base64|eval|php|AddType|Files/i",$post)) { echo "上传成功,审核通过后将会展示您的图片!"; exit(); }
赛后别的师傅说 把黑名单改成白名单 ,也能防御成功
ssrf
一个简单的ssrf,过滤常用协议就好了,但是题目上线半个多小时内,主办方的check就一直不通过,导致我一度怀疑人生
Fetch a URL Fetch a URL
这个输入框的功能可以指向某个链接,他会造成什么漏洞呢? example http://www.baidu.com (note: this PHP script is still in the works).
之后主办方提示说放松代码段的检测,将 file dict 协议过滤了,就成功防御了。
from flask import Flask,request,render_template,request
from jinja2 import Templateapp = Flask(__name__)
app.debug = True@app.route("/")
def root():return render_template('index.html')@app.route("/index")
def index():def san(v):#suggestioin: another way to execute `cat /flag`, not here.blacklist = ['\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', '\x08', 'flag', 'fl', 'ag','lag', '\t', '\n', '\x0b', '\x0c', '\r', '\x0e', '\x0f', '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f', '!', '"', '#', '$', '%', '&', '*',':', ';', '<', '>', '?', '@', '[', '\\', ']', '^', '`', '|', '~', '\x7f']for black in blacklist:if black.lower() in v.lower():return Falsereturn Truename = request.args.get('name')if name!=None and san(name):content = Template('''hello, %s ! You are going through the second wormhole!!!'''% (name)).render()return render_template('wormhole.html',content=content)elif name !=None:return render_template('wormhole.html',content='Bad bugs')else:return render_template('wormhole.html',content='Hello, Guest.')if __name__ == "__main__":app.run(port=9000)
是一个SSTI漏洞,我把之前遇到的题目过滤全都加进去了,结果还是EXP 利用成功,应该漏掉了估计应该是过滤的点不对
blacklist = ['%','-',':','+','class','base','mro','_','config','args','init','global','.','\'','req','|','attr','get']
blacklist += ['os', '+', '[', ']', "'", '{}', 'add', 'chr', 'ord', 'mro', 'get', 'form', 'class', 'base', 'request', 'session', 'cookies', 'headers', 'redirect', 'url_for', 'config', 'builtins', 'subclasses','get_flashed_messages', 'doc' , 'join' , 'str' , '~']
blacklist += ['\\u','\\x','set']
然后将危险渲染函数去掉,本地修改源码后,不再对{{ }}解析了,代码应该是可行的。
content = Template('''hello, %s ! You are going through the second wormhole!!!'''% (name)).render()
替换为
content = '''hello, %s !You are going through the second wormhole!!!'''%name
但是后,一直显示服务异常,应该是主办方检测了代码段,不能修改渲染函数。
from flask import Flask, render_template_string, request
import requests
from urllib.parse import unquoteapp = Flask(__name__)@app.route('/')
def index():return 'welcome'@app.route('/404/')
def notfound(content):template = ''' the route {{path}} not found
'''return render_template_string(template,path=content)@app.route('/url')
def url():try:url = request.args.get('url')if 'file' in unquote(url) :return 'forbidden' res = requests.get(url).textexcept:return 'nop'return resif __name__ == '__main__':app.run(host='0.0.0.0',port=5000)
比赛的时候没想到,觉得请求file协议不通过了应该没有利用的地方了,后来才知道协议大小写不敏感,需要将 file 大小写都过滤
应用了TP6框架写的总共就两个控制器
.php
getDirFile($path);} elseif(empty($subdirectory)) {$path = 'uploads/';$this->getDirFile($path);}}}public function delete(){$datas = json_decode(file_get_contents('php://input'), true);if(isset($datas)) {$subdirectory = $datas['subdirectory'];if(!empty($subdirectory) && !preg_match('/\.\/|\.\.\/|\\\/', $subdirectory)) {$path = 'uploads/'.$subdirectory;$this->deleteDirFile($path);} elseif(empty($subdirectory)) {$path = 'uploads/';$this->deleteDirFile($path);}}}public function deleteDirFile($path) {if(is_dir($path)){if($handle = opendir($path)){while($file = readdir($handle)){if(is_dir($path.'/'.$file.'/') && $file!='.' && $file!='..'){system('rm -rf '.$path.'/'.$file);} else {if($file!='.' && $file!='..'){system('rm -f '.$path.'/'.$file);echo $path.'/'.$file;}}}closedir($handle);}}}public function getDirFile($path){if($file_handler=opendir($path)){while(false !== ($file=readdir($file_handler))){if($file!="." && $file!=".."){if(is_dir("$path/$file")){if(substr_count("$path/$file","/")>1){$count=str_repeat("    ",substr_count("$path/$file","/"));echo $count.$file;}else{echo $file;}echo "
";$this->getDirFile("$path/$file");}else{if(substr_count("$path/$file","/")>1){$count=str_repeat("    ",substr_count("$path/$file","/"));echo $count.$file;}else{echo $file;}echo "
";}}}}}
}
.php
isPost()){//接收文件上传类型$type = request()->param('type','','trim');$name = request()->param('name','','trim');//获取表单上传文件$file = request()->file('file');//组装文件保存目录$upload_dir = '/'.$type.'/'.$name;try {//从config/upload.php配置文件中读取允许上传的文件后缀和大小$suffix_config = config('upload.suffix_arr');$size_config = config('upload.size_arr');if (empty($size_config[$type]) || empty($size_config[$type])){return false;}else{$suffix = $suffix_config[$type];$size = $size_config[$type];}//验证器验证上传的文件validate(['file'=>[//限制文件大小'fileSize' => $size * 1024 * 1024,//限制文件后缀'fileExt' => $suffix]],['file.fileSize' => '上传的文件大小不能超过'.$size.'M','file.fileExt' => '请上传后缀为:'.$suffix.'的文件'])->check(['file'=>$file]);//上传文件到本地服务器$filename = \think\facade\Filesystem::disk('public')->putFile($upload_dir, $file);if ($filename){$src = '/uploads/'.$filename;return json(['code'=>1,'result'=>$src]);}else{return json(['code'=>0,'msg'=>'上传失败']);}}catch (ValidateException $e){return json(['code'=>0,'msg'=>$e->getMessage()]);}}else{return json(['code'=>0,'msg'=>'非法请求']);}}
}
第一感觉 就是利用 phar:// 伪协议 结合反序列链rce
而题目还有 扫描目录还会调用函数 删除文件 的功能
直接上了通防没防住,Exp仍然利用成功,希望有修好的师傅能说一下
public function check(){if(preg_match("/system|flag|exec|base64|phar|__HALT_COMPILER|gzip|compress|jpg|jpeg|png|gif/i",$_SERVER['REQUEST_URI'])){die('hack');}$post = "";foreach($_POST as $key => $value){$post .= $key;$post .= $value;}if(preg_match("/system|flag|exec|base64|phar|__HALT_COMPILER|gzip|compress|jpg|jpeg|png|gif/i",$post)){die("hack");}}调用
$this->check();
eztom
菜鸡看了一下,连存在漏洞点jsp都没找到,就放弃修补了
是一个服务,版本是1.2.24,正好以前打过
但是不会加修补和过滤,也放弃了
好像是个GO语言的,放弃了