Однажды, устав от насмешек всех остальных хакеров, он решил, наконец, взглянуть на BASH.
Его первой мыслью было: «Баш? Баш винду? Ох уж эти жестокие сценарные детишки!». Пройдя сотни онлайн-руководств,
он случайно (очевидно) нашел флаг. Следующим обновлением его статуса было «Дети со сценариями никогда не смогут получить флаг из этого защищенного паролем двоичного файла». Как он смеет называть вас и нас «сценарными детишками»?! Убейте его.
Вот файл . Для 32-битных пользователей — файл
Флаг SHA-256 полученного хэша MD-5.
Создатель: Рави Кишоре Р
Начнем с того, что откроем бинарник в GDB и проверим его дизассемблирование.
0x0000000000400943 <+0>: push rbp 0x0000000000400944 <+1>: mov rbp,rsp 0x0000000000400947 <+4>: sub rsp,0x30 0x000000000040094b <+8>: mov DWORD PTR [rbp-0x24],edi 0x000000000040094e <+11>: mov QWORD PTR [rbp-0x30],rsi 0x0000000000400952 <+15>: mov rax,QWORD PTR fs:0x28 0x000000000040095b <+24>: mov QWORD PTR [rbp-0x8],rax 0x000000000040095f <+28>: xor eax,eax 0x0000000000400961 <+30>: cmp DWORD PTR [rbp-0x24],0x1 0x0000000000400965 <+34>: jle 0x400b47 <main+516> 0x000000000040096b <+40>: mov rax,QWORD PTR [rbp-0x30] 0x000000000040096f <+44>: add rax,0x8 0x0000000000400973 <+48>: mov rax,QWORD PTR [rax] 0x0000000000400976 <+51>: add rax,0x1 0x000000000040097a <+55>: movzx eax,BYTE PTR [rax] 0x000000000040097d <+58>: test al,al 0x000000000040097f <+60>: je 0x400ad3 <main+400> 0x0000000000400985 <+66>: mov rax,QWORD PTR [rbp-0x30] 0x0000000000400989 <+70>: add rax,0x8 0x000000000040098d <+74>: mov rax,QWORD PTR [rax] 0x0000000000400990 <+77>: add rax,0x1 0x0000000000400994 <+81>: movzx eax,BYTE PTR [rax] ---Type <return> to continue, or q <return> to quit--- 0x0000000000400997 <+84>: test al,al 0x0000000000400999 <+86>: jne 0x400ad3 <main+400> 0x000000000040099f <+92>: mov esi,0x400cd0 0x00000000004009a4 <+97>: mov edi,0x602060 0x00000000004009a9 <+102>: call 0x400730 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt> 0x00000000004009ae <+107>: mov esi,0x400770 0x00000000004009b3 <+112>: mov rdi,rax 0x00000000004009b6 <+115>: call 0x400760 <_ZNSolsEPFRSoS_E@plt> 0x00000000004009bb <+120>: mov esi,0x400cf0 0x00000000004009c0 <+125>: mov edi,0x602060 0x00000000004009c5 <+130>: call 0x400730 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt> 0x00000000004009ca <+135>: mov esi,0x400770 0x00000000004009cf <+140>: mov rdi,rax 0x00000000004009d2 <+143>: call 0x400760 <_ZNSolsEPFRSoS_E@plt> 0x00000000004009d7 <+148>: mov esi,0x400d15 0x00000000004009dc <+153>: mov edi,0x602060 0x00000000004009e1 <+158>: call 0x400730 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt> 0x00000000004009e6 <+163>: mov esi,0x400770 0x00000000004009eb <+168>: mov rdi,rax 0x00000000004009ee <+171>: call 0x400760 <_ZNSolsEPFRSoS_E@plt>
Начнем с сохранения кадра стека и выделения 0x30 байт в стеке.
0x000000000040094b <+8>: mov DWORD PTR [rbp-0x24],edi 0x000000000040094e <+11>: mov QWORD PTR [rbp-0x30],rsi
Мы сохраняем в стеке содержимое edi (которое устанавливается равным количеству вещей в argv при вызове main) и rsi (которое указывает на вектор аргументов).
0x0000000000400952 <+15>: mov rax,QWORD PTR fs:0x28
Я понятия не имею, что делает эта строка, если кто-то из вас знает, напишите об этом в комментариях.
0x000000000040095b <+24>: mov QWORD PTR [rbp-0x8],rax 0x000000000040095f <+28>: xor eax,eax
Мы помещаем rax в стек и обнуляем eax. Не знаю, почему.
0x0000000000400961 <+30>: cmp DWORD PTR [rbp-0x24],0x1 0x0000000000400965 <+34>: jle 0x400b47 <main+516>
if (argc ‹ 0x1) { перейти к 0x400b47; }
0x400b47 содержит код, который печатает «Пожалуйста, введите пароль».
0x000000000040096b <+40>: mov rax,QWORD PTR [rbp-0x30] 0x000000000040096f <+44>: add rax,0x8 0x0000000000400973 <+48>: mov rax,QWORD PTR [rax] 0x0000000000400976 <+51>: add rax,0x1
Идем дальше. Мы знаем, что argv — это двойной указатель. Здесь мы сохраняем *argv в rbp-0x30 и добавляем к нему 0x8. Итак, мы обращаемся к *(argv + 8) или argv[1].
Мы сохраняем указатель на this в rax и добавляем к нему 1.
rax теперь указывает на argv[1][1].
0x000000000040097a <+55>: movzx eax,BYTE PTR [rax]
Теперь он копирует значение *argv[1][1] в eax (1-байтовая копия с нулевым расширением). Eax занимает 4 байта, что означает, что он скопирует байт с *rax в eax и установит первые 3 байта eax в 0.
0x000000000040097d <+58>: test al,al 0x000000000040097f <+60>: je 0x400ad3 <main+400>
если (al == 0) { jmp to 0x400ad3; }
Это верно только тогда, когда младшие 8 битов == 0, что означает, что для того, чтобы это было правдой, значение в *argv[1][1] должно быть больше 256 или 0. Следовательно, argv[1][1] должно быть 0 чтобы получить доступ к этому блоку,
Затем мы снова делаем то же самое упражнение (60–86) и прыгаем, если не равны. Следовательно, мы в любом случае перейдем к 0x400ad3. Теперь давайте проверим дизассемблирование 0x400ad3:
0x400ad3 <main+400>: mov BYTE PTR [rbp-0x20],0x4d 0x400ad7 <main+404>: mov BYTE PTR [rbp-0x1f],0x61 0x400adb <main+408>: mov BYTE PTR [rbp-0x1e],0x73 0x400adf <main+412>: mov BYTE PTR [rbp-0x1d],0x74 0x400ae3 <main+416>: mov BYTE PTR [rbp-0x1c],0x65 0x400ae7 <main+420>: mov BYTE PTR [rbp-0x1b],0x72 0x400aeb <main+424>: mov BYTE PTR [rbp-0x1a],0x6e 0x400aef <main+428>: mov BYTE PTR [rbp-0x19],0x61 0x400af3 <main+432>: mov BYTE PTR [rbp-0x18],0x6d 0x400af7 <main+436>: mov BYTE PTR [rbp-0x17],0x65 0x400afb <main+440>: mov BYTE PTR [rbp-0x16],0x72
Похоже, мы помещаем кучу байтов в стек.
0x400aff <main+444>: mov rax,QWORD PTR [rbp-0x30] 0x400b03 <main+448>: add rax,0x8 0x400aff <main+444>: mov rax,QWORD PTR [rbp-0x30] 0x400b03 <main+448>: add rax,0x8 0x400b07 <main+452>: mov rax,QWORD PTR [rax] 0x400b0a <main+455>: lea rdx,[rbp-0x20]
Это тот же самый код, который мы видели ранее, он копирует указатель на argv[1] в rax. Затем мы загружаем адрес rbp-0x20 в rdx.
0x400b0e <main+459>: mov rsi,rdx 0x400b11 <main+462>: mov rdi,rax 0x400b14 <main+465>: call 0x4008eb <_Z7strcmprPcS_>
strcmp(строка в стеке, argv[1]);
Давайте теперь расшифруем, что на самом деле представляет собой строка в стеке:
(RBP-0x20 до 0x16)
72 65 6d 61 6e 72 65 74 73 61 4d
‹отредактировано›
перевернуть его (0x16–0x20)
‹отредактировано›
Ввод этого в качестве входных данных для двоичного файла дает нам наш флаг!