Summerschool Aachen 2004/Malware Lab
Contents
Notes about Presentation
ELF Tools and others
- http://soua.net/elf.py
- http://michael.bacarella.com/projects/sograph/sograph-0.95/elf.py
- http://elfsh.devhell.org/
- http://directory.fsf.org/libs/misc/libelf.html (http://developers.sun.com/solaris/articles/elf.html)
- http://sourceforge.net/projects/elfio/
- http://www.kerneled.org/projects/elf/index.html
- http://www.z0mbie.host.sk/infelf.html
- ELF(5)
- readelf(1), from GNU binutils
Notes about Lab Session
more elf tools
- elfsh - elf shell
Backdoor
I decided to install and test an openssh-3.7.1p2 backdoor today. As I didn't want to mess around with my own computer I asked the "LuFG I4" to lend me their notebook ;) and they were so kind to lend it to me. I first had some problems to install the backdoor as many necessary libraries were not installed and I had problems installing some of them (which took me a while to do so). But finally it worked out and I got that program running which was quite interesting. Apart from that we discussed during lunch about security issues in student dorms and later we discussed how to get to Essen tomorrow...
small quiz
There is a really small quiz consisting of just one question here.
The Quiz, Question 1
The perl scripts execute the system call with the number 4, sys_write(), supplying a file descriptor of "1", which denotes stdout, the standard address of the ELF header of the binary (probably the perl interpreter) being executed (increased by 1 to skip the leading 0x7f) and an output length of "3". The perl script outputs "ELF" on my Linux box.
The Quiz, Question 2
I found out that the "yourtoy" file was a UPX - compressed file pretty fast. But trying to uncompress it with the upx utility didn't succeed as the file seemed to be corrupt.
I then tried upx'ing two other files, creating hexdumps of them and then doing a "diff3" on the hexdumps. I noticed that there were some areas where the other files matched but differed from "yourtoy" so I tried overwriting the corresponding areas in "yourtoy" with a hexeditor with the values found in the other files, but this did not work out.
After that i downloaded the source code for upx and ucl (the library upx uses) and used ddd to debug upx. I traced through several function calls until I found the one that finally returned the information "this is not a valid upx file". That function just searched the to-be-uncompressed file for some magic string, which it didn't find in "yourtoy". I then had a look at the other files I had created to find out that those contained the string twice whereas "yourtoy" only contained it once and instead of a second occurence contained another magic string of bovine origin. I replaced that with the correct string and was finally able to uncompress "yourtoy".
-- Lutz Böhne
I too had no problem finding the UPX header information in the packed binary. I tried to understand the UPX header format in order to be able to find bugs in the yourtoy compressed executable. After starring at it for a while and not knowing why the four bytes I thought were the version number were not at all what I expected, I tried to find the origin of the NotPackedException (btw. you only get it if you do a "upx -d yourtoy"). But after like five files of C++ code I can only barely read I gave up about this. I then upx'ed another binary and compared it to the provided one in order to find differences. But I only had a look at the top of the file b/c that's where I thought the header resided. Of course I had no luck with this either, so I jumped back into the source, finally finding a comment which explained the file layout. Now that I knew I had to look at the bottom, things were pretty straightforward. By changing only three bytes you could make the UPX file actually behave like it was supposed to...
I did things a bit different. When I noticed that *someone* had messed up the upx header (or is it footer ?) I tried to look for documentation on the upx site. I didn't find any decent documentation on their site and didn't feel like looking at the source code because experiece has told me that such a thing can take a while (and we only have a couple of hours for the labsession). After some playing around with a hexeditor (to try and figure out what got changed) I got bored with the hexeditor and started looking at the manual file of upx. It said something interesting:
NOTES FOR LINUX/386 How it works: Because Linux is a real operating system, the in-place in-memory decompression scheme used in the other executable formats doesn't work here. Instead we must use temporary decompression to disk. Interestingly - because of the good memory management of the Linux kernel - this often does not introduce a noticable delay, and in fact there will be no disk access at all if you have enough free memory as the entire process takes places within the filesystem buffers. A compressed executable consists of the UPX stub and an overlay which contains the original program in a compressed form. The UPX stub is a statically linked ELF executable and does the following at program startup: 1) decompress the overlay to a temporary location in /tmp ...
Hm, so it gets decompressed in some temp file. To test this I made a test user and used the strace utility to see if this was true:
test@%m:%~% strace -i -s 10 ./yourtoy [????????] execve("./yourtoy", ["./yourtoy"], [/* 32 vars */]) = 0 [08048296] getpid() = 1664 [080482cf] open("/proc/1664/exe", O_RDONLY) = 3 [080482f1] lseek(3, 1468, SEEK_SET) = 1468 [08048232] read(3, "\27\4`\341"..., 12) = 12 [08048351] gettimeofday({1097073253, 115757}, NULL) = 0 [0804837c] unlink("/tmp/upxBXX3ARQABUA") = -1 ENOENT (No such file or directory) [0804839d] open("/tmp/upxBXX3ARQABUA", O_WRONLY|O_CREAT|O_EXCL, 0700) = 4 [080483aa] ftruncate(4, 15996) = 0 [080483bc] old_mmap(NULL, 20480, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40000000 [08048232] read(3, "|>\0\0\315"..., 8) = 8 [08048232] read(3, "\177?d\371"..., 5837) = 5837 [0804846d] write(4, "\177ELF\1\1"..., 15996) = 15996 [0804858b] unlink("/tmp/upxBXX3ARQABUA") = 0 [08048590] _exit(134518160) = ? test@%m:%~%
Aha ! so it just gets uncompressed and stored in some file in /tmp. I then fired up my debugger, set a breakpoint at 0x08048589 (right before the unlink()) and ran it. at this point it was uncompressed and not deleted yet. Then I ran file to see if that is the real binary:
test@%m:%~% ls -alhp *upxB* -rwx------ 1 test users 16K Oct 6 12:39 upxBXX3ARQABUA test@%m:%~% file upxBXX3ARQABUA upxBXX3ARQABUA: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), not stripped test@%m:%~%
I found what I needed to solve the second question. hooray :)
-- Ilja van Sprundel
I tried to find errors in the ELF header, but it looked alright. In desperation i ran strace against the binary and discovered the tempfile. Grabbing tempfiles is quite easy...
# first term while true; do ./yourtoy; done # other term cd tmp; while true; do cp upx* old.`date +%H%M%S` 2>/dev/null; done
After a minute i stopped the loops and used "file" to discover a working copy.
--MM 17:22, 6 Oct 2004 (CEST)
Some more ELF articles
Here are a couple of (old) articles about ELF, but they're nicely written:
and the online copy of the binutils documentation is here:
--Slewis 18:56, 6 Oct 2004 (CEST)