Paramiko does not return segmentation fault error

Question:

I am trying to make a simple fuzzer for segmentation faults.

from pwn import *
import paramiko
import base64

#Setting Vars
#C Program
nameOfFileToExploit = "vuln"

hostname = '10.0.2.15'
port = 22
username = 'kali' 
password = 'kali'
command = 'ls'


context.update(arch='i386', os='linux')

# Connect to the server with SSH
client = paramiko.client.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(hostname, username=username, password=password)


# Find the point at which the program crashes
for i in range(1, 500):
    _stdin, _stdout,_stderr = client.exec_command('python -c "print( 'A'*' + str(i) + ')" | /home/kali/Desktop/BufferOverflow/PracOne/vuln')
    stdout = _stdout.readlines()
    print(stdout)
    stderr = _stderr.readlines()
    print(stderr)
    
    if 'Segmentation' in str(stdout):
        # For some reason when sent through pwntools the buffer to crash was 1 length longer than
        # it should have been?
        print('Crash at %d characters' % (i - 1))
        print('Crash at value will be %s' % hex(i - 1))
        break

A segmentation fault should occur at 31+ characters.

Please enter your name: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA Hi AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA zsh: segmentation fault ./vuln

But despite this being correctly reported by the terminal manually, it does not appear in the stderr for paramiko, meaning the if statement is never correct:

Example Output:

['Please enter your name: Hi An']
[]
['Please enter your name: Hi AAn']
[]
['Please enter your name: Hi AAAn']
[]
['Please enter your name: Hi AAAAn']
[]
['Please enter your name: Hi AAAAAn']
[]
['Please enter your name: Hi AAAAAAn']
[]
['Please enter your name: Hi AAAAAAAn']
[]
['Please enter your name: Hi AAAAAAAAn']
[]
['Please enter your name: Hi AAAAAAAAAn']
[]
['Please enter your name: Hi AAAAAAAAAAn']
[]
['Please enter your name: Hi AAAAAAAAAAAn']
[]
['Please enter your name: Hi AAAAAAAAAAAAn']
[]
['Please enter your name: Hi AAAAAAAAAAAAAn']
[]
['Please enter your name: Hi AAAAAAAAAAAAAAn']
[]
['Please enter your name: Hi AAAAAAAAAAAAAAAn']
[]
['Please enter your name: Hi AAAAAAAAAAAAAAAAn']
[]
['Please enter your name: Hi AAAAAAAAAAAAAAAAAn']
[]
['Please enter your name: Hi AAAAAAAAAAAAAAAAAAn']
[]
['Please enter your name: Hi AAAAAAAAAAAAAAAAAAAn']
[]
['Please enter your name: Hi AAAAAAAAAAAAAAAAAAAAn']
[]
['Please enter your name: Hi AAAAAAAAAAAAAAAAAAAAAn']
[]
['Please enter your name: Hi AAAAAAAAAAAAAAAAAAAAAAn']
[]
['Please enter your name: Hi AAAAAAAAAAAAAAAAAAAAAAAn']
[]
['Please enter your name: Hi AAAAAAAAAAAAAAAAAAAAAAAAn']
[]
['Please enter your name: Hi AAAAAAAAAAAAAAAAAAAAAAAAAn']
[]
['Please enter your name: Hi AAAAAAAAAAAAAAAAAAAAAAAAAAn']
[]
['Please enter your name: Hi AAAAAAAAAAAAAAAAAAAAAAAAAAAn']
[]
['Please enter your name: Hi AAAAAAAAAAAAAAAAAAAAAAAAAAAAn']
[]
['Please enter your name: Hi AAAAAAAAAAAAAAAAAAAAAAAAAAAAAn']
[]
['Please enter your name: Hi AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAn']
[]
['Please enter your name: Hi AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAn']
[]
['Please enter your name: Hi AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAn']
[]
['Please enter your name: Hi AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAn']
[]
['Please enter your name: Hi AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAn']
[]
['Please enter your name: Hi AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAn']
[]
['Please enter your name: Hi AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAn']
[]
['Please enter your name: Hi AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAn']
[]
['Please enter your name: Hi AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAn']
[]
['Please enter your name: Hi AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAn']
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]
[]

Paramiko should return the stderr mentioning the segementation fault. Why is it not?

Asked By: Seymour Butts

||

Answers:

But despite this being correctly reported by the terminal manually, it does not appear in the stderr for paramiko

This looks to be a zsh behavior, and is reproducible without paramiko being involved. Here’s my test setup. I have a C program that segfaults:

#include "stdio.h"

int main() {
  char* c;
  sprintf(c, "hi world");
}

When I run this from the terminal I get the following (I’m on OSX so for me it’s a "bus error" but the behavior is the same):

% ./t                                                                                                                                                                                                                                                                                      
zsh: bus error  ./t

Here is my invocation in python:

import subprocess

res = subprocess.run(["zsh", "-c", "./t"], capture_output=True)
print(res)
print(res.returncode)
% python3 ./t.py                                                                                                                                                                                                                                                                           
CompletedProcess(args=['zsh', '-c', './t'], returncode=-10, stdout=b'', stderr=b'')
-10

Paramiko should return the stderr mentioning the segementation fault. Why is it not?

Because running this way, zsh doesn’t provide the message for paramiko to return. I don’t have an answer to why zsh behaves like this (maybe because the output is not a terminal?).

However, I do have a suggestion that will work for you. Take zsh out of the equation and use subprocess in the python command you send through paramiko, and you can benefit from the behavior of subprocess:

A negative value -N indicates that the child was terminated by signal N (POSIX only).

Instead of checking stdout your internal python program can check the return code of the process to see if it is equal to -1 * signal.SIGSEGV2

import subprocess, signal

n = 1
while True:
    res = subprocess.run(["./t"], input=bytes('A' * n, "utf-8"), capture_output=True)
    print(res)
    if res.returncode * -1 in [ signal.SIGBUS, signal.SIGSEGV ]:
        break
    n += 1

print(f"{n} As was too many")

This gives me

% python3 ./t.py                                                                                                                                                                                                                                                                          11736ms
CompletedProcess(args=['./t'], returncode=-10, stdout=b'', stderr=b'')
1 As was too many

The big difference here is that I’m checking return code, not stderr. I’m not relying on the shell behavior, though the shell would have the same exit code. Therefore you could do the same thing at the paramiko level with How can you get the SSH return code using Paramiko?

Answered By: erik258
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.