Side Project: Entrance Song

I’ve been working on a side project inspired by a professional wrestling and a blog post I read about a year ago.

I can’t remember the blog’s name, but it described how the local system administrator setup their Sonos speakers to play a theme song whenever an employee entered the office. They used DHCP to detect when an employee’s mobile phone connected to the corporate WiFi and then matched the MAC address to an employee and their chosen entrance song.

The whole idea was pretty amazing and it made me think of my favorite thing about professional wrestling: getting a chance to pick an entrance song for when you enter the ring. It’s something I give a lot of thought to.

This is essentially how I wanted to feel walking in my front door every day after work and also let my friends set their own entrance theme songs.

The implementation used in the blog used tcpdump and some bash scripts, but I wanted to write a version in Python.

Here’s the link to the GitHub repo.

Sniffing the DHCP Transactions

Detecting when someone enters my house is done using DHCP, which is a weird protocol. It’s based on UDP (and implicitly, IP) but it’s used for getting an IP address lease so the device likely doesn’t have an IP address. The solution for this is to just broadcast the DHCP request and response to everyone on the network. The end result is a very noisy transaction, but that makes it easy to sniff.

  • A client sends a DHCPDISCOVER packet broadcast to 255.255.255.255
  • A nearby DHCP server detects and responds with a DHCPOFFER to 255.255.255.255 containing an IP address
  • The client device then sends back a DHCPREQUEST packet (again, to 255.255.255.255) requesting this offered IP address.
  • Finally, the DHCP server sends an acknowledgement DHCPACK message, this time just as a unicast packet.

The whole thing is like walking into a hotel lobby and shouting at everyone present until the conceirge hears you and gives you a room key.

For our entrance song to detect a device, we just need to listen for that DHCPREQUEST packet, since it’s being broadcast everywhere.

I’m using Scapy, a very awesome Python library, to sniff my network for DHCP traffic. Scapy is pretty well known for doing cyber security stuff like crafting packets and building sniffers. It’s very easy to write a basic sniffer with a callback.

from scapy.all import Ether, DHCP, sniff

sniff(prn=self.dhcp_monitor_callback, filter='udp and (port 67 or 68)', store=0)

def dhcp_monitor_callback(pkt):
    """Callback for DHCP requests"""
    if not pkt.haslayer(DHCP):
        return
    if pkt[DHCP].options[0][1] != 3:
        return

    mac_addr = pkt[Ether].src
    logging.info('DHCP request from %s', mac_addr)

Here, we’re sniffing for UDP on port 67 and 68 and passing the packet to a callback function. In the callback, we’re making sure it’s actually a DHCP packet and that it’s a DHCPREQUEST packet (the options are a list of tuples of DHCP key-value options. The first tuple is the type of DHCP message, here we’re using type 3 for DHCP requests).

After verifying that we have a DHCP request, we can easily pull the MAC address out of the Ethernet layer. We’ll use this later to compare to my friend’s names in a SQLite database and figure out what song to play.

device = data.get_device_by_mac_addr(mac_addr)
if not device:
    logging.info('This is not a device I know about... Adding it to the database')
    data.insert_device(mac_addr)
    return

if device.owner.song:
    song = device.owner.song
    logging.info('%s is about to enter (%s)! playing %s by %s', \
        device.owner.name,
        device.friendly_name,
        song.title,
        song.artist)
else:
    logging.info('Device owner %s does not have a song. Doing nothing.', device.owner.name)
    return

Meanwhile the SQLAlchemy models are pretty standard classes that represent a Device, an Owner, and a Song and the foreign keys between them. The data.get_device_by_mac_addr and data.insert_device are just shortcut functions for querying and inserting data into my database.

Playing Music

Once we’ve determined who’s entered my front door, we need to play their theme song. I have a Spotify premium account, so I can literally play anything. Spotify provides a pretty good web-based REST API and there’s a Python wrapper for it called Spotipy.

Spotipy hasn’t been updated in a while and the original author doesn’t seem to be merging pull requests. There are a few issues with the library so I forked it and patched them up myself.

Authenticating my premium Spotify account using Spotipy is a little weird. You have to register for their developer program and create a new project. After that, you need to allow the project to access your Spotify account with the appropriate level of access.

For playing the music, we want to do the following:

  1. Look up who entered my front door and see if they have an entrance song.
  2. If so, save the current Spotify playback so we can resume it later.
  3. Fade out the current song.
  4. Set the volume high and blast that entrance song for the next 45 seconds or so
  5. Fade out the entrance song
  6. Fade in the previously played music prior to the entrance and return everything back to normal

