Display related posts without a plugin

I slightly modified your code and tested on my WordPress installation.
If you using a filter, like ‘the_content’, which recieves a content as an argument,
you must also return this content back.
Here is working version of your function with comments.

 //this filter recieves $content, which you should always return, 
 //otherwise you will not see the content

 function posts_related($content){ 
    if (is_single()){

        $custom_query = new WP_Query( array( 
            'posts_per_page' => 8, 
            'post__not_in' => array(get_queried_object_id()), 
            'orderby' => 'rand', 
        ));

        //lets define $related variable outside of while loop
        // and if statement to make it visible 
        $related = '';

    if ( $custom_query->have_posts() ) : 
        while ( $custom_query->have_posts() ) : $custom_query->the_post();

            //use functions with "get_" to recieve a value
            // and not echo it
            $permalink = get_the_permalink();
            $title = get_the_title();
        
            if ( has_post_thumbnail() ) { 

                //here we receiving just url with
                //get_the_post_thumbnail_url()
                $related .= '<a href="' . $permalink .  '">
                                <img src="' . get_the_post_thumbnail_url() . '" />
                             </a>';   
      
            }else{    
                $related .=  '<a href="' .  $permalink . '"><b>' . $title . '</b></a>';
            }
        endwhile;
        wp_reset_postdata();

        //return content + related posts
        //if is_singular and we have related post
        return $content . $related;

    else : 

        //if is_singular but no posts were found
        $related .= '<p>Nothing to show.</p>';

    endif;

    //if is_singular and we have related posts
    //return content + related posts 
    //or content + "nothing to show"
    return $content . $related;
    
}

//if page is not a single,
//just return content
return $content;

}

P.S. Usually related posts are posts with the same category or tag, just orderby with rand will return random posts, but I guess you know this.