Have a look at this Gist:
That's the test script I've been using, along with stack traces after the interrupt. The `when :get` case statement writes progress, so we can see exactly how many bytes were transferred, plus we get some insight in to the read loop activity level. Observing the script, we can see the Net::SFTP read loop stop updating when trying to read 4294049792 bytes. Operations::Download#on_read updates progress every time a read finishes, so when it stops, we can assume that something has hung internally.
In that same Gist, you can see the stack trace from the script when given SIGINT after it hangs. The resulting stack trace gives further clues that the problem is actually inside Net::SSH. I tried both Session#download! and Session#download. Both show the hang at net-ssh-2.6.7/lib/net/ssh/ruby_compat.rb:30 when given the interrupt. There's a call to IO#select here, which makes a system call to select(2) (we've arrived at the threshold of Ruby's C implementations) and returns an array. If we wanted to keep debugging, we'd want to attach a debugger to the process and have a look at the stack, focusing on arguments passed to IO#select. Given the file offset range we're in, a datatype boundary issue is likely.
And at this point, you're really traveling down the rabbit hole. I'm an amateur Rubyist, so we're down to the point where my knowledge withers and I start grabbing at straws.
This would make a good ticket for Net::SSH (and maybe Net::SFTP). I'm sure the authors of those libraries would be interested in seeing what we've discovered here. If you're not up for it, I'd be happy to take it to them. If you do open a ticket, be sure to post a link here so we can follow the progress.
In the interim, it looks like your best bet for a workaround is shelling out.