Sounds easy enough. There are a few interesting problems to solve though. Spotify doesn’t have a way to play just a section of music and then stop. We can fake this out by starting the music in a new thread, sleeping in that thread for the entrance duration (usually about 45 seconds to a minute) and then stop the music right before ending the thread.

class MusicThread(Thread):
    """A thread to start music and sleep. This is a cheap way to implement playing
    a duration of a song since the Spotify API doesn't include that.
    """
    def __init__(self, sp_context, mp_context, uri, position_ms, duration=45,
                 device_id=None):
        """Constructor
        Args
            sp_context - The spotify context
            mp_context - music player context
            uri - The URI to play
            position_ms - The position in ms to start from
            duration - The duration to play, in seconds
            device_id - The device to play on, or None to play on the default device
        """
        super().__init__()

        self.sp = sp_context
        self.mp = mp_context
        self.uri = uri
        self.position_ms = position_ms
        self.duration = duration
        self.device_id = device_id

    def run(self):
        """Runs the thread.
        Starts playback on the track, sleeps, and then fades out the track.
        """
        logging.info('Starting playback in new thread')
        try:
            self.sp.pause_playback()
        except SpotifyException as e:
            # This often happens if there is no current active device. We'll assume there's
            # device_id being used. The next try/catch block will handle it if not.
            pass
        try:
            self.sp.start_playback(device_id=self.device_id, uris=[self.uri],
                                   position_ms=self.position_ms)
        except SpotifyException as e:
            logging.error(e)
            return
        self.mp.set_volume(self.mp.default_volume)

        logging.info('Putting the thread to sleep for %s seconds', self.duration)
        sleep(self.duration)
        logging.info('Stopping playback')

        # Get the currently playing tack to be sure we're stopping this track and not
        # someone else's.
        current_track = self.sp.currently_playing()
        try:
            uri = current_track['item']['uri']
            if uri == self.uri:
                self.mp.fade_out()
                self.sp.pause_playback()
            else:
                logging.info('Attempted to stop song %s but it\'s not playing', self.uri)
        finally:
            return

Another problem is figuring out how to handle if someone shows up while another person’s entrance song is already playing. Should the previous person’s music be cut off? That’s disrespectful. Also the threads might start fighting over the music controls and we’d get into a weird state.

I thought about writing some state machine code to keep the threads in sync but there’s a much more elegant solution: using Python’s Queue class. These things deserve way more credit than they get. They allow you to have a thread safe way to manipulate a collection. In my case I want to add things without blocking (i.e. someone walks in the door so I need to queue up their song) while removing things with blocking (i.e. making Spotify play the song but block until the song is complete before starting the next one). No awful state machine required!

Here’s the main loop for the music player:

def player_main(self):
    logging.info('Starting music player')
    while True:
        uri, start_minute, start_second, duration = self.song_queue.get(True)
        logging.info('Found a song on the queue!')
        logging.info('Playing %s at %d:%d duration %d', uri, start_minute, start_second,
                     duration)
        self.save_current_playback()
        t = self._play_song(uri, start_minute, start_second, duration)
        logging.info('Waiting for song to end...')
        t.join()
        self.restore_playback()
        logging.info('Song over... waiting for the next song on the queue')

The get method will block the thread until it sees something on the queue. The join method on the MusicThread will also block the main thread until the song completes. The MusicPlayer itself is a Thread so the whole thing doesn’t block the rest of my application while calling these methods.

My Scapy DHCP sniffing code just needs to drop songs on the queue and they will get picked up by the music player thread.

Results and Conclusion

It’s honestly pretty awesome to walk into your own house and have an entrance song play. I now get pumped up as I enter my door and start waving my hands to an imaginary crowd. I’m not sure what my neighbors think of this.

Other Things

  • I use some NetGear wifi extenders in my house. It turns out, they change a device’s MAC address by swapping the first 3 bytes to 02:0f:b5. To get around this, I added a --virtualmac option that attempts to fall back to looking in the database for just the last 3 bytes of a MAC address.
  • I recommend using a static IP on the device hosting the Python code. I put this code on a Pine64 board I had sitting around and plugged it directly into my wifi router’s wired ethernet switch. There’s no reason it couldn’t have just run on a regular desktop or laptop, but I needed an excuse to use the Pine64.
  • It works but I’ve noticed a few bugs, especially when switching playback between output devices. I might get around to fixing them.

