Una nota sobre fgets()
: Esto es lo que observé al pasar por el depurador. Cuando los datos canalizados se envían a fgets()
, se almacenan en búfer en el montón, luego se transfieren a la pila uno por uno hasta que
-
Una instrucción de comparación devuelve verdadero en un contador y el valor que se pasa en el registro RSI a la función fgets()
; que es la longitud de cadena especificada por el programador.
-
O el recibo de un byte 0x0a ("\ n")
Una vez que se cumpla cualquiera de los dos, la función sale y STDIN se desactiva. Así que se trata de E / S en búfer y no de E / S real en un descriptor de archivo que se utiliza para comunicarse con el sistema operativo.
De todos modos, para administrar la entrada estándar como lo está haciendo con bash en Python, puede usar la función Popen
de Subprocess para conectarse directamente al servicio, pero hay un problema. Python bloquea SIGPIPE de forma predeterminada, lo que impide mantener abierto el fd para escritura. Puede superar esto utilizando el módulo de señales y creando una función de parche que anula la configuración de los subprocesos para permitir 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)
Luego, llame a su programa directamente (sin canalización) a través de un shell de Subproccess que pasa el parche SIGPIP a través de preexec_fn
arg. También especifiqué bash como el shell que quería usar, pero no es obligatorio:
myproc = Popen("./test",
stdin=PIPE,
stdout=PIPE,
shell=True,
executable='/bin/bash',
preexec_fn=**restore_signals()**)
Esto mantendrá una tubería abierta y solo tendrás que cerrarla manualmente. Supongo que desde el punto de vista de un desarrollador de exploits, realmente no te importará.
Para enviar datos, no use el método .communicate()
. Tendrá que administrar manualmente la conexión enviando sus bytes de shellcode de esta forma:
#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")
Note el "\ n" para simular, ingrese para que fgets()
sepa que debe terminar el búfer.
Luego, para leer la salida, probablemente no querrá usar el método .readline()
a menos que sepa que la salida (como un banner) terminará con un byte de nueva línea (0x0a / '\ n'). Tendrá que administrarlo manualmente usando el método .read()
.
Lo que ocurre con .read()
es que necesitarás tener una idea de los datos que se devuelven. Entonces, si está esperando un retorno de algo como una pérdida de memoria o una cadena y conoce el tamaño, querrá especificarlo para que su secuencia de comandos no se bloquee mientras espera más datos. No puedo recordar cuál es el tamaño del búfer predeterminado en lectura, pero no volverá hasta que se llene ese búfer. .readline()
devolverá en un byte 0x0a. Por lo tanto, si está esperando una cadena de 10 bytes:
#sleep 1 second after your shellcode send to give the process time
time.sleep(1)
output = myproc.stdout.read(10)
Tenga en cuenta que si está enviando varias líneas, querrá flush()
el búfer stdout cada vez, o su respuesta leerá 10 bytes de los datos recibidos anteriormente.
Repita según sea necesario. Espero que ayude.