Прыгать в аргв? - PullRequest
       24

Прыгать в аргв?

3 голосов
/ 01 мая 2010

Я экспериментирую с шеллкодом и наткнулся на технику nop-slide. Я написал небольшой инструмент, который принимает размер буфера в качестве параметра и создает буфер следующим образом: [NOP | SC | RET], при этом NOP забирает половину буфера, затем шелл-код, а остальные заполняются (предположительно) адресом возврата. Он очень похож на инструмент aleph1, описанный в его знаменитой статье.

Мое уязвимое тестовое приложение такое же, как в его статье:

int main(int argc, char **argv) {
char little_array[512];
if(argc>1)
    strcpy(little_array,argv[1]);   
return 0;
}

Я проверил и хорошо, он работает:

jth@insecure:~/no_nx_no_aslr$ ./victim $(./exploit 604 0)
$ exit

Но, честно говоря, я понятия не имею, почему. Хорошо, сохраненный eip был перезаписан как положено, но вместо того, чтобы прыгнуть куда-нибудь в буфер, я думаю, что он переместился в argv.

gdb обнаружил следующие адреса до вызова strcpy ():

(gdb) i f  
Stack level 0, frame at 0xbffff1f0:  
 eip = 0x80483ed in main (victim.c:7); saved eip 0x154b56  
 source language c.  
 Arglist at 0xbffff1e8, args: argc=2, argv=0xbffff294  
 Locals at 0xbffff1e8, Previous frame's sp is 0xbffff1f0  
 Saved registers:  
  ebp at 0xbffff1e8, eip at 0xbffff1ec  

Адрес little_array:

(gdb) print &little_array[0]
 $1 = 0xbfffefe8 "\020"

После strcpy ():

(gdb) i f
Stack level 0, frame at 0xbffff1f0:
 eip = 0x804840d in main (victim.c:10); saved eip 0xbffff458
 source language c.
 Arglist at 0xbffff1e8, args: argc=-1073744808, argv=0xbffff458
 Locals at 0xbffff1e8, Previous frame's sp is 0xbffff1f0
 Saved registers:
  ebp at 0xbffff1e8, eip at 0xbffff1ec

Итак, что здесь произошло? Я использовал 604-байтовый буфер для переполнения little_array, поэтому он, безусловно, перезаписал сохраненные ebp, сохраненные eip и argc, а также argv с предполагаемым адресом 0xbffff458.

Затем, после возврата, EIP указал на 0xbffff458. Но little_buffer находится в 0xbfffefe8, это разница в 1136 байт, поэтому он, конечно, не выполняет little_array. Я следил за выполнением с помощью команды stepi и, ну 0xbffff458 и далее, он выполняет NOP и достигает шелл-кода.

Я не совсем уверен, почему это происходит. Прежде всего, я прав, что он выполняет мой шелл-код в argv, а не little_array? И где загрузчик (?) Помещает argv в стек? Я думал, что это следует сразу после argc, но между argc и 0xbffff458 существует разрыв в 620 байт. Как это возможно, что он успешно «приземлился» в NOP-Pad по адресу 0xbffff458, что намного выше сохраненного eip в 0xbffff1ec?

Может кто-нибудь уточнить это? Я действительно понятия не имею, почему это работает. Мой тестовый компьютер - 32-разрядный компьютер Ubuntu 9.10 без ASLR. У жертвы есть исполняемый стек, установленный с помощью execstack -s.

Заранее спасибо.

1 Ответ

3 голосов
/ 01 мая 2010

Во-первых, составьте схему стека.

0xBFFFF4F9 |          |
            ----------
          ...   
0xBFFFF29E |    NOP   |
0xBFFFF29D |    NOP   | argv[1]   * guestimate
            ----------
0xBFFFF29C |   '\0'   |
          ...
0xBFFFF295 |    '/'   |
0xBFFFF294 |    '.'   | argv[0] "./victim"
            ----------
          ... 
            ----------
0xBFFFF1F8 |   NULL   |
0xBFFFF1F8 | &argv[1] |
0xBFFFF1F4 | &argv[0] | argv
            ---------- 
0xBFFFF1F0 | 0x000002 | argc
            ---------- 
          ...
0xBFFFEFE9 |          |
0xBFFFEFE8 |          | little_array
            ----------

(Примечание: на диаграмме некоторые блоки содержат 1 байт на строку, другие 4 или 8, в зависимости от типа, хранящегося внутри)

А где загрузчик (?) Помещает argv в стек?

То, что argc и argv перезаписываются после вызова strcpy, предполагает, что они, вероятно, выше little_array. Я поместил их в 0xBFFFF1F0, так как именно там стек завершился для предыдущего кадра, и в кадре main, похоже, нет места, даже если GDB говорит, что arglist расположен в 0xBFFFF1E8. Он лежал в моей системе - argc и argv были ниже little_array. Попробуйте p &argv и p &argc, чтобы точно определить, где они находятся.

Так как 0xBFFFF458 находится в argv [1], шелл-код в argv [1] - это действительно то, что будет выполняться, а не то, что в little_array. Поскольку существует две копии кода оболочки (одна в little_array, другая в argv[1]), можно выполнить любую из них, в зависимости от того, какой адрес возврата вы предполагаете.

Я думал, что [argv] следует сразу после argc, но между argc и 0xbffff458 существует разрыв в 620 байт.

0xBFFFF458 - это значение , сохраненное в argv после эксплойта (обратите внимание, что argc содержит то же самое, что и (signed)0xBFFFF458 == -1073744808). Ранее argv содержал 0xbffff294. В обоих случаях argv хранится в другом месте.

...