Future Work

  • I also own some Philips Hue lights and they are a lot of fun. I want to bring those into the entrance by having them dim as the music fades and then bringing them up at a certain point in the music.

Appendix: What my friends picked as their entrance theme songs

  • Me - Still Fly (cover) by The Devil Wears Prada
  • Alex - So Fresh, So Clean by OutKast
  • Yaro (@hokietron) - Bumble Bee Tuna Song by Mephiskapheles
  • Samantha - Hide and Seek by Imogen Heap (but at the 2’52” mark where all the memes happen)
  • Rob - Imperial March by John Williams
  • Suzie (@California_Suz) - Cotton Eye Joe by Rednex
  • Smitty - Mah Nà Mah Nà by Piero Umilian
  • Patrick - Holy Diver by Dio
  • Default song for unknown people - Frolic by Luciano Michelini (better known as the “Curb Your Enthusiasm” theme)

Switching to i3

A co-worker convinced me to try out the i3 tiling window manager. I initially wrote this off as a show of nerdy one-upmanship (I admitted to him I still run Unity on my main desktop) but I told him I’d try it out. After a few days, I have some thoughts in place.

If you’ve never used a tiling window manager, the big difference is that your windows are no longer floating and overlaping. Instead they are mutually exclusive units on the screen, usually arranged in a grid. You lose the traditional desktop metaphor, but you gain some performance and efficiency improvements.

Initial things

I installed i3 via apt-get and started a new session. There was a wave of mild paralysis when I realized I had no clue how to make anything work. Using the windows & desktop model for so long made it hard for me to even conceptualize anything else. I’m used to dragging windows around, maximizing them, minimizing them, sliding them around like physical objects at my whim.

Now I was staring at rigid compartments of cold unfeeling terminal windows with no friendly context menus or hints of what to do next. After learning a few key shortcuts and dmenu, I began to breath a sigh of relief. I could pop open my traditional desktop apps and get my footing. It felt weird to open Firefox and not be able to drag the window around. Even worse, it was frightening when I realized that my usual methods of moving files via drag-and-drop was no longer an option. Opening Nautilus was no longer an option (although if you try, you’ll just get a messed up desktop).

After a few hours of tweaking and seeing what’s available, I had a really attractive clean desktop.

desktop1 desktop2

Things I like

For starters, this thing is fast. I can go from login screen to desktop in less than a second. Granted, most Linux window managers tend to be fast compared to Windows, but this is easily the fastest one I’ve personally used.

If you do most of your work in the terminal, then this is a fantastic experience. You can open terminals, have them cleanly arranged on your screen and never need to go searching for the last one you had open among a bunch of minimized windows.

The concept of workspaces is something I never really appreciated until I began to use them in i3. I never used the Unity workspaces because it was easier to just do all your work in one and just minimize the stuff you weren’t dealing with at the moment. In i3, workspaces are a necessity since you can’t just minimize things to a taskbar. The number of workspaces grows as you need them and they quietly disappear after you’re done. This is a fantastic feature I don’t need to dig through empty workspaces trying to find the one I was using. My current setup involves opening my background applications in their own workspaces so I can drop in on them periodically without having them take up my full attention (things like Thunderbird and Spotify). It keeps everything clean and mentally tidy.

Easily my favorite feature is the i3 status bar. It’s such a small part of the i3 experience, but it’s a lot of fun to tweak. Like the rest of i3, it’s simple and aesthetically pleasing.

I fully expected that i3 would take a hard stance on using the keyboard for everything, but I was pleasantly surprised to see the mouse as a fully capable tool. I still use it for switching workspaces.

For my (relatively simple) setup, all my tweaks use only 3 files: ~/.config/i3/config, ~/.i3status.conf, and ~/.Xdefaults. I also like that you can re-load your entire session with a few keystrokes so you can quickly play around with settings and see the results.

Things I’m not liking (yet)

Copy/pasting between windows feels weird, especially if one window supports traditional Ctrl-C/Ctrl-V but the other doesn’t. I don’t think this is an i3 issue but rather something that just isn’t configured in my current urxvt setup.

When doing front-end development, I felt slowed down, mostly because I didn’t have an easy way to view image thumbnails.

Conclusions

I like the change so far and I’m planning on sticking with i3. I haven’t had any compatibility issues with my GUI applications (even Steam runs fine). I haven’t been able to find anything that I absolutely hate.

