Una nota su fgets()
: questo è quello che ho osservato passando il debugger. Quando i dati inviati in pipe vengono inviati a fgets()
, vengono memorizzati nel buffer nell'heap, quindi vengono trasferiti nello stack uno alla volta finché
-
Un'istruzione di confronto restituisce true su un contatore e il valore che viene passato nel registro RSI alla funzione fgets()
; che è la lunghezza della stringa specificata dal programmatore.
-
o ricevuta di un byte 0x0a ("\ n")
Una volta che uno di questi viene raggiunto, la funzione si chiude e STDIN viene rimosso. Quindi è in I / O con buffer e non I / O effettivo in un descrittore di file che viene utilizzato per comunicare con il sistema operativo.
In ogni caso, per gestire lo stdin come se fossi lì con bash in Python, puoi usare la funzione Popen
del sottoprocesso per connetterti direttamente al servizio, ma c'è un problema. Python blocca SIGPIPE di default, che impedisce di aprire fd per scrivere. Puoi superare questo problema usando il modulo dei segnali e creando una funzione patch che sovrascrive la configurazione dei Sottoprocessi per consentire SIGPIP:
import signals
def restore_signals():
signals = ('SIGPIPE', 'SIGXFZ', 'SIGXFSZ')
for sig in signals:
if hasattr(signal, sig):
signal.signal(getattr(signal, sig), signal.SIG_DFL)
Quindi, chiama il tuo programma direttamente (senza piping) tramite una shell Subproccess passando il patch SIGPIP a preexec_fn
arg. Ho anche specificato bash come shell che volevo usare, ma non è necessario:
myproc = Popen("./test",
stdin=PIPE,
stdout=PIPE,
shell=True,
executable='/bin/bash',
preexec_fn=**restore_signals()**)
Ciò manterrà una pipe aperta e dovrai semplicemente chiuderla manualmente. Presumo da un punto di vista degli exploit dev, non sarà importante per te.
Per inviare dati, non utilizzare il metodo .communicate()
. Dovrai gestire manualmente la connessione inviando i tuoi byte shellcode in questo modo:
#prep the receive buffer by clearing it first
myproc.stdout.flush()
#Send your shellcode where shellcode is a var holding your shellcode
myproc.stdin.write(shellcode+"\n")
Notare "\ n" per simulare l'immissione in modo che fgets()
sappia terminare il buffer.
Quindi, per leggere l'output, probabilmente non vorrai usare il metodo .readline()
a meno che tu non sappia che l'output (come un banner) terminerà con un byte newline (0x0a / '\ n'). Dovrai gestirlo manualmente usando il metodo .read()
.
La cosa con .read()
è che devi avere un po 'di senso dei dati che vengono restituiti. Quindi, se ti aspetti un ritorno di qualcosa come una perdita di memoria o una stringa e conosci la dimensione, devi specificarlo, in modo che lo script non si blocchi mentre aspetti altri dati. Non riesco a ricordare quale sia la dimensione del buffer di default su read, ma non verrà restituita fino a quando il buffer non sarà riempito. .readline()
restituirà un byte 0x0a. Quindi, se ti stai aspettando una stringa da 10 byte:
#sleep 1 second after your shellcode send to give the process time
time.sleep(1)
output = myproc.stdout.read(10)
Tieni presente che se stai inviando più righe, vuoi aggiungere flush()
del buffer stdout ogni volta, altrimenti la tua risposta leggerà 10 byte dai dati ricevuti in precedenza.
Ripeti se necessario. Spero che ti aiuti.