Rating: 5.0
# **Write-up onnx_re (CTFZone 2021)**
![](https://raw.githubusercontent.com/osogi/writeups/main/ctfzone2021/onnx_re/attachments/1.jpg)
> Так как никто до сих пор не купил у меня рекламы, я просто прорекламирую мой канал [@ch4nnel1](https://t.me/ch4nnel1) в райтапах от этого же канала.
Original write-up: [https://hackmd.io/CeWJhxjFRMuHI1uOzgkocw](https://hackmd.io/CeWJhxjFRMuHI1uOzgkocw)
## Разбор
![](https://raw.githubusercontent.com/osogi/writeups/main/ctfzone2021/onnx_re/attachments/2.png)
Сам таск (https://ctf.bi.zone/challenges/7)
Скачиваем прикрепленный файл, это 7z архив со следующими файлами
![](https://raw.githubusercontent.com/osogi/writeups/main/ctfzone2021/onnx_re/attachments/3.png)
Установив все необходимое с помощью команды `python3.7 -m pip install -r requirements.txt`, изучим питон код
```python=0
import numpy as np
from onnx import load
from onnx.onnx_ONNX_REL_1_7_ml_pb2 import ModelProto
from onnxruntime.backend import prepare
def run_model(data: bytearray, model: ModelProto):
"""Run model and check result"""
x = np.array(data, dtype=np.int64)
i = np.array([0], dtype=np.int64)
keep_going = np.array([True], dtype=np.bool)
max_index = np.array([32], dtype=np.int64)
model_rep = prepare(model)
out = model_rep.run([x, i, keep_going, max_index])[1].reshape((1, 30))[0]
assert np.array_equal(
out,
np.array( [16780, 9831, ... 6214, 7169], dtype=np.int64,),
)
print(f"Flag: ctfzone\x7b{data.decode()}\x7d")
if __name__ == "__main__":
data = input("Enter your password 32 bytes long: ")
if len(data) != 32:
exit("Invalid length")
model = load("model.onnx")
try:
run_model(bytearray(data.encode()), model)
except AssertionError:
print("Wrong password")
```
Из кода видим, что нам нужно ввести какие-то 32 символа, которые после обработки нейронкой преобразуются в массив на 30 интов, и получившийся массив должен совпасть с данным нам.
Введем в код новую функцию `run_model_test`, кторая пока только выводит массив полученный от нейронки, и поставим ее на запуск сразу после загрузки модели
```python=0
def run_model_test(model: ModelProto):
data=bytearray(("1"*32).encode())
x = np.array(data, dtype=np.int64)
i = np.array([0], dtype=np.int64)
keep_going = np.array([True], dtype=np.bool)
max_index = np.array([32], dtype=np.int64)
model_rep = prepare(model)
ob = model_rep.run([x, i, keep_going, max_index])[1].reshape((1, 30))[0]
print(ob)
```
Чуть потыкавшись и поигравшись с результирующим массивом
![](https://raw.githubusercontent.com/osogi/writeups/main/ctfzone2021/onnx_re/attachments/4.png)
Доработаем нашу функцию `run_model_test` так, чтобы мы могли нагляднее увидеть зависимоть выхлопа ии от предоставленных данных. Для этого посчитаем результат ии от неких 32 байт, после будем менять по 1 байту и сравнивать новый результат с изначальным.
```python=0
def run_model_test(model: ModelProto):
data=bytearray(("1"*32).encode())
x = np.array(data, dtype=np.int64)
i = np.array([0], dtype=np.int64)
keep_going = np.array([True], dtype=np.bool)
max_index = np.array([32], dtype=np.int64)
model_rep = prepare(model)
standard = model_rep.run([x, i, keep_going, max_index])[1].reshape((1, 30))[0]
res=[]
for c in range(0, 32):
inp="1"*c+"2"+"1"*(31-c)
data=bytearray((inp).encode())
x = np.array(data, dtype=np.int64)
i = np.array([0], dtype=np.int64)
keep_going = np.array([True], dtype=np.bool)
max_index = np.array([32], dtype=np.int64)
model_rep = prepare(model)
out = model_rep.run([x, i, keep_going, max_index])[1].reshape((1, 30))[0]
buf=(out-standard)
res.append(buf)
print(str(c)+": "+str(buf))
```
После запуска этого кода, уже можно понять что `data[i]` влияет на `out[i-2:i+1]`
![](https://raw.githubusercontent.com/osogi/writeups/main/ctfzone2021/onnx_re/attachments/5.png)
Сделав еще один запуск, но в этот раз с "0" вместо "1" и "1" вместо "2" (1 и 10 строчка функции `run_model_test`), получим такой же результат как и от предыдущей версии скрипта => нейросетка просто делает `out[i]=data[i]*x+data[i+1]*y+data[i+2]*z+b`, где `x`, `y`, `z`, `b` - некие коэфиценты. `x`, `y` и `z` мы уже выясняли с помощью предыдущего скрипта. Осталось выяснить только `b`, сделаь это несложно, все что нужно это дать на вход `data=b"\x00"*32`. Сделав это, получим что все `b` равны 0.
Все что осталось сделать это решить систему линейных уравнений, я это сделал [доработав скрипт](https://github.com/osogi/writeups/tree/main/ctfzone2021/onnx_re/solution/solution.py) с помощью z3.
![](https://raw.githubusercontent.com/osogi/writeups/main/ctfzone2021/onnx_re/attachments/6.png)
Ну вот и флаг
## Решение
- Понять зависимость out'а нейронки от data поданной на вход (я не знаю как еще расписать этот шаг)
- Составить систему линейных уравнений (желательно с помощью скрипта)
- Решить эту систему
[Скрипт решение](https://github.com/osogi/writeups/tree/main/ctfzone2021/onnx_re/solution/solution.py)
## Эпилог
Так же в чате ctfzone при обсуждение этого таска всплывали такие ссылки https://github.com/onnx/onnx-mlir/ и https://netron.app/, говорят они как раз могут помочь при реверсе нейронок. Первую ссыль я особо не тыкал, но открыв сетку во второй я запутался, испугался и закрыл.
![](https://raw.githubusercontent.com/osogi/writeups/main/ctfzone2021/onnx_re/attachments/7.png)
Так же я наверное буду переходить с https://telegra.ph на https://hackmd.io, так как в первом нельзя простым способом красиво вставить код, + markdown -> можно безпроблемотично переносить на другие сайты и тд.