Since I’m a command-line person, the learning curve hasn’t been too difficult. When combined with a good mechanical keyboard, i3 is one of the most satisfying ways to interact with your Unix-like system.

My setup

Getting it

sudo apt-get install i3 dmenu rxvt-unicode-256color feh

Setting urxvt to be the default terminal

Swap out the command to start a terminal to use urxvt command in ~/.config/i3/config

# start a terminal
# bindsym $mod+Return exec i3-sensible-terminal
bindsym $mod+Return exec urxvt

Get yourself a cool wallpaper

I’m using this one. Add to the end of the ~/.config/i3/config file

exec --no-startup-id feh --bg-scale ~/Downloads/pexels-photo-14676.png

Colors!

Create an .Xdefaults file. I’m using the Railscast theme exported from terminal.sexy along with some tweaks to make urxvt transparent.

urxvt.font:           xft: Dejavu Sans Mono:autohint=true:antialias=true:size=9
urxvt.background:     black
urxvt.foreground:     white
urxvt.scrollBar:      false
urxvt.tintColor:      white
urxvt.fading:         15
urxvt.fadeColor:      black
urxvt.shading:        25
urxvt.inheritPixmap:  true
urxvt.pointerColor:   black
urxvt.pointerColor2:  white

! special
*.foreground:   #e6e1dc
*.background:   #2b2b2b
*.cursorColor:  #e6e1dc

! black
*.color0:       #2b2b2b
*.color8:       #5a647e

! red
*.color1:       #da4939
*.color9:       #da4939

! green
*.color2:       #a5c261
*.color10:      #a5c261

! yellow
*.color3:       #ffc66d
*.color11:      #ffc66d

! blue
*.color4:       #6d9cbe
*.color12:      #6d9cbe

! magenta
*.color5:       #b6b3eb
*.color13:      #b6b3eb

! cyan
*.color6:       #519f50
*.color14:      #519f50

! white
*.color7:       #e6e1dc
*.color15:      #f9f7f3

Tweak the status bar

Copy the existing status bar file and make a few changes.

cp /etc/i3status.conf ~/.i3status.conf
# i3status configuration file.
# see "man i3status" for documentation.

# It is important that this file is edited as UTF-8.
# The following line should contain a sharp s:
# ß
# If the above line is not correctly displayed, fix your editor first!

general {
        colors = true
        interval = 5
        color_bad = '#da4939'
        color_good = '#a5c261'
}

order += "disk /"
order += "run_watch DHCP"
order += "wireless _first_"
order += "ethernet _first_"
order += "cpu_usage"
order += "load"
order += "cpu_temperature 0"
order += "tztime local"

wireless _first_ {
        format_up = "Wireless: (%quality at %essid) %ip"
        format_down = "Wireless: down"
}

ethernet _first_ {
        # if you use %speed, i3status requires root privileges
        format_up = "Ethernet: %ip (%speed)"
        format_down = "Ethernet: down"
}

run_watch DHCP {
        pidfile = "/var/run/dhclient*.pid"
}

tztime local {
        format = "%A, %B %e %l:%M %P"
}

load {
        format = "CPU load: %1min"
}

disk "/" {
        format = "%avail"
}

cpu_temperature 0 {
        format = "Temp: %degrees °C"
        path = "/sys/class/thermal/thermal_zone0/temp"
}

cpu_usage {
        format = "CPU usage: %usage"
}

Setuid Script Wrapper Example

The problem? I have a Node.js script that needs to run as root and Linux doesn’t like me to shoot myself in the foot.

I have a bunch of sysadmin utilities that I like to use to do various clean up tasks on my projects. I call them “agents” and they are just short Node.js scripts that run forever using PM2 (PM2 is one of my favorite utilities ever because it lets you wrap up all your microservices into one frontend, no matter what language you wrote them in – also you’re allowed to say microservices and not sound like a complete tool).

One of these agents was ping-agent.js, a Node script that uses net-ping and node-schedule to periodically send ping packets to a list of hosts and write the results back to a Redis cache. The problem is that doing a ping requires privileged access to the OS and using sudo put it in an entirely different PM2 daemon so it wasn’t appearing with my normal pm2 list results. Sad!

Most of the time this would be a job for setuid, but setuid doesn’t work for interpreted scripts for security reasons. One solution is to code up a quick-and-dirty wrapper in C. This is usually a Bad Idea™ since it’s a perfect way to do privilege escalation on a compromised system. My version of this is to make it as narrowly defined as possible and useable for only a specific case, while still being kind of portable when I need to re-deploy code in a different situation.

