0/ Intro
A debugger or debugging tool is a computer program that is used to test and debug other programs (the "target" program) (source wikipedia). We can cite IDA or gdb as well known debuggers used in security community.
A debugger is also the security analyst best friend :-) it will help him (or her) to understand what the program is doing. But what if somebody craft an executable file which will show different behaviors between the debugger and the real life?
Let's do that under linux with an ELF binary. ELF is an executable format, supported by IDA and gdb. The format of ELF files is well-known. ELF contains headers, Program Headers and Section Headers.
Program headers are read by kernel when the ELF binary is launched, Section headers are read by debuggers. Theorically, they are the same. Theorically, they said.
Let's start with a very simple Hello World program:
#include <stdlib.h>
const char hello[]="Hello World\n\x00\x03\x00\x00\x00\x01\x00\x02";
const char hell[]="Bad, bad World\n";
int main(void) {
puts(hello);
exit(0);
}
Don't pay attention for the garbage at the end of hello[] and the presence of hell[] for now. No need to be an expert to understand what will be printed.
1/ Taking a look Section headers
Let's look at .rodata section, containing the strings, and the relevant asm part in .text:
mitsurugi@dojo:~/chall/magick$ readelf -x .rodata hello
Hex dump of section '.rodata':
0x080484e8 03000000 01000200 48656c6c 6f20576f ........Hello Wo
0x080484f8 726c640a 00030000 00010002 00426164 rld..........Bad
0x08048508 2c206261 6420576f 726c640a 00 , bad World..
mitsurugi@dojo:~/chall/magick$ readelf -S hello
There are 30 section headers, starting at offset 0xf20:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0 0 4
(...)
[15] .rodata PROGBITS 080484e8 0004e8 00002e 00 A 0 0 4
(...)
mitsurugi@dojo:~/chall/magick$
And the asm:
0804842b <main>:
804842b: 8d 4c 24 04 lea 0x4(%esp),%ecx
804842f: 83 e4 f0 and $0xfffffff0,%esp
8048432: ff 71 fc pushl -0x4(%ecx)
8048435: 55 push %ebp
8048436: 89 e5 mov %esp,%ebp
8048438: 51 push %ecx
8048439: 83 ec 04 sub $0x4,%esp
804843c: 83 ec 0c sub $0xc,%esp
804843f: 68 f0 84 04 08 push $0x80484f0
8048444: e8 a7 fe ff ff call 80482f0 <puts@plt>
8048449: 83 c4 10 add $0x10,%esp
804844c: 83 ec 0c sub $0xc,%esp
804844f: 6a 00 push $0x0
8048451: e8 ba fe ff ff call 8048310 <exit@plt>
puts will print the string located in 0x080484f0, located in .rodata section, which is "Hello World\n\x00<unprinted garbage>"
The Section headers says that you put 0x2e bytes from the offset 0x4e8 at the adress 0x080484e8
Let's change this slightly. Only in section headers, let says that the .rodata sections copy 0x18 bytes from the offset 0x4fd at 0x080484e8.
2/ Who are you, and what are you doing to my Section headers?
We now have a second file, called hello-patched. This file still runs good:
mitsurugi@dojo:~/chall/magick$ ./hello-patched
Hello World
mitsurugi@dojo:~/chall/magick$
The relevant part of Section headers are now:
mitsurugi@dojo:~/chall/magick$ readelf -S hello-patched
There are 30 section headers, starting at offset 0xf20:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
(...)
[15] .rodata PROGBITS 080484e8 0004fd 000018 00 A 0 0 4
2/1/ IDA gets tricked
Here is the printscreen of IDA disassembling this file:
Well, this can be confusing when you know that ./hello writes "Hello World"
So, what you see with IDA is not what you get (I've tested the Free edition of IDA v5.0 and IDA Pro 6.7)
2/2/ gdb is even more confusing
Ths string is not the same between x/s and puts call :) mitsurugi@dojo:~/chall/magick$ gdb -nx -q hello-patched
Reading symbols from hello...(no debugging symbols found)...done.
(gdb) disass main
Dump of assembler code for function main:
0x0804842b <+0>: lea 0x4(%esp),%ecx
0x0804842f <+4>: and $0xfffffff0,%esp
0x08048432 <+7>: pushl -0x4(%ecx)
0x08048435 <+10>: push %ebp
0x08048436 <+11>: mov %esp,%ebp
0x08048438 <+13>: push %ecx
0x08048439 <+14>: sub $0x4,%esp
0x0804843c <+17>: sub $0xc,%esp
0x0804843f <+20>: push $0x80484f0
0x08048444 <+25>: call 0x80482f0 <puts@plt>
0x08048449 <+30>: add $0x10,%esp
0x0804844c <+33>: sub $0xc,%esp
0x0804844f <+36>: push $0x0
0x08048451 <+38>: call 0x8048310 <exit@plt>
End of assembler dump.
(gdb) x/s 0x80484f0
0x80484f0 <hello>: "Bad, bad World\n"
(gdb) run
Starting program: /home/mitsurugi/chall/magick/hello-patched
Hello World
[Inferior 1 (process 5382) exited normally]
(gdb)
Ok, that's not really really true: As soon as you run the program, the Section is corrected, and x/s works as expected, but the demo is worth it. Said differently: if you don't run the binary under gdb, you'll get all wrong.
3/ Conclusion
Now think at a crackme, or malware using this technique. Static Analysts will get lost, without even noticing it :-)
Well, nothing new here: if the same information can be picked from two different places, you can be sure there will be problems.
The fact that Section headers can be changed is known since a very long time, I've just wanted to play a bit with it and manipulate ELF format. The idea of this blogpost has born in my mind after filling a bug I've had with radare2.
You can read another blogpost here with some mitigations in bonus.
If you want to try it with your debugger, you can download a copy of the patched hello file here:
http://42.meup.org/e8AyyDp6/hello (sha256 = f63edf2de7d4edeb02650e3821921ddf3831ee33226cece8f81f31b912e464a5 ) and if it works/fail send me the info, I could compile some results :-)
0xMitsurugi
There are few people who will make mistakes with fire after having once been burned.
~Yamamoto Tsunetomo
~Yamamoto Tsunetomo
Aucun commentaire:
Enregistrer un commentaire