You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Декомпилируем. Расставляем удобные нам имена локальных переменных, чтобы код читался проще
22
+
Замечаем функцию `win`, которая выводит флаг - ищем референс на неё и понимаем, что она нигде не вызывается.
23
+
24
+
Смотрим функцию `move_player` и видим не упомянутую команду `l<символ>` которая меняет символ, представляющий игрока на поле с `@` на любой введенный. Значит, мы можем поменять один байт внутри массива чаров, представляющего карту.
25
+
26
+
Замечаем, что на "карту"(массив чаров длинной 2700) игрок выставляется следующей строчкой кода по анализу Гидры(undefined имеет длину 1 байт):
Это значит, что мы можем ходить по памяти по одному байту за пределами карты, т.к. выражение в скобках - `int` и он приводится потом к указателю на однобайтовый тип данных, разыменовав который мы устанавливаем значок игрока на карту
30
+
31
+
Замечаем, что адрес возврата из `move_player` в `main` равен 080497**04**, а адрес начала функции `win` равен 080497**5d**, которые как раз различаются на один байт. А мы как раз обладаем возможностью изменить один байт в любой точки памяти из-за способа адресации, выбранного создателем игры.
32
+
33
+
Значение **5d** равно символу `']'`.
34
+
35
+
Запускаем удобный дебагер - я воспользовался [gdb+gef](https://github.com/hugsy/gef), ставим брейкпоинт на `move_player` и смотрим, как выглядит стек.
36
+
37
+
Смещение от начала массива, представляющего карту до первого байта адреса возврата из `move_player` в main равно 39. Это значит, что нам надо от левого верхнего угла 39 раз повторить ход влево (от левого - т.к. мы ползем вверх по стеку - в сторону уменьшения адресов)
38
+
39
+
При попытке просто проехаться на 39 влево мы цепляем важное и все крашит - поэтому мы делаем шаг вверх, чтобы "загрязнять" своими похождениями мусорные байты(ход вверх сместил нас на 90 байт вверх по стеку).
40
+
41
+
После 39 влево делаем один раз вниз (получаем минус 90 байт, которые мы прибавили ранее ходом вверх) - и попадаем на нужный байт. При установке на него его значение заменяется значком нашего игрока - поэтому предварительно выполняем команду `l]`
42
+
43
+
Тестируем локально. Радуемся - все получилось - мы подменили адрес возврата на адрес начала `win` и вызвали её
44
+
45
+
Запускаем дистанционно - ничего не работает.
46
+
47
+
Почему-то буфер не сбрасывается, если мы приходим в самое начало функции `win` 080497**5d**, поэтому пробуем последующие адреса вида 080497**xx** (т.к. мы можем менять только один байт), пока не получится
48
+
49
+
Сработало на адресе 080497**79**, а `0x79` - это символ `y`
50
+
51
+
Так мы и получили нашу полезную нагрузку для этого задания и флаг
Декомпилируем. Расставляем удобные нам имена локальных переменных, чтобы код читался проще
22
-
Замечаем функцию`win`, которая выводит флаг - ищем референс на неё и понимаем, что она нигде не вызывается.
21
+
Decompiling. Arrange the local variable names, so that the code is easier to read.
22
+
Note the`win` function that outputs the flag - there is not reference to it, it is not called anywhere.
23
23
24
-
Смотрим функцию move_player и видим не упомянутую команду `l<символ>` которая меняет символ, представляющий игрока на поле с `@` на любой введенный. Значит, мы можем поменять один байт внутри массива чаров, представляющего карту.
24
+
Inside the `move_player` function we can see a hidden command `l<symbol>` - it changes the symbol, that represents the player.
25
+
26
+
So we can use it as follows:
27
+
1. player:`@`
28
+
2. command:`l3`
29
+
3. player: `3`
30
+
31
+
It means, that we can change one byte inside the array of chars, that represents a map.
32
+
33
+
The following line is responsible for placing player character on the map:(undefined size is 1 byte):
25
34
26
-
Замечаем, что на "карту"(массив чаров длинной 2700) игрок выставляется следующей строчкой кода по анализу Гидры(undefined имеет длину 1 байт):
Это значит, что мы можем ходить по памяти по одному байту за пределами карты, т.к. выражение в скобках - `int` и он приводится потом к указателю на однобайтовый тип данных, разыменовав который мы устанавливаем значок игрока на карту
37
+
It means, that we can travel across the memory byte by byte, because `int` value `(*playerCoordY * 0x5a + map + playerCoordY[1])` was casted to a pointer on value of size 1 byte. And we can change one byte anywhere in the stack. It's not much we can do - and the first (and it is right) thought would be about changing the return address.
30
38
31
-
Замечаем, что адрес возврата из `move_player`в`main`равен 080497**04**, а адрес начала функции `win`равен 080497**5d**, которые как раз различаются на один байт. А мы как раз обладаем возможностью изменить один байт в любой точки памяти из-за способа адресации, выбранного создателем игры.
39
+
We can see, that return address of `move_player`inside`main`is 080497**04** and start address of the `win`function is 080497**5d**. They differ by one byte. Now we, what we need to do.
32
40
33
-
Запускаем удобный дебагер - я воспользовался [gdb+gef](https://github.com/hugsy/gef), ставим брейкпоинт на move_player и смотрим, как выглядит стек.
41
+
**5d** is equal to char `']'`.
34
42
35
-
Смещение от начала массива, представляющего карту до первого байта адреса возврата из move_player в main равно 39. Это значит, что нам надо от левого верхнего угла 39 раз повторить ход влево (от левого - т.к. мы ползем вверх по стеку - в сторону уменьшения адресов)
43
+
Run a debugger you're comfortable with - I was using [gdb+gef](https://github.com/hugsy/gef). Place breakpoint on `move_player` and inspect the stack.
36
44
37
-
При попытке просто проехаться на 39 влево мы цепляем важное и все крашит - поэтому мы делаем шаг вверх, чтобы "загрязнять" своими похождениями мусорные байты(ход вверх сместил нас на 90 байт вверх по стеку).
45
+
Offset from the beginning of the array to the first byte of the return address from `move_player` is equal to **39**. It means, that we need to make 39 steps to the left from the top left corner.
38
46
39
-
После 39 влево делаем один раз вниз (получаем минус 90 байт, которые мы прибавили ранее ходом вверх) - и попадаем на нужный байт. При установке на него его значение заменяется значком нашего игрока - поэтому предварительно выполняем команду `l]`
47
+
But program will crush, if you do like that. So we need to move up from top left conner (because with that we will move by 90 bytes in one step, and will not overwrite something important on the stack with our player-char).
40
48
41
-
Тестируем локально. Радуемся - все получилось - мы подменили адрес возврата на адрес начала `win` и вызвали её
49
+
So we need to make one step up. Then we make 39 steps to the left. And finally - make one step down (it eliminates out +90 bytes offset from the beginning) - and it's done.
42
50
43
-
Запускаем дистанционно - ничего не работает.
51
+
Locally it will work just fune. Yay!
44
52
45
-
Почему-то буфер не сбрасывается, если мы приходим в самое начало функции win 080497**5d**, поэтому пробуем последующие адреса вида 080497**xx** (т.к. мы можем менять только один байт), пока не получится
53
+
Then you will run that remote and it will not work.
46
54
47
-
Сработало на адресе 080497**79**, а `0x79` - это символ `y`
55
+
I don't know why, but buffer will not flush, if you jump exactly to 080497**5d** - the beginning of the `win`. So we just try addresses of next instructions until it works.
48
56
49
-
Так мы и получили нашу полезную нагрузку для этого задания и флаг
Someone created a program to read text files; we think the program reads files with root privileges but apparently it only accepts to read files that are owned by the user running it.
14
+
Additional details will be available after launching your challenge instance.
15
+
16
+
## Solution ##
17
+
18
+
Самая большая подсказка - тег задачи `toctou`, что является названием вида уязвимостей. `toctou` -> `Time-of-check to time-of-use`
19
+
20
+
Файлом `flag.txt` владеет root, программой владеет тоже root, но там стоит бит `s` в правах - значит она запустится от рута - мы должны заставить её открыть `flag.txt`, при этом обойдя проверку нашего настоящего uid с uid владельца файла со стороны программы через функцию getuid.
21
+
22
+
1. Подключаемся по SSH Замечаем, что проверка владения файлом со стороны программы (где getuid == uid_файла) происходит один раз, а дальше идет чтение файла
23
+
24
+
2. Создаем файл, которым мы владеем - `ttt.txt` - я написал туда `123`
25
+
26
+
3. Создадим символьную ссылку `my_link` и будем её бесконечно привязывать к `ttt.txt`, затем к `flag.txt` и так по кругу следующим скриптом:
4. Затем запускаем бесконечный цикл, который выполняет программу на `my_link` следующим скриптом:
33
+
34
+
```bash
35
+
whiletrue;do ./txtreader my_link;done
36
+
```
37
+
5. После этого в определенный момент произойдет следующее: программа проверит uid юзера на совпадение с uid владельца файла, когда `my_link` -> `ttt.txt`. Затем `my_link` переподвяжится на `flag.txt`, а параллельно с этим программа `txtreader` выполнит чтение из `my_link`, тем самым мы получим флаг, т.к. мы обошли проверку uid, а файл `flag.txt` прочитается, т.к. им владеет root, под именем которого программа выполняется
Copy file name to clipboardexpand all lines: Binary Exploitation/tic-tac/tic-tac.md
+12-7
Original file line number
Diff line number
Diff line change
@@ -15,25 +15,30 @@ Additional details will be available after launching your challenge instance.
15
15
16
16
## Solution ##
17
17
18
-
Самая большая подсказка - тег задачи `toctou`, что является названием вида уязвимостей. `toctou` -> `Time-of-check to time-of-use`
18
+
The most useful hint - tag of the task `toctou`. It is a name of the vulnerability: `toctou` -> `Time-of-check to time-of-use`.
19
19
20
-
Файлом `flag.txt`владеет root, программой владеет тоже root, но там стоит бит `s` в правах - значит она запустится от рута - мы должны заставить её открыть `flag.txt`, при этом обойдя проверку нашего настоящего uid с uid владельца файла со стороны программы через функцию getuid.
20
+
The owner of the `flag.txt`is **root**, also `root` is the owner of the program.
21
21
22
-
1. Подключаемся по SSH Замечаем, что проверка владения файлом со стороны программы (где getuid == uid_файла) происходит один раз, а дальше идет чтение файла
22
+
But program have bit `s` in its permissions - it means, that it runs from **root**: we need to trick it to open `flag.txt`, avoiding uid check from the program at the same time.
23
23
24
-
2. Создаем, которым мы владеем - `ttt.txt` - я написал туда `123`
24
+
1. Connect via SSH to the instance. We can see, that there is **one** check for uid.
25
25
26
-
3. Создадим символьную ссылку my_link и будем её бесконечно привязывать к `ttt.txt`, затем к `flag.txt` и так по кругу следующим скриптом:
26
+
2. Create a file - I named it `ttt.txt` and wrote `123` inside it.
27
+
28
+
3. Create a symbol link `my_link`. Let's endlessly make it point to `ttt.txt`, then to `flag.txt` in a loop with following script:
4.Затем запускаем бесконечный цикл, который выполняет программу на`my_link` следующим скриптом:
34
+
4.Then let's endless loop, that runs `txtreader` over`my_link`:
33
35
34
36
```bash
35
37
whiletrue;do ./txtreader my_link;done
36
38
```
37
-
5. После этого в определенный момент произойдет следующее: программа проверит uid юзера на совпадение с uid владельца файла, когда `my_link` -> `ttt.txt`. Затем `my_link` переподвяжится на `flag.txt`, а параллельно с этим программа `txtreader` выполнит чтение из `my_link`, тем самым мы получим флаг, т.к. мы обошли проверку uid, а файл `flag.txt` прочитается, т.к. им владеет root, под именем которого программа выполняется
39
+
5. Then next events will occure:
40
+
1. Program will check uid, when `my_link` -> `ttt.txt`.
41
+
2.`my_link` will change and point to `flag.txt`.
42
+
3.`txtreader` will read from `my_link` - we have already bypass uid check, so we will get the `flag.txt`.
0 commit comments