Here’s my solution:

The C preprocessor here requires that you explicitly give a path to the script to run but the Makefile figures that out for you so you don’t need to hardcode something.

make
pm2 start wrapper --name ping-agent
[PM2] Spawning PM2 daemon with pm2_home=/home/austin/.pm2
[PM2] PM2 Successfully daemonized
[PM2] Starting /home/austin/projects/ping-agent/wrapper in fork_mode (1 instance)
[PM2] Done.
┌────────────┬────┬──────┬───────┬────────┬─────────┬────────┬─────┬───────────┬──────────┐
│ App name   │ id │ mode │ pid   │ status │ restart │ uptime │ cpu │ mem       │ watching │
├────────────┼────┼──────┼───────┼────────┼─────────┼────────┼─────┼───────────┼──────────┤
│ ping-agent │ 0  │ fork │ 14568 │ online │ 0       │ 0s     │ 3%  │ 24.0 MB   │ disabled │
└────────────┴────┴──────┴───────┴────────┴─────────┴────────┴─────┴───────────┴──────────┘
 Use `pm2 show <id|name>` to get more details about an app

Woo! Now I can run my Node.js stuff in PM2 without needing a spearate PM2 daemon for root-ish activity. One big caveat is that you need to have a shebang line on your Node.js script (e.g. #!/usr/bin/env node).

Final warning: setuid is still pretty nasty. It’s probably not a good idea to use it for anything that gets input from the outside world.

Security BSides NoVA 2017 Retrospective

I must be doing something right because they keep sending me to security conferences.

bsides

After going to ShmooCon last month, I got the opportunity to attend the inaugural Security BSides Northern Virginia conference on February 25. As a 1 day event, it was pleasantly brief and packed a lot of talks without requiring me to commit a full weekend. Also, it took place in Herndon, which is pretty easy to get to and didn’t require a trip on the DC metro.

Like with ShmooCon, I made a list of things that stood out to me at the conference.

  • The first thing I noticed when walking in is this conference attracted a lot of big sponsors. ShmooCon actively limited the sponsorship spots and tried to showcase smaller companies, but this one had all the big names: SANS, ISC², Symantec, and pretty much ever contractor from the DC metro area.

    I don’t have strong feelings about this, but I was surprised since I sort of assumed that BSides was informal and driven by community individuals (I mean, the main website is a PBworks wiki running with an educational license, so I really didn’t expect to see Fortune 500 companies setting up booths). I guess it’s cool that the BSides NoVA organizers were able to get big names for the first conference.

  • Initial keynote was by Tennable co-founder Ron Gula (@rongula) under the title Cyber Security 2017: Trends and Start Ups which rolled three talks into one on the topics of 1.) the cyber security market, 2.) new rules for business and 3.) how to pitch a cyber security startup to a VC.

    It was oddly refreshing to have someone talk about the intersections of startup culture and hacker culture. They both have a lot of common ground but tend to get mired by ideology.

    My favorite part of his talk was his take on the traditional 5 slide pitch deck (they spend too much time on posturing and not enough on describing the problem being solved).

pitch deck

  • The talk 0 to 31337 Real Quick: Lessons Learned by Reversing the Flare-On Challenge was a retrospective on last year’s FLAREOn Challenge presented by two Endgame developers Josh Wang (@rh0gue) and Blaine Stancil (@MalwareMechanic).

    These challenges were what really got me into doing reverse engineering, so I was excited to meet someone who actually completed all of them. They didn’t reveal any of the answers, but instead provided a rundown of all the techniques they saw.

    This slide was the central point of the talk since it provided a table with the challenges and which reverse engineering techniques were used to solve each one:

