Difference between revisions of "Summerschool Aachen 2004/Malware Lab"
(→The Quiz, Question 1) |
m (Reverted edits by Oxudocopaj (talk) to last revision by Mario Manno) |
||
(25 intermediate revisions by 10 users not shown) | |||
Line 1: | Line 1: | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
== Notes about Lab Session == | == Notes about Lab Session == | ||
=== more elf tools === | === more elf tools === | ||
* elfsh - elf shell | * elfsh - elf shell | ||
− | === small quiz | + | === 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... | ||
+ | |||
+ | --[[Samad]] | ||
+ | |||
+ | === Some more ELF articles === | ||
+ | |||
+ | Here are a couple of (old) articles about ELF, but they're nicely written: | ||
+ | |||
+ | * http://linuxjournal.com/article.php?sid=1059 | ||
+ | * http://www.linuxjournal.com/article.php?sid=1060 | ||
+ | |||
+ | and the online copy of the binutils documentation is here: | ||
+ | |||
+ | * http://sources.redhat.com/binutils/docs-2.15/binutils/index.html | ||
+ | |||
+ | --[[User:Slewis|Slewis]] 18:56, 6 Oct 2004 (CEST) | ||
+ | |||
+ | === Google Help === | ||
+ | |||
+ | As some of the participants were not very happy with Google, I looked | ||
+ | for that older version of Google Help which I once read. I found it | ||
+ | here: [http://find.stanford.edu/user_help.html] | ||
+ | |||
+ | -- [[Alexander Becher]] | ||
+ | |||
+ | === What to do with large capture files === | ||
+ | |||
+ | I played with the 1.6 GiB pcap file of summerschool traffic. It's not fun to have either ethereal or tcpdump display that file, because my machine does not have enough memory and I do not have enough patience. | ||
+ | |||
+ | I found the tools tcpslice and pcapmerge, which can be used to split pcap files into smaller pieces. These pieces can then be analyzed by ethereal without waiting forever. | ||
+ | |||
+ | -- [[Alexander Becher]] | ||
+ | |||
+ | == small quiz == | ||
There is a really small quiz consisting of just one question [http://www.informatik.hu-berlin.de/~thalheim/aachen2004/lab0610/ here]. | There is a really small quiz consisting of just one question [http://www.informatik.hu-berlin.de/~thalheim/aachen2004/lab0610/ 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 scripts execute the system call with the number 4, sys_write(), supplying a file descriptor of "1", which denotes stdout, the address of the ELF header of the executed | ||
The perl script outputs "ELF" on my Linux box. | 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.<br> | ||
+ | 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.<br> | ||
+ | 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... | ||
+ | |||
+ | -- [[Ernest Hammerschmidt]] | ||
+ | |||
+ | |||
+ | 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. | ||
+ | |||
+ | --[[User:Mario Manno|MM]] 17:22, 6 Oct 2004 (CEST) | ||
[[Category:Summerschools]] [[Category:Hacks]] | [[Category:Summerschools]] [[Category:Hacks]] | ||
+ | |||
+ | |||
+ | I made an infection tool which uses .eh_frame to store the evil payload. The infection routine doesn't overwrite the the entry point, but instead it makes an entry is the constructor list (.ctors). | ||
+ | The evil payload does nothing else then print a string and then leave, ret. | ||
+ | an example: | ||
+ | |||
+ | ilja@nikita:~/Summerschool/lab/virus%make infect | ||
+ | cc infect.c -o infect | ||
+ | ilja@nikita:~/Summerschool/lab/virus%cp /bin/ls . | ||
+ | ilja@nikita:~/Summerschool/lab/virus%./infect ./ls | ||
+ | offset to .eh_frame: 0x10a20 | ||
+ | The .eh_frame addr: 0x8058a20 | ||
+ | addr_shellcode: 0x8058a20 | ||
+ | offset to .ctors: 0x11328 | ||
+ | ilja@nikita:~/Summerschool/lab/virus%./ls | ||
+ | netric was here # <-- this is the strings :) | ||
+ | answers getroot.c ilrk.o ilrk3.o infect.c malware-unix.pdf upx upx.hex | ||
+ | ... | ||
+ | ilja@nikita:~/Summerschool/lab/virus% | ||
+ | |||
+ | next I made a small rootkit. I abused the register_sysctl_table() function which allows me to register an entry for sysctl() calls. When you use sysctl() right the entry I registered gets used and it calls the handler in it (which you can specify). All my handler does is give you uid/gid 0. but this could be extended with ease. It works like this: | ||
+ | |||
+ | ilja@nikita:~/Summerschool/lab/virus/ilrk%gcc -c ilrk.c -I/lib/modules/$(uname -r)/build/include | ||
+ | ilja@nikita:~/Summerschool/lab/virus/ilrk%su | ||
+ | Password: | ||
+ | root@nikita:/home/ilja/Summerschool/lab/virus/ilrk#insmod ilrk.o | ||
+ | root@nikita:/home/ilja/Summerschool/lab/virus/ilrk#lsmod | ||
+ | Module Size Used by Not tainted | ||
+ | ilrk 780 0 (unused) | ||
+ | snd-pcm-oss 37252 0 (unused) | ||
+ | ... | ||
+ | agpgart 39576 0 (unused) | ||
+ | root@nikita:/home/ilja/Summerschool/lab/virus/ilrk#exit | ||
+ | ilja@nikita:~/Summerschool/lab/virus/ilrk%make getroot | ||
+ | cc getroot.c -o getroot | ||
+ | ilja@nikita:~/Summerschool/lab/virus/ilrk%id | ||
+ | uid=500(ilja) gid=500(ilja) groups=500(ilja),11(floppy) | ||
+ | ilja@nikita:~/Summerschool/lab/virus/ilrk%./getroot | ||
+ | sh-2.05b# id | ||
+ | uid=0(root) gid=0(root) groups=500(ilja),11(floppy) | ||
+ | sh-2.05b# | ||
+ | |||
+ | I'll shortly upload both the rootkit and the virus to the ftpd. | ||
+ | |||
+ | -- Ilja van Sprundel |
Latest revision as of 17:36, 24 November 2010
Contents
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...
--Samad
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)
Google Help
As some of the participants were not very happy with Google, I looked for that older version of Google Help which I once read. I found it here: [1]
What to do with large capture files
I played with the 1.6 GiB pcap file of summerschool traffic. It's not fun to have either ethereal or tcpdump display that file, because my machine does not have enough memory and I do not have enough patience.
I found the tools tcpslice and pcapmerge, which can be used to split pcap files into smaller pieces. These pieces can then be analyzed by ethereal without waiting forever.
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)
I made an infection tool which uses .eh_frame to store the evil payload. The infection routine doesn't overwrite the the entry point, but instead it makes an entry is the constructor list (.ctors).
The evil payload does nothing else then print a string and then leave, ret.
an example:
ilja@nikita:~/Summerschool/lab/virus%make infect cc infect.c -o infect ilja@nikita:~/Summerschool/lab/virus%cp /bin/ls . ilja@nikita:~/Summerschool/lab/virus%./infect ./ls offset to .eh_frame: 0x10a20 The .eh_frame addr: 0x8058a20 addr_shellcode: 0x8058a20 offset to .ctors: 0x11328 ilja@nikita:~/Summerschool/lab/virus%./ls netric was here # <-- this is the strings :) answers getroot.c ilrk.o ilrk3.o infect.c malware-unix.pdf upx upx.hex ... ilja@nikita:~/Summerschool/lab/virus%
next I made a small rootkit. I abused the register_sysctl_table() function which allows me to register an entry for sysctl() calls. When you use sysctl() right the entry I registered gets used and it calls the handler in it (which you can specify). All my handler does is give you uid/gid 0. but this could be extended with ease. It works like this:
ilja@nikita:~/Summerschool/lab/virus/ilrk%gcc -c ilrk.c -I/lib/modules/$(uname -r)/build/include ilja@nikita:~/Summerschool/lab/virus/ilrk%su Password: root@nikita:/home/ilja/Summerschool/lab/virus/ilrk#insmod ilrk.o root@nikita:/home/ilja/Summerschool/lab/virus/ilrk#lsmod Module Size Used by Not tainted ilrk 780 0 (unused) snd-pcm-oss 37252 0 (unused) ... agpgart 39576 0 (unused) root@nikita:/home/ilja/Summerschool/lab/virus/ilrk#exit ilja@nikita:~/Summerschool/lab/virus/ilrk%make getroot cc getroot.c -o getroot ilja@nikita:~/Summerschool/lab/virus/ilrk%id uid=500(ilja) gid=500(ilja) groups=500(ilja),11(floppy) ilja@nikita:~/Summerschool/lab/virus/ilrk%./getroot sh-2.05b# id uid=0(root) gid=0(root) groups=500(ilja),11(floppy) sh-2.05b#
I'll shortly upload both the rootkit and the virus to the ftpd.
-- Ilja van Sprundel