看到第五章了。
标题中 Dll Tramplining(跳板)名字是从如下地址找到的,写的很好:
http://en.wikipedia.org/wiki/Buffer_overflow#The_jump_to_address_stored_in_a_register_technique
Shellcode
原来,shellcode 这个词来源于一篇论文:1996 年 Aleph One 发表跨时代的《Smathing The Stack For Fun And Profit》,文中描述讲到利用基于栈的溢出向进程中植入一段用于获得 shell 的代码,并将植入的代码称为 Shellcode。后来 shellcode 被统一指代通过缓冲区溢出植入的代码。
exploit 是一个过程,其结果通常体现为一段代码,这段代码承载了 shellcode。exploit 用于生成攻击性的网络数据包或者其他形式的攻击性输入,其核心是淹没返回地址,劫持进程控制权,之后跳转执行 shellcode。
shellcode 有通用性,而 exploit 往往针对特定漏洞。如果说 exploit 是导弹,shellcode 则可以比喻成弹头。Metasploit 就充分利用了模块化和代码复用的思想,将 exploit 和 payload (shellcode) 分开。
Shellcode 需要解决的问题
1. 自动定位 shellcode 的起点。实际使用中,shellcode 经常被动态加载(特别是 IE)。
2. 填充数据的设计。(想到《The Art Of Exploitation》中提到过好多技巧,可惜细节忘差不多了)
3. 动态获取系统的 API 地址。
4. 对 shellcode 进行编码解码,突破 buffer 和 IDS 的限制。(又想到《TAOE》)
动态定位 Shellcode — 跳板原理
实际 exploit 过程中,由于 dll 的装入和卸载等原因,Windows 进程的函数栈帧经常会移位,如此一来,将返回地址设置成定值的方法就不通用。
1998 年,黑客组织“Cult of the Dead Cow”的 Dildog 在 Bugtrq 邮件列表中以 Microsoft Netmeeting 为例首次提出了利用 jmp esp 完成对 shellcode 的动态定位,从而解决了 Windows 下栈帧移位问题给开发稳定 exploit 带来的重重困难。毫不夸张地讲,跳板技术是 Windows 栈溢出利用技术的一个里程碑。
注意到在一般情况下,ESP 中的地址问题指向系统栈中,且不会被溢出的数据破坏。函数返回时,ESP 所指的位置通常在返回地址的下一个位置,这就为跳板技术的实施开了条路。(需要注意,函数返回时 ESP 所指的位置与函数调用约定、返回指令有关,例如“retn 3”、“retn 4”之后,ESP 所指的位置是会有偏差)
如上图,使用 jmp esp 跳板动态定位的方法为:
1. 用内存中任意一条 jmp esp 指令的地址覆盖返回地址,而不用上一篇中手工查出的 shellcode 的起始地址。
2. 函数返回地去执行 jmp esp(跳板),而不直接执行 shellcode。
3. 将 shellcode 放置在返回地址之后,这样 jmp esp 之后执行的就是 shellcode。
这样一来,不管栈帧如何移位,shellcode 都能被动态地准确定位到。
获取跳板
获取踏板可以使用下面这段代码:
1 /* 2 Findjmp.c 3 written by Ryan Permeh - ryan at eeye - Summarily modified by I2S-LaB.com 4 http://www.eeye.com 5 6 Findjmp2.c (pop/pop/ret scanner, logging to file) 7 version by A.D - class101 at hat-squad 8 http://class101.org, http://www.hat-squad.com 9 10 11 This finds useful jump points in a dll. Once you overflow a buffer, by 12 looking in the various registers, it is likely that you will find a 13 reference to your code. This program will find addresses suitible to 14 overwrite eip that will return to your code. 15 16 It should be easy to modify this to search for other good jump points, 17 or specific code patterns within a dll. 18 19 It currently supports looking for: 20 1. jmp reg 21 22 2. call reg 23 24 3. push reg 25 ret 26 All three options result in the same thing, EIP being set to reg. 27 28 It also supports the following registers: 29 EAX 30 EBX 31 ECX 32 EDX 33 ESI 34 EDI 35 ESP 36 EBP 37 */ 38 39 #include <iostream.h> 40 #include <fstream.h> 41 #include <Windows.h> 42 #include <stdio.h> 43 44 FILE *fplog; 45 46 void usage(); 47 void sep(); 48 void iok(BYTE *curpos, char *reg); 49 void iok2(BYTE *curpos, char *reg); 50 void ook(BYTE *curpos, char *reg); 51 void ook2(BYTE *curpos, char *reg); 52 53 DWORD GetRegNum( char *reg ); 54 void findjmp( char *dll, char *reg ); 55 56 //This finds useful jump points in a dll. Once you overflow a buffer, by 57 //looking in the various registers, it is likely that you will find a 58 //reference to your code. This program will find addresses of suitible 59 //addresses of eip that will return to your code. 60 61 int main( int argc, char **argv ) 62 { 63 if( argc <= 2 ) 64 usage(); 65 66 else 67 { 68 char dll[512], //holder for the dll to look in 69 reg[512]; // holder for the register 70 71 if ((fplog =fopen("findjmp.txt","r"))==NULL){ 72 fplog =fopen("findjmp.txt","w");} 73 else fplog =fopen("findjmp.txt","a"); 74 strncpy( dll, argv[1], 512 ); 75 strncpy( reg, argv[2], 512 ); 76 findjmp( dll, reg ); 77 } 78 return 0; 79 } 80 81 //This prints the usage information. 82 83 void usage() 84 { 85 printf("\nFindjmp, Eeye, I2S-LaB\nFindjmp2, Hat-Squad\nFindJmp DLL registre\nEx: findjmp KERNEL32.DLL esp"\ 86 "\nCurrently supported registre are: EAX, EBX, ECX, EDX, ESI, EDI, ESP, EBP\n" ); 87 } 88 89 //findjmp is the workhorse. it loads the requested dll, and searches for 90 //the specific patterns for jmp reg, push reg ret, and call reg 91 92 void findjmp( char *dll,char *reg ) 93 { 94 char reg1[]="eax";char reg2[]="ebx"; 95 char reg3[]="ecx";char reg4[]="edx"; 96 char reg5[]="esi";char reg6[]="edi"; 97 char reg7[]="esp";char reg8[]="ebp"; 98 99 BYTE jmppat[8][2]={ { 0xFF, 0xE0 }, { 0xFF, 0xE3 }, { 0xFF, 0xE1 }, { 0xFF, 0xE2 }, 100 { 0xFF, 0xE6 }, { 0xFF, 0xE7 }, { 0xFF, 0xE4 }, { 0xFF, 0xE5 } }; // patterns for jmp ops 101 102 BYTE callpat[8][2]={ { 0xFF, 0xD0 }, { 0xFF, 0xD3 }, { 0xFF, 0xD1 }, { 0xFF, 0xD2}, 103 { 0xFF, 0xD6 }, { 0xFF, 0xD7 }, { 0xFF, 0xD4 }, { 0xFF, 0xD5 } }; // patterns for call ops 104 105 BYTE pushretpat[8][2]={ { 0x50, 0xC3 }, { 0x53, 0xC3 }, { 0x51, 0xC3 }, { 0x52, 0xC3 }, 106 { 0x56, 0xC3 }, { 0x57, 0xC3 }, { 0x54, 0xC3 }, { 0x55, 0xC3 } }; // patterns for pushret ops 107 108 BYTE poppat[8][1]={ { 0x58 }, { 0x5B }, { 0x59 }, { 0x5A }, // patterns for pop,pop,ret 109 { 0x5E }, { 0x5F }, { 0x5C }, { 0x5D },}; 110 111 BYTE retn[1][1]={ 0xC3 }; // pattern for pop,pop,ret 112 113 BYTE retnbis[1][1]={ 0xC2 }; // pattern for pop,pop,ret 114 115 116 HMODULE loadedDLL; //base pointer for the loaded DLL 117 118 BYTE *curpos; //current position within the DLL 119 BYTE *curpos2; //subposition pop,pop,ret 120 121 DWORD regnum=GetRegNum(reg); // decimal representation of passed register 122 DWORD regnum1=GetRegNum(reg1);DWORD regnum2=GetRegNum(reg2); 123 DWORD regnum3=GetRegNum(reg3);DWORD regnum4=GetRegNum(reg4); 124 DWORD regnum5=GetRegNum(reg5);DWORD regnum6=GetRegNum(reg6); 125 DWORD regnum7=GetRegNum(reg7);DWORD regnum8=GetRegNum(reg8); 126 127 DWORD numaddr=0; //accumulator for addresses 128 129 if( regnum == -1 ) //check if register is useable 130 { //it didn't load, time to bail 131 printf( "There was a problem understanding the register.\n"\ 132 "Please check that it isa correct IA32 register name\n"\ 133 "Currently supported are:\n "\ 134 "EAX, EBX, ECX, EDX, ESI, EDI, ESP, EBP\n"\ 135 ); 136 137 exit(-1); 138 } 139 140 if( (loadedDLL=LoadLibraryA(dll)) == NULL) // check if DLL loaded correctly 141 { //it didn't load, time to bail 142 printf( "There was a problem Loading the requested DLL.\n"\ 143 "Please check that it is in your path and readable\n" ); 144 exit(-1); 145 } 146 else 147 { 148 sep(); 149 fprintf(fplog,"Findjmp, Eeye, I2S-LaB\nFindjmp2, Hat-Squad\n"); 150 printf("\nFindjmp, Eeye, I2S-LaB\nFindjmp2, Hat-Squad\n"); 151 printf( "Scanning %s for code useable with the %s register\n", dll, reg ); //we loaded the dll correctly, time to scan it 152 fprintf(fplog,"Scanning %s for code useable with the %s register\n", dll, reg ); //we loaded the dll correctly, time to scan it 153 sep(); 154 curpos=(BYTE*)loadedDLL; //set curpos at start of DLL 155 curpos2=(BYTE*)loadedDLL; //pop,pop,ret subscan. 156 157 __try 158 { 159 while(1) 160 { 161 Sleep(1/10); 162 if( !memcmp( curpos, jmppat[regnum], 2) ) //check for jmp match 163 { 164 printf( "0x%X\tjmp %s\n", curpos, reg ); // we have a jmp match 165 fprintf(fplog,"0x%X\tjmp %s\n", curpos, reg ); // we have a jmp match 166 numaddr++; 167 } 168 else if( !memcmp( curpos, callpat[regnum],2) ) //check for call match 169 170 { 171 printf( "0x%X\tcall %s\n", curpos, reg ); // we have a call match 172 fprintf(fplog,"0x%X\tcall %s\n", curpos, reg ); 173 numaddr++; 174 } 175 else if( !memcmp(curpos,pushretpat[regnum], 2) ) //check for push/ret match 176 { 177 printf( "0x%X\tpush %s - ret\n", curpos, reg ); // we have a pushret match 178 fprintf(fplog,"0x%X\tpush %s - ret\n", curpos, reg ); // we have a jmp match 179 numaddr++; 180 } 181 else if( !memcmp(curpos,poppat[regnum], 1) ) //check for pop/pop/ret match 182 { 183 curpos2++; 184 if( !memcmp(curpos2,poppat[regnum1], 1) ) 185 { 186 curpos2++; 187 if( !memcmp(curpos2,retn, 1) ) 188 { 189 iok(curpos, reg); // we have a popopret match 190 ook(curpos, reg); // we have a popopret match 191 numaddr++; 192 } 193 if( !memcmp(curpos2,retnbis, 1) ) 194 { 195 iok2(curpos, reg); // we have a popopret match 196 ook2(curpos, reg); // we have a popopret match 197 numaddr++; 198 } 199 curpos2--;curpos2--;goto loop; 200 } 201 if( !memcmp(curpos2,poppat[regnum2], 1) ) 202 { 203 curpos2++; 204 if( !memcmp(curpos2,retn, 1) ) 205 { 206 iok(curpos, reg); // we have a popopret match 207 ook(curpos, reg); // we have a popopret match 208 numaddr++; 209 } 210 if( !memcmp(curpos2,retnbis, 1) ) 211 { 212 iok2(curpos, reg); // we have a popopret match 213 ook2(curpos, reg); // we have a popopret match 214 numaddr++; 215 } 216 curpos2--;curpos2--;goto loop; 217 } 218 if( !memcmp(curpos2,poppat[regnum3], 1) ) 219 { 220 curpos2++; 221 if( !memcmp(curpos2,retn, 1) ) 222 { 223 iok(curpos, reg); // we have a popopret match 224 ook(curpos, reg); // we have a popopret match 225 numaddr++; 226 } 227 if( !memcmp(curpos2,retnbis, 1) ) 228 { 229 iok2(curpos, reg); // we have a popopret match 230 ook2(curpos, reg); // we have a popopret match 231 numaddr++; 232 } 233 curpos2--;curpos2--;goto loop; 234 } 235 if( !memcmp(curpos2,poppat[regnum4], 1) ) 236 { 237 curpos2++; 238 if( !memcmp(curpos2,retn, 1) ) 239 { 240 iok(curpos, reg); // we have a popopret match 241 ook(curpos, reg); // we have a popopret match 242 numaddr++; 243 } 244 if( !memcmp(curpos2,retnbis, 1) ) 245 { 246 iok2(curpos, reg); // we have a popopret match 247 ook2(curpos, reg); // we have a popopret match 248 numaddr++; 249 } 250 curpos2--;curpos2--;goto loop; 251 } 252 if( !memcmp(curpos2,poppat[regnum5], 1) ) 253 { 254 curpos2++; 255 if( !memcmp(curpos2,retn, 1) ) 256 { 257 iok(curpos, reg); // we have a popopret match 258 ook(curpos, reg); // we have a popopret match 259 numaddr++; 260 } 261 if( !memcmp(curpos2,retnbis, 1) ) 262 { 263 iok2(curpos, reg); // we have a popopret match 264 ook2(curpos, reg); // we have a popopret match 265 numaddr++; 266 } 267 curpos2--;curpos2--;goto loop; 268 } 269 if( !memcmp(curpos2,poppat[regnum6], 1) ) 270 { 271 curpos2++; 272 if( !memcmp(curpos2,retn, 1) ) 273 { 274 iok(curpos, reg); // we have a popopret match 275 ook(curpos, reg); // we have a popopret match 276 numaddr++; 277 } 278 if( !memcmp(curpos2,retnbis, 1) ) 279 { 280 iok2(curpos, reg); // we have a popopret match 281 ook2(curpos, reg); // we have a popopret match 282 numaddr++; 283 } 284 curpos2--;curpos2--;goto loop; 285 } 286 if( !memcmp(curpos2,poppat[regnum7], 1) ) 287 { 288 curpos2++; 289 if( !memcmp(curpos2,retn, 1) ) 290 { 291 iok(curpos, reg); // we have a popopret match 292 ook(curpos, reg); // we have a popopret match 293 numaddr++; 294 } 295 if( !memcmp(curpos2,retnbis, 1) ) 296 { 297 iok2(curpos, reg); // we have a popopret match 298 ook2(curpos, reg); // we have a popopret match 299 numaddr++; 300 } 301 curpos2--;curpos2--;goto loop; 302 } 303 if( !memcmp(curpos2,poppat[regnum8], 1) ) 304 { 305 curpos2++; 306 if( !memcmp(curpos2,retn, 1) ) 307 { 308 iok(curpos, reg); // we have a popopret match 309 ook(curpos, reg); // we have a popopret match 310 numaddr++; 311 } 312 if( !memcmp(curpos2,retnbis, 1) ) 313 { 314 iok2(curpos, reg); // we have a popopret match 315 ook2(curpos, reg); // we have a popopret match 316 numaddr++; 317 } 318 curpos2--;curpos2--;goto loop; 319 } 320 curpos2--; 321 } 322 loop: 323 curpos++; 324 curpos2++; 325 } 326 } 327 __except(1) 328 { 329 sep(); 330 fprintf( fplog,"Finished Scanning %s for code useable with the %s register\n", dll, reg ); 331 printf( "Finished Scanning %s for code useable with the %s register\n", dll, reg ); 332 printf( "Found %d usable addresses\n", numaddr ); 333 fprintf( fplog,"Found %d usable addresses\n", numaddr );sep();fprintf( fplog,"\n\n\n"); 334 } 335 } 336 337 } 338 339 340 DWORD GetRegNum( char *reg ) 341 { 342 DWORD ret=-1; 343 if( !stricmp( reg, "eax") ) 344 { 345 ret=0; 346 } 347 else if( !stricmp( reg, "ebx") ) 348 { 349 ret=1; 350 } 351 else if( !stricmp( reg, "ecx") ) 352 { 353 ret=2; 354 } 355 else if( !stricmp( reg, "edx") ) 356 { 357 ret=3; 358 } 359 else if( !stricmp( reg, "esi") ) 360 { 361 ret=4; 362 } 363 else if( !stricmp( reg, "edi") ) 364 { 365 ret=5; 366 } 367 else if( !stricmp( reg, "esp") ) 368 { 369 ret=6; 370 } 371 else if( !stricmp( reg, "ebp") ) 372 { 373 ret=7; 374 } 375 376 return ret; //return our decimal register number 377 } 378 379 void sep() 380 { 381 fprintf(fplog,"----------------------------------------------------------------------------\n"); 382 } 383 384 void iok(BYTE *curpos, char *reg) 385 { 386 printf( "0x%X\tpop %s - pop - ret\n", curpos, reg ); // we have a popopret match 387 } 388 389 void iok2(BYTE *curpos, char *reg) 390 { 391 printf( "0x%X\tpop %s - pop - retbis\n", curpos, reg ); // we have a popopret match 392 } 393 394 void ook(BYTE *curpos, char *reg) 395 { 396 fprintf(fplog,"0x%X\tpop %s - pop - ret\n", curpos, reg ); // we have a jmp match 397 } 398 399 void ook2(BYTE *curpos, char *reg) 400 { 401 fprintf(fplog,"0x%X\tpop %s - pop - retbis\n", curpos, reg ); // we have a jmp match 402 }