FLAREOn techniques used

  • Another interesting talk was Doomsday Preppers: Fortifying Your Red Team Infrastructure by Steve Borosh (@424f424f) and Jeff Dimmock (@bluscreenofjeff). It focused on how a red team can setup a successful pen test by building out simple, redundant, and redeployable networks to keep the attack alive, even if the blue team takes active steps toward blocking it.

    I kind of assumed that red teams weren’t very organized and just grasped for footholds until they got the results they needed, but this really changed how I look at red team/blue team exercises.

  • The final keynote was by Georgia Weidman (@georgiaweidman) whom I went to college with at James Madison University. She’s been much more successful than me since then. The organizers unfortunately put her keynote in the foyer of the building instead of an auditorium and that was a terrible idea; the sound system was awful and it was happening alongside the happy hour so it was hard to hear anything over the noise. My biggest complaint about the conference is that they messed this up so badly.

  • But hey, there was free alcohol and food.
  • In addition to talks, I dropped into a few workshops. There was a Malware Analysis 101 workshop in the schedule, but there must have been some confusion when printing the schedule because it was actually an OWSAP workshop on identifying attack patterns from network packet captures.

    The other workshop I did was a Forensics 101 course that was very well put together and instructed by Marcelle Lee (@marcelle_fsg), Brian Moran (@brianjmoran) and Courtney Lancaster (@allth3things).

    The wifi at the conference was pretty bad; probably because it wasn’t designed to hold a ton of people all at once. I made the mistake of not downloading the workshop materials in advance. A lot of people did the same thing. To remedy this, they began passing around a thumb drive with all the materials. At a security conference. During a session on malware. Let this sink in.

    whaat

  • The badges are awesome. They are an electronics reference board so they are actually pretty useful after the con. badge

  • I saw Bruce Potter (@gdead) wandering around. It’s kind of cool to recognize people from other conferences.

Overall it was a fun conference. Most of the talks were aimed at a broader audience so it was highly accessible for all levels of experience. I definitely plan on returning.

ShmooCon 2017 Retrospective

I went to ShmooCon!

I’ve always wanted to attend but tickets are limited and incredibly hard to get. I was finally able to secure 2 barcodes this year so I passed one off to my friend, Ed, and we hopped on the metro to Dupont Circle.

Imgur

The easiest way to get kicked out of the conference is take photos without permission. There’s a two strike policy on this; unfortunately that means I don’t have any photos of the conference. There were some interesting people and displays but I was going to respect the privacy of the attendees.

The vibe I got was that this was a gathering of good natured people and there wasn’t going to be much cover for illegal or blatantly malicious activities (I still wasn’t going to use the ATM in the lobby, however).

After 3 days of talks and shenanigans, I made a few retrospective notes.

  • How did I get a barcode? No gimmicks. I just went to the website and hit F5 as the site went live. My Internet connection at home is reasonably good and I think this helped.
  • Want to blend in? Wear a black tshirt and jeans. Want to make it easy for your friends to find you? Wear anything else.
  • There were two talks that really stuck out to me. The first was Anti-Ransomware: Turning the Tables by Gal Shpantzer and G. Mark Hardy. The presenters discussed why ransomware is such a big deal now (hint: money) and how it’s getting more sophisticated (e.g. being able to detect if VPN software is present and waiting for a connection for better exposure). The other talk was Goodnight Moon & the House of Horrors: A look at the current IoT ecosystem and the regulations trying to control it by Whitney Merrill and Aaron Alva. IoT is still a garbage fire from a security perspective and this talk discussed possible regulations to try to contain it.
  • I mostly attended talks. I regret not visiting some of the side rooms and participating in the lockpick village. Next time I plan on dedicating at least half a day to visiting these. A co-worker spent most of his time doing Hack Fortress and his team wound up winning the championship. My TF2 skills are a little too rusty to join a team, but I might consider doing that next year.
  • The Metro sucks. If you are coming from out of town, you might not know about the delays, shutdowns, and fires. I highly recommend giving yourself extra time if you plan on using it. This is especially true if you plan on using the disability access elevators since these are notorious for being out of order.
  • A VPN is highly recommended, even for the WPA secured Wifi networks. Cell service was pretty bad. Probably because of all the devices.
  • The food situation was wasn’t ideal. There was an impromptu bar set up in the lounge area where food was being sold, but it wasn’t great. Dupont Circle has some good restaurants within walking distance, however.
  • The Friday night fire talks were fun and probably the best part of the day (also the lightest attended).
  • Get a Twitter account. Set up alerts from @shmoocon. You’ll get buzzed when stuff happens. Also you can set up a list of all the speakers you saw so you can follow up with them later.
  • I kept sitting behind Ed Skoudis (@edskoudis) at various talks but I didn’t realize it was him until some of his tweets later.

Overall, it was pretty awesome. If I can get a barcode for next year, I’m definitely going back. There are a few other hacker cons being scheduled that I might attend in the meantime, including BSides DC and, of course, DEF CON.

Image credits: Dupont Circle Station, Wikimedia Commons