利用scanf函数的栈溢出漏洞,实现在main()方法中调用change()方法,并调用secret()方法。
1、苹果电脑
2、已越狱的32位系统的iphone手机,我这里用的是iphone4s(ios7.1)
存在漏洞的 roplevel.c 代码如下:
iPhoneOS7.1.sdk下载地址:
终端输入命令编译roplevel.c,编译的时候仍然需要关掉thumb模式、地址随机化和其他一些保护特性:
如果你用clang编译时使用的是新系统版本的iphone.sdk,会报错:
因为system()方法在新的sdk中已不能使用,所以这里要下载iPhoneOS7.1.sdk,并且再用clang编译时用到的sdk选择刚刚下载的iPhoneOS7.1.sdk。
把编译好的roplevel可执行文件通过爱思助手拖入手机的/var/mobile/Documents/test目录下
电脑终端使用iproxy映射22端口到2222
电脑终端输入连接ssh的命令并cd到/var/mobile/Documents/test目录下:
增加执行权限:
因为 roplevel.c 代码中,char buff[12]接收scanf()方法输入的字符串,限制为12个字符,当我们输入字符数超过12时会导致栈溢出。
程序崩溃,用爱思助手查看手机 /var/Logs/CrashReporter 路径下的崩溃日志文件,本次 崩溃日志文件是roplevel_2020-06-15-181505_chaorende-iPhone.ips ,内容如下:
从上可看出, r7 中存储的是 EEEE , pc 中存储的是 FFFF(我也没搞明白pc的0x46464644为啥对应FFFF)
由于我们输入的字符过长,覆盖了栈中保存的LR寄存器的值,当main函数结束时,程序会把栈中保存的LR的值赋给PC寄存器,PC寄存器中的值就是即将执行的下一条指令的地址。所以只要输入的字符串时用change()方法的地址覆盖掉栈中LR寄存器的值,就能实现main()方法结束时去调用change()方法。
gdb附加roplevel
查看change()的地址:
查看secret()的地址:
根据地址 0x0000be64,用 "\x64\xbe\x00\x00"替换"FFFF" ,执行main()函数时,scanf接收到"AAAABBBBCCCCDDDDEEEE\x64\xbe\x00\x00"从而覆盖main()方法栈中存储的LR为0x0000be64,当main即将结束时会把0x0000be64传给PC寄存器,从而跳转到change()方法:
先实现执行change()方法,操作如下:
看到终端中输出了"command changed.",则证明执行了 chang() 方法,虽然最后程序还是崩溃掉了。
chang()函数开头 push {r7, lr} 把lr和r7存储到了栈中,change()函数的结束时通过 pop {r7, pc} 把栈中存储的lr传给 pc寄存器,只要让 change()结束时的pc指向 secret() 方法,就可调用并执行secret()方法了。
于是,我们直接让 main()结束时跳转到 0x0000be68 位置,而不是0x0000be64位置,这样可以不执行chang()函数开头的 push {r7, lr}而不影响change()方法的执行。
当change()结束时执行pop {r7, pc},会把”main()入栈的LR“上方的第一个”其他数据“当做R7的值,把第二个”其他数据“当做LR的值传给PC寄存器,这样就能跳转到secret()方法了:
因为栈上的数据每次读取都是4个字节,我们输入的字符串溢出的长度只要刚好覆盖到栈上两个4字节的数据即